KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > types > selectors > SelectorUtils


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18
19 package org.apache.tools.ant.types.selectors;
20
21 import java.io.File JavaDoc;
22 import java.util.StringTokenizer JavaDoc;
23 import java.util.Vector JavaDoc;
24
25 import org.apache.tools.ant.types.Resource;
26 import org.apache.tools.ant.util.FileUtils;
27
28 /**
29  * <p>This is a utility class used by selectors and DirectoryScanner. The
30  * functionality more properly belongs just to selectors, but unfortunately
31  * DirectoryScanner exposed these as protected methods. Thus we have to
32  * support any subclasses of DirectoryScanner that may access these methods.
33  * </p>
34  * <p>This is a Singleton.</p>
35  *
36  * @since 1.5
37  */

38 public final class SelectorUtils {
39
40     private static SelectorUtils instance = new SelectorUtils();
41     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
42
43     /**
44      * Private Constructor
45      */

46     private SelectorUtils() {
47     }
48
49     /**
50      * Retrieves the instance of the Singleton.
51      * @return singleton instance
52      */

53     public static SelectorUtils getInstance() {
54         return instance;
55     }
56
57     /**
58      * Tests whether or not a given path matches the start of a given
59      * pattern up to the first "**".
60      * <p>
61      * This is not a general purpose test and should only be used if you
62      * can live with false positives. For example, <code>pattern=**\a</code>
63      * and <code>str=b</code> will yield <code>true</code>.
64      *
65      * @param pattern The pattern to match against. Must not be
66      * <code>null</code>.
67      * @param str The path to match, as a String. Must not be
68      * <code>null</code>.
69      *
70      * @return whether or not a given path matches the start of a given
71      * pattern up to the first "**".
72      */

73     public static boolean matchPatternStart(String JavaDoc pattern, String JavaDoc str) {
74         return matchPatternStart(pattern, str, true);
75     }
76
77     /**
78      * Tests whether or not a given path matches the start of a given
79      * pattern up to the first "**".
80      * <p>
81      * This is not a general purpose test and should only be used if you
82      * can live with false positives. For example, <code>pattern=**\a</code>
83      * and <code>str=b</code> will yield <code>true</code>.
84      *
85      * @param pattern The pattern to match against. Must not be
86      * <code>null</code>.
87      * @param str The path to match, as a String. Must not be
88      * <code>null</code>.
89      * @param isCaseSensitive Whether or not matching should be performed
90      * case sensitively.
91      *
92      * @return whether or not a given path matches the start of a given
93      * pattern up to the first "**".
94      */

95     public static boolean matchPatternStart(String JavaDoc pattern, String JavaDoc str,
96                                             boolean isCaseSensitive) {
97         // When str starts with a File.separator, pattern has to start with a
98
// File.separator.
99
// When pattern starts with a File.separator, str has to start with a
100
// File.separator.
101
if (str.startsWith(File.separator)
102                 != pattern.startsWith(File.separator)) {
103             return false;
104         }
105
106         String JavaDoc[] patDirs = tokenizePathAsArray(pattern);
107         String JavaDoc[] strDirs = tokenizePathAsArray(str);
108
109         int patIdxStart = 0;
110         int patIdxEnd = patDirs.length - 1;
111         int strIdxStart = 0;
112         int strIdxEnd = strDirs.length - 1;
113
114         // up to first '**'
115
while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
116             String JavaDoc patDir = patDirs[patIdxStart];
117             if (patDir.equals("**")) {
118                 break;
119             }
120             if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
121                 return false;
122             }
123             patIdxStart++;
124             strIdxStart++;
125         }
126
127         if (strIdxStart > strIdxEnd) {
128             // String is exhausted
129
return true;
130         } else if (patIdxStart > patIdxEnd) {
131             // String not exhausted, but pattern is. Failure.
132
return false;
133         } else {
134             // pattern now holds ** while string is not exhausted
135
// this will generate false positives but we can live with that.
136
return true;
137         }
138     }
139
140     /**
141      * Tests whether or not a given path matches a given pattern.
142      *
143      * @param pattern The pattern to match against. Must not be
144      * <code>null</code>.
145      * @param str The path to match, as a String. Must not be
146      * <code>null</code>.
147      *
148      * @return <code>true</code> if the pattern matches against the string,
149      * or <code>false</code> otherwise.
150      */

151     public static boolean matchPath(String JavaDoc pattern, String JavaDoc str) {
152         return matchPath(pattern, str, true);
153     }
154
155     /**
156      * Tests whether or not a given path matches a given pattern.
157      *
158      * @param pattern The pattern to match against. Must not be
159      * <code>null</code>.
160      * @param str The path to match, as a String. Must not be
161      * <code>null</code>.
162      * @param isCaseSensitive Whether or not matching should be performed
163      * case sensitively.
164      *
165      * @return <code>true</code> if the pattern matches against the string,
166      * or <code>false</code> otherwise.
167      */

168     public static boolean matchPath(String JavaDoc pattern, String JavaDoc str,
169                                     boolean isCaseSensitive) {
170         String JavaDoc[] patDirs = tokenizePathAsArray(pattern);
171         String JavaDoc[] strDirs = tokenizePathAsArray(str);
172
173         int patIdxStart = 0;
174         int patIdxEnd = patDirs.length - 1;
175         int strIdxStart = 0;
176         int strIdxEnd = strDirs.length - 1;
177
178         // up to first '**'
179
while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
180             String JavaDoc patDir = patDirs[patIdxStart];
181             if (patDir.equals("**")) {
182                 break;
183             }
184             if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
185                 patDirs = null;
186                 strDirs = null;
187                 return false;
188             }
189             patIdxStart++;
190             strIdxStart++;
191         }
192         if (strIdxStart > strIdxEnd) {
193             // String is exhausted
194
for (int i = patIdxStart; i <= patIdxEnd; i++) {
195                 if (!patDirs[i].equals("**")) {
196                     patDirs = null;
197                     strDirs = null;
198                     return false;
199                 }
200             }
201             return true;
202         } else {
203             if (patIdxStart > patIdxEnd) {
204                 // String not exhausted, but pattern is. Failure.
205
patDirs = null;
206                 strDirs = null;
207                 return false;
208             }
209         }
210
211         // up to last '**'
212
while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
213             String JavaDoc patDir = patDirs[patIdxEnd];
214             if (patDir.equals("**")) {
215                 break;
216             }
217             if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) {
218                 patDirs = null;
219                 strDirs = null;
220                 return false;
221             }
222             patIdxEnd--;
223             strIdxEnd--;
224         }
225         if (strIdxStart > strIdxEnd) {
226             // String is exhausted
227
for (int i = patIdxStart; i <= patIdxEnd; i++) {
228                 if (!patDirs[i].equals("**")) {
229                     patDirs = null;
230                     strDirs = null;
231                     return false;
232                 }
233             }
234             return true;
235         }
236
237         while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
238             int patIdxTmp = -1;
239             for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
240                 if (patDirs[i].equals("**")) {
241                     patIdxTmp = i;
242                     break;
243                 }
244             }
245             if (patIdxTmp == patIdxStart + 1) {
246                 // '**/**' situation, so skip one
247
patIdxStart++;
248                 continue;
249             }
250             // Find the pattern between padIdxStart & padIdxTmp in str between
251
// strIdxStart & strIdxEnd
252
int patLength = (patIdxTmp - patIdxStart - 1);
253             int strLength = (strIdxEnd - strIdxStart + 1);
254             int foundIdx = -1;
255             strLoop:
256                         for (int i = 0; i <= strLength - patLength; i++) {
257                             for (int j = 0; j < patLength; j++) {
258                                 String JavaDoc subPat = patDirs[patIdxStart + j + 1];
259                                 String JavaDoc subStr = strDirs[strIdxStart + i + j];
260                                 if (!match(subPat, subStr, isCaseSensitive)) {
261                                     continue strLoop;
262                                 }
263                             }
264
265                             foundIdx = strIdxStart + i;
266                             break;
267                         }
268
269             if (foundIdx == -1) {
270                 patDirs = null;
271                 strDirs = null;
272                 return false;
273             }
274
275             patIdxStart = patIdxTmp;
276             strIdxStart = foundIdx + patLength;
277         }
278
279         for (int i = patIdxStart; i <= patIdxEnd; i++) {
280             if (!patDirs[i].equals("**")) {
281                 patDirs = null;
282                 strDirs = null;
283                 return false;
284             }
285         }
286
287         return true;
288     }
289
290     /**
291      * Tests whether or not a string matches against a pattern.
292      * The pattern may contain two special characters:<br>
293      * '*' means zero or more characters<br>
294      * '?' means one and only one character
295      *
296      * @param pattern The pattern to match against.
297      * Must not be <code>null</code>.
298      * @param str The string which must be matched against the pattern.
299      * Must not be <code>null</code>.
300      *
301      * @return <code>true</code> if the string matches against the pattern,
302      * or <code>false</code> otherwise.
303      */

304     public static boolean match(String JavaDoc pattern, String JavaDoc str) {
305         return match(pattern, str, true);
306     }
307
308     /**
309      * Tests whether or not a string matches against a pattern.
310      * The pattern may contain two special characters:<br>
311      * '*' means zero or more characters<br>
312      * '?' means one and only one character
313      *
314      * @param pattern The pattern to match against.
315      * Must not be <code>null</code>.
316      * @param str The string which must be matched against the pattern.
317      * Must not be <code>null</code>.
318      * @param isCaseSensitive Whether or not matching should be performed
319      * case sensitively.
320      *
321      *
322      * @return <code>true</code> if the string matches against the pattern,
323      * or <code>false</code> otherwise.
324      */

325     public static boolean match(String JavaDoc pattern, String JavaDoc str,
326                                 boolean isCaseSensitive) {
327         char[] patArr = pattern.toCharArray();
328         char[] strArr = str.toCharArray();
329         int patIdxStart = 0;
330         int patIdxEnd = patArr.length - 1;
331         int strIdxStart = 0;
332         int strIdxEnd = strArr.length - 1;
333         char ch;
334
335         boolean containsStar = false;
336         for (int i = 0; i < patArr.length; i++) {
337             if (patArr[i] == '*') {
338                 containsStar = true;
339                 break;
340             }
341         }
342
343         if (!containsStar) {
344             // No '*'s, so we make a shortcut
345
if (patIdxEnd != strIdxEnd) {
346                 return false; // Pattern and string do not have the same size
347
}
348             for (int i = 0; i <= patIdxEnd; i++) {
349                 ch = patArr[i];
350                 if (ch != '?') {
351                     if (isCaseSensitive && ch != strArr[i]) {
352                         return false; // Character mismatch
353
}
354                     if (!isCaseSensitive && Character.toUpperCase(ch)
355                             != Character.toUpperCase(strArr[i])) {
356                         return false; // Character mismatch
357
}
358                 }
359             }
360             return true; // String matches against pattern
361
}
362
363         if (patIdxEnd == 0) {
364             return true; // Pattern contains only '*', which matches anything
365
}
366
367         // Process characters before first star
368
while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
369             if (ch != '?') {
370                 if (isCaseSensitive && ch != strArr[strIdxStart]) {
371                     return false; // Character mismatch
372
}
373                 if (!isCaseSensitive && Character.toUpperCase(ch)
374                         != Character.toUpperCase(strArr[strIdxStart])) {
375                     return false; // Character mismatch
376
}
377             }
378             patIdxStart++;
379             strIdxStart++;
380         }
381         if (strIdxStart > strIdxEnd) {
382             // All characters in the string are used. Check if only '*'s are
383
// left in the pattern. If so, we succeeded. Otherwise failure.
384
for (int i = patIdxStart; i <= patIdxEnd; i++) {
385                 if (patArr[i] != '*') {
386                     return false;
387                 }
388             }
389             return true;
390         }
391
392         // Process characters after last star
393
while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
394             if (ch != '?') {
395                 if (isCaseSensitive && ch != strArr[strIdxEnd]) {
396                     return false; // Character mismatch
397
}
398                 if (!isCaseSensitive && Character.toUpperCase(ch)
399                         != Character.toUpperCase(strArr[strIdxEnd])) {
400                     return false; // Character mismatch
401
}
402             }
403             patIdxEnd--;
404             strIdxEnd--;
405         }
406         if (strIdxStart > strIdxEnd) {
407             // All characters in the string are used. Check if only '*'s are
408
// left in the pattern. If so, we succeeded. Otherwise failure.
409
for (int i = patIdxStart; i <= patIdxEnd; i++) {
410                 if (patArr[i] != '*') {
411                     return false;
412                 }
413             }
414             return true;
415         }
416
417         // process pattern between stars. padIdxStart and patIdxEnd point
418
// always to a '*'.
419
while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
420             int patIdxTmp = -1;
421             for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
422                 if (patArr[i] == '*') {
423                     patIdxTmp = i;
424                     break;
425                 }
426             }
427             if (patIdxTmp == patIdxStart + 1) {
428                 // Two stars next to each other, skip the first one.
429
patIdxStart++;
430                 continue;
431             }
432             // Find the pattern between padIdxStart & padIdxTmp in str between
433
// strIdxStart & strIdxEnd
434
int patLength = (patIdxTmp - patIdxStart - 1);
435             int strLength = (strIdxEnd - strIdxStart + 1);
436             int foundIdx = -1;
437             strLoop:
438             for (int i = 0; i <= strLength - patLength; i++) {
439                 for (int j = 0; j < patLength; j++) {
440                     ch = patArr[patIdxStart + j + 1];
441                     if (ch != '?') {
442                         if (isCaseSensitive && ch != strArr[strIdxStart + i
443                                 + j]) {
444                             continue strLoop;
445                         }
446                         if (!isCaseSensitive
447                             && Character.toUpperCase(ch)
448                                 != Character.toUpperCase(strArr[strIdxStart + i + j])) {
449                             continue strLoop;
450                         }
451                     }
452                 }
453
454                 foundIdx = strIdxStart + i;
455                 break;
456             }
457
458             if (foundIdx == -1) {
459                 return false;
460             }
461
462             patIdxStart = patIdxTmp;
463             strIdxStart = foundIdx + patLength;
464         }
465
466         // All characters in the string are used. Check if only '*'s are left
467
// in the pattern. If so, we succeeded. Otherwise failure.
468
for (int i = patIdxStart; i <= patIdxEnd; i++) {
469             if (patArr[i] != '*') {
470                 return false;
471             }
472         }
473         return true;
474     }
475
476     /**
477      * Breaks a path up into a Vector of path elements, tokenizing on
478      * <code>File.separator</code>.
479      *
480      * @param path Path to tokenize. Must not be <code>null</code>.
481      *
482      * @return a Vector of path elements from the tokenized path
483      */

484     public static Vector JavaDoc tokenizePath (String JavaDoc path) {
485         return tokenizePath(path, File.separator);
486     }
487
488     /**
489      * Breaks a path up into a Vector of path elements, tokenizing on
490      *
491      * @param path Path to tokenize. Must not be <code>null</code>.
492      * @param separator the separator against which to tokenize.
493      *
494      * @return a Vector of path elements from the tokenized path
495      * @since Ant 1.6
496      */

497     public static Vector JavaDoc tokenizePath (String JavaDoc path, String JavaDoc separator) {
498         Vector JavaDoc ret = new Vector JavaDoc();
499         if (FileUtils.isAbsolutePath(path)) {
500             String JavaDoc[] s = FILE_UTILS.dissect(path);
501             ret.add(s[0]);
502             path = s[1];
503         }
504         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(path, separator);
505         while (st.hasMoreTokens()) {
506             ret.addElement(st.nextToken());
507         }
508         return ret;
509     }
510
511     /**
512      * Same as {@link #tokenizePath tokenizePath} but hopefully faster.
513      */

514     private static String JavaDoc[] tokenizePathAsArray(String JavaDoc path) {
515         String JavaDoc root = null;
516         if (FileUtils.isAbsolutePath(path)) {
517             String JavaDoc[] s = FILE_UTILS.dissect(path);
518             root = s[0];
519             path = s[1];
520         }
521         char sep = File.separatorChar;
522         int start = 0;
523         int len = path.length();
524         int count = 0;
525         for (int pos = 0; pos < len; pos++) {
526             if (path.charAt(pos) == sep) {
527                 if (pos != start) {
528                     count++;
529                 }
530                 start = pos + 1;
531             }
532         }
533         if (len != start) {
534             count++;
535         }
536         String JavaDoc[] l = new String JavaDoc[count + ((root == null) ? 0 : 1)];
537
538         if (root != null) {
539             l[0] = root;
540             count = 1;
541         } else {
542             count = 0;
543         }
544         start = 0;
545         for (int pos = 0; pos < len; pos++) {
546             if (path.charAt(pos) == sep) {
547                 if (pos != start) {
548                     String JavaDoc tok = path.substring(start, pos);
549                     l[count++] = tok;
550                 }
551                 start = pos + 1;
552             }
553         }
554         if (len != start) {
555             String JavaDoc tok = path.substring(start);
556             l[count/*++*/] = tok;
557         }
558         return l;
559     }
560
561     /**
562      * Returns dependency information on these two files. If src has been
563      * modified later than target, it returns true. If target doesn't exist,
564      * it likewise returns true. Otherwise, target is newer than src and
565      * is not out of date, thus the method returns false. It also returns
566      * false if the src file doesn't even exist, since how could the
567      * target then be out of date.
568      *
569      * @param src the original file
570      * @param target the file being compared against
571      * @param granularity the amount in seconds of slack we will give in
572      * determining out of dateness
573      * @return whether the target is out of date
574      */

575     public static boolean isOutOfDate(File JavaDoc src, File JavaDoc target, int granularity) {
576         if (!src.exists()) {
577             return false;
578         }
579         if (!target.exists()) {
580             return true;
581         }
582         if ((src.lastModified() - granularity) > target.lastModified()) {
583             return true;
584         }
585         return false;
586     }
587
588     /**
589      * Returns dependency information on these two resources. If src has been
590      * modified later than target, it returns true. If target doesn't exist,
591      * it likewise returns true. Otherwise, target is newer than src and
592      * is not out of date, thus the method returns false. It also returns
593      * false if the src file doesn't even exist, since how could the
594      * target then be out of date.
595      *
596      * @param src the original resource
597      * @param target the resource being compared against
598      * @param granularity the int amount in seconds of slack we will give in
599      * determining out of dateness
600      * @return whether the target is out of date
601      */

602     public static boolean isOutOfDate(Resource src, Resource target,
603                                       int granularity) {
604         return isOutOfDate(src, target, (long) granularity);
605     }
606
607     /**
608      * Returns dependency information on these two resources. If src has been
609      * modified later than target, it returns true. If target doesn't exist,
610      * it likewise returns true. Otherwise, target is newer than src and
611      * is not out of date, thus the method returns false. It also returns
612      * false if the src file doesn't even exist, since how could the
613      * target then be out of date.
614      *
615      * @param src the original resource
616      * @param target the resource being compared against
617      * @param granularity the long amount in seconds of slack we will give in
618      * determining out of dateness
619      * @return whether the target is out of date
620      */

621     public static boolean isOutOfDate(Resource src, Resource target,
622                                       long granularity) {
623         if (!src.isExists()) {
624             return false;
625         }
626         if (!target.isExists()) {
627             return true;
628         }
629         if ((src.getLastModified() - granularity) > target.getLastModified()) {
630             return true;
631         }
632         return false;
633     }
634
635     /**
636      * "Flattens" a string by removing all whitespace (space, tab, linefeed,
637      * carriage return, and formfeed). This uses StringTokenizer and the
638      * default set of tokens as documented in the single arguement constructor.
639      *
640      * @param input a String to remove all whitespace.
641      * @return a String that has had all whitespace removed.
642      */

643     public static String JavaDoc removeWhitespace(String JavaDoc input) {
644         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
645         if (input != null) {
646             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(input);
647             while (st.hasMoreTokens()) {
648                 result.append(st.nextToken());
649             }
650         }
651         return result.toString();
652     }
653
654     /**
655      * Tests if a string contains stars or question marks
656      * @param input a String which one wants to test for containing wildcard
657      * @return true if the string contains at least a star or a question mark
658      */

659     public static boolean hasWildcards(String JavaDoc input) {
660         return (input.indexOf('*') != -1 || input.indexOf('?') != -1);
661     }
662
663     /**
664      * removes from a pattern all tokens to the right containing wildcards
665      * @param input the input string
666      * @return the leftmost part of the pattern without wildcards
667      */

668     public static String JavaDoc rtrimWildcardTokens(String JavaDoc input) {
669         String JavaDoc[] tokens = tokenizePathAsArray(input);
670         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
671         for (int i = 0; i < tokens.length; i++) {
672             if (hasWildcards(tokens[i])) {
673                 break;
674             }
675             if (i > 0 && sb.charAt(sb.length() - 1) != File.separatorChar) {
676                 sb.append(File.separator);
677             }
678             sb.append(tokens[i]);
679         }
680         return sb.toString();
681     }
682 }
683
684
Popular Tags