KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > junit > output > antutils > FileSetScanner


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.junit.output.antutils;
21
22 import java.io.File JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.Collections JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.StringTokenizer JavaDoc;
30 import java.util.regex.Matcher JavaDoc;
31 import java.util.regex.Pattern JavaDoc;
32 import org.netbeans.modules.junit.output.antutils.FileSetScanner.AntPattern.PatternPartType;
33 import org.netbeans.modules.junit.output.antutils.FileUtils;
34
35 /**
36  *
37  * @author Marian Petras
38  */

39 class FileSetScanner {
40     
41     /** */
42     private static final String JavaDoc[] DEFAULT_EXCLUDES = new String JavaDoc[] {
43         "**/*~", //NOI18N
44
"**/#*#", //NOI18N
45
"**/.#*", //NOI18N
46
"**/%*%", //NOI18N
47
"**/._*", //NOI18N
48
"**/CVS", //NOI18N
49
"**/CVS/**", //NOI18N
50
"**/.cvsignore", //NOI18N
51
"**/SCCS", //NOI18N
52
"**/SCCS/**", //NOI18N
53
"**/vssver.scc", //NOI18N
54
"**/.svn", //NOI18N
55
"**/.svn/**", //NOI18N
56
"**/.DS_Store" //NOI18N
57
};
58     
59     /** */
60     private static final String JavaDoc[] EMPTY_STRING_ARR = new String JavaDoc[0];
61     
62     /** */
63     private final FileSet fileSet;
64     
65     /** */
66     private File JavaDoc baseDir;
67     /** */
68     private boolean caseSensitive;
69     /** */
70     private boolean followSymlinks;
71     /** */
72     private AntPattern[] includePatterns;
73     /** */
74     private AntPattern[] excludePatterns;
75     
76     /**
77      */

78     static Collection JavaDoc<File JavaDoc> listFiles(FileSet fileSet) {
79         return new FileSetScanner(fileSet).getMatchingFiles();
80     }
81     
82     /**
83      */

84     FileSetScanner(FileSet fileSet) {
85         this.fileSet = fileSet;
86     }
87     
88     /**
89      */

90     Collection JavaDoc<File JavaDoc> getMatchingFiles() {
91         File JavaDoc file = fileSet.getFile();
92         
93         if (file != null) {
94             file = FileUtils.resolveFile(fileSet.getBaseDir(), file.getName());
95             if (file.exists()) {
96                 return Collections.singleton(file);
97             } else {
98                 return Collections.emptyList();
99             }
100         }
101         
102         this.baseDir = fileSet.getBaseDir();
103         this.caseSensitive = fileSet.isCaseSensitive();
104         this.followSymlinks = fileSet.isFollowSymlinks();
105         preparePatterns();
106         findMatchingFiles();
107         return matchingFiles;
108     }
109     
110     /** */
111     private Collection JavaDoc<File JavaDoc> matchingFiles;
112     
113     /**
114      */

115     private void findMatchingFiles() {
116         matchingFiles = new ArrayList JavaDoc<File JavaDoc>(32);
117         findMatchingFiles(baseDir, createPatternTests(includePatterns,
118                                                       excludePatterns));
119     }
120     
121     /**
122      */

123     private void findMatchingFiles(
124             final File JavaDoc directory,
125             final Collection JavaDoc<PatternTest> patternTests) {
126         
127         final File JavaDoc[] children = directory.listFiles();
128         for (File JavaDoc child : children) {
129             final boolean isFile = child.isFile();
130             final boolean isDir = child.isDirectory();
131             if (!isFile && !isDir) {
132                 continue; //skip device files, named pipes, sockets, etc.
133
//TODO - handling symbolic links
134
}
135             
136             Collection JavaDoc<PatternTest> childTests;
137                     
138             childTests = isDir ? new ArrayList JavaDoc<PatternTest>(patternTests.size())
139                                : null;
140             boolean matches = checkFileAgainstPatterns(child,
141                                                        patternTests,
142                                                        childTests);
143             if (matches) {
144                 if (isFile) {
145                     matchingFiles.add(child);
146                 } else {
147                     findMatchingFiles(child, childTests);
148                 }
149             }
150         }
151     }
152     
153     /**
154      */

155     private boolean checkFileAgainstPatterns(
156             final File JavaDoc file,
157             final Collection JavaDoc<PatternTest> tests,
158             final Collection JavaDoc<PatternTest> childrenTests) {
159         
160         assert !tests.isEmpty();
161         assert tests.iterator().next().includePattern;
162         
163         final boolean isDir = childrenTests != null;
164         final boolean isFile = !isDir;
165         
166         boolean matches = false;
167         for (PatternTest patternTest : tests) {
168             final AntPattern pattern = patternTest.pattern;
169             final boolean isIncludePattern = patternTest.includePattern;
170             final int partIndex = patternTest.patternPartIndex;
171             final PatternPartType partType =
172                     pattern.patternPartTypes[partIndex];
173             final boolean isLastPart = pattern.isLastPart(partIndex);
174
175             /*
176              * There is an overview sketch of the following code
177              * (the many if-then-else condition statements) available
178              * in the 'www' section of this module.
179              *
180              * Local access:
181              * <cvsroot>/junit/www/doc/dev/ant-pattern-matcher-decision-tree.gif
182              *
183              * Web access:
184              * http://junit.netbeans.org/doc/dev/ant-pattern-matcher-decision-tree.gif
185              *
186              * The 'HANDLES THE <colour> AREAS' notes refer to the sketch.
187              */

188             
189             if (isIncludePattern && isLastPart
190                     && partType == PatternPartType.DOUBLE_STAR) {
191                             /* HANDLES THE BLUE AREAS */
192                 
193                 /*
194                  * This is a universal include pattern (**).
195                  * If it is present, it should be the only include pattern
196                  * in the collection.
197                  */

198                 matches = true;
199                 childrenTests.add(patternTest);
200                 continue;
201             }
202             
203             if (isFile && (!isLastPart || (matches && isIncludePattern))) {
204                             /* HANDLES THE GREEN AREAS */
205                 continue;
206             }
207             if (isDir && isLastPart
208                     && (partType != PatternPartType.DOUBLE_STAR)) {
209                             /* HANDLES THE YELLOW AREA */
210                 continue;
211             }
212             
213             final boolean nameMatches =
214                     (partType == PatternPartType.DOUBLE_STAR)
215                     || isMatchingFile(file, pattern, partIndex);
216             if (!nameMatches) {
217                             /* HANDLES THE RED AREAS */
218                 continue;
219             }
220             
221             if (!isLastPart) {
222                             /* HANDLES THE CYAN AREAS */
223                 assert isDir; // We know it's a dir - see the conditions above.
224

225                 if (isIncludePattern) {
226                     matches = true;
227                 }
228                 
229                 int nextPartIndex = partIndex + 1;
230                 PatternPartType nextPartType =
231                         pattern.patternPartTypes[nextPartIndex];
232                 if (partType != PatternPartType.DOUBLE_STAR
233                         && nextPartType == PatternPartType.DOUBLE_STAR
234                         && pattern.isLastPart(nextPartIndex)) {
235                     /*
236                      * The child pattern would be a universal pattern (**).
237                      * We will handle it in a special way:
238                      */

239                     if (isIncludePattern) {
240                         /*
241                          * The universal pattern is stronger than any
242                          * non-universal patterns - remove these patterns
243                          * and use only the universal include pattern
244                          * for children:
245                          */

246                         childrenTests.clear();
247                         childrenTests.add(new PatternTest(pattern,
248                                                           isIncludePattern,
249                                                           nextPartIndex));
250                         /*
251                          * Warning: The two statements above work correctly
252                          * only under condition that all include patterns
253                          * are handled before any exclude pattern!
254                          */

255                     } else {
256                         /*
257                          * The universal exclude pattern would exclude
258                          * everything. Just stop searching for more matches:
259                          */

260                         matches = false;
261                         break;
262                     }
263                 } else {
264                     childrenTests.add(new PatternTest(pattern,
265                                                       isIncludePattern,
266                                                       partIndex + 1));
267                     if (partType == PatternPartType.DOUBLE_STAR) {
268                         childrenTests.add(patternTest);
269                     }
270                 }
271             } else /* (lastPart) */ {
272                             /* HANDLES THE REMAINING UNCOLOURED AREAS */
273                 if (isIncludePattern) {
274                     assert !isDir; //already handled by blue and yellow areas
275

276                     matches = true;
277                 } else {
278                     matches = false;
279                     break;
280                 }
281             }
282         }
283         return matches;
284     }
285     
286     /**
287      */

288     private boolean isMatchingFile(final File JavaDoc file,
289                                    final AntPattern pattern,
290                                    final int partIndex) {
291         assert file.isDirectory() || file.isFile();
292         
293         final String JavaDoc name = file.getName();
294         final PatternPartType patternType = pattern.patternPartTypes[partIndex];
295         
296         assert patternType == PatternPartType.PLAIN
297                || patternType == PatternPartType.REGEXP;
298         
299         if (patternType == PatternPartType.PLAIN) {
300             final String JavaDoc fileNamePattern = pattern.patternParts[partIndex];
301             return caseSensitive
302                    ? name.equals(fileNamePattern)
303                    : name.equalsIgnoreCase(fileNamePattern);
304         } else {
305             Pattern JavaDoc patternPartMatcher =
306                     pattern.getPatternPartMatcher(partIndex, caseSensitive);
307             assert pattern.patternPartMatchers[partIndex] != null;
308             return pattern.patternPartMatchers[partIndex].matcher(name)
309                    .matches();
310         }
311     }
312     
313     /**
314      */

315     private static Collection JavaDoc<PatternTest> createPatternTests(
316                                         final AntPattern[] includePatterns,
317                                         final AntPattern[] excludePatterns) {
318         Collection JavaDoc<PatternTest> result =
319                 new ArrayList JavaDoc<PatternTest>(includePatterns.length
320                                            + excludePatterns.length);
321         /*
322          * Warning! Method checkFileAgainsPatterns(...) assumes that all include
323          * patterns are added before any exclude pattern. Keep this rule in mind
324          * when changing the code!
325          */

326         for (AntPattern pattern : includePatterns) {
327             if (pattern.patternPartTypes[0] == PatternPartType.DOUBLE_STAR) {
328                 if (pattern.isLastPart(0)) {
329                     /*
330                      * This is a universal include pattern (**).
331                      * There is no need for other include patterns.
332                      */

333                     result.clear();
334                     result.add(new PatternTest(pattern, true, 0));
335                     break;
336                 } else {
337                     result.add(new PatternTest(pattern, true, 1));
338                 }
339             }
340             result.add(new PatternTest(pattern, true, 0));
341         }
342         for (AntPattern pattern : excludePatterns) {
343             if (pattern.patternPartTypes[0] == PatternPartType.DOUBLE_STAR) {
344                 if (pattern.isLastPart(0)) {
345                     /*
346                      * This is a universal exclude pattern (**).
347                      * It excludes everything - there is no need to search
348                      * at all.
349                      */

350                     return Collections.emptyList();
351                 } else {
352                     result.add(new PatternTest(pattern, false, 1));
353                 }
354             }
355             result.add(new PatternTest(pattern, false, 0));
356         }
357         return result;
358     }
359     
360     /**
361      * Prepares a set of include and exclude patterns to be used by
362      * this scanner. It does the following procedures:
363      * <ul>
364      * <li>if no include pattern is specified by the file set,
365      * the default one ({@code **}) is added</li>
366      * <li>if default exclude patterns are to be used, they are added
367      * to the set of exclude patterns specified in the file set</li>
368      * <li>the pattern strings are parsed and split into tokens,
369      * using the file separator character ({@code '/'} or {@code '\\'})
370      * as the token separator</li>
371      * </ul>
372      * The parsed patterns are stored to arrays {@link #includePatterns}
373      * and {@link #excludePatterns}.
374      *
375      * @see AntPattern
376      */

377     private void preparePatterns() {
378         Collection JavaDoc<String JavaDoc> patterns;
379         
380         /* Parse include patterns: */
381         patterns = fileSet.getIncludePatterns();
382         if (patterns.isEmpty()) {
383             patterns = Collections.singletonList("**"); //NOI18N
384
}
385         includePatterns = parsePatternStrings(patterns);
386         
387         /* Parse excludePatterns: */
388         patterns = fileSet.getExcludesPatterns();
389         if (fileSet.isDefaultExcludes()) {
390             Collection JavaDoc<String JavaDoc> defExcludes = Arrays.asList(DEFAULT_EXCLUDES);
391             if (patterns.isEmpty()) {
392                 patterns = defExcludes;
393             } else {
394                 patterns.addAll(defExcludes);
395             }
396         }
397         excludePatterns = parsePatternStrings(patterns);
398     }
399     
400     /**
401      * Parses a collection of pattern strings.
402      *
403      * @param patternStrings collection of Ant pattern strings
404      * @return array of {@code AntPattern} structures representing the same
405      * patterns, in the same order as the given pattern strings
406      * @see #parsePatternString(String)
407      */

408     private AntPattern[] parsePatternStrings(
409                                             Collection JavaDoc<String JavaDoc> patternStrings) {
410         final AntPattern[] patterns = new AntPattern[patternStrings.size()];
411         final Iterator JavaDoc<String JavaDoc> it = patternStrings.iterator();
412         for (int i = 0; i < patterns.length; i++) {
413             patterns[i] = parsePatternString(it.next());
414         }
415         return patterns;
416     }
417     
418     /**
419      * Parses the pattern string - splits it to an array of patterns
420      * of directory names and a pattern of file name.
421      *
422      * @param patternString pattern to be parsed
423      * @return data structure representing the parsed pattern
424      * @see AntPattern
425      */

426     AntPattern parsePatternString(String JavaDoc patternString) {
427         if ((patternString.length() != 0)
428                 && (patternString.charAt(0) == File.separatorChar)) {
429             assert false : "corner case - not implemented"; //TODO - corner case
430
}
431
432         List JavaDoc<String JavaDoc> tokens = new ArrayList JavaDoc<String JavaDoc>(6);
433         boolean lastWasDoubleStar = false;
434         int tokenStart = 0;
435         String JavaDoc token;
436         int slashIndex = patternString.indexOf(File.separatorChar);
437         while (slashIndex != -1) {
438             token = patternString.substring(tokenStart, slashIndex);
439             
440             boolean isDoubleStar = token.equals("**"); //NOI18N
441
if (!(isDoubleStar && lastWasDoubleStar)) {
442                 tokens.add(patternString.substring(tokenStart, slashIndex));
443             }
444             lastWasDoubleStar = isDoubleStar;
445             
446             tokenStart = slashIndex + 1;
447             slashIndex = patternString.indexOf(File.separatorChar,
448                                                tokenStart);
449         }
450         if (tokenStart == patternString.length()) { //pattern ends with '/'
451
token = "**"; //NOI18N
452
} else {
453             token = patternString.substring(tokenStart);
454         }
455         if (!(lastWasDoubleStar && token.equals("**"))) { //NOI18N
456
tokens.add(token);
457         }
458         
459         String JavaDoc[] patternParts = new String JavaDoc[tokens.size()];
460         tokens.toArray(patternParts);
461         return new AntPattern(patternParts);
462     }
463     
464     
465     /**
466      *
467      */

468     static final class PatternTest {
469         final AntPattern pattern;
470         final boolean includePattern;
471         int patternPartIndex;
472         PatternTest(AntPattern pattern, boolean includePattern, int index) {
473             this.pattern = pattern;
474             this.includePattern = includePattern;
475             this.patternPartIndex = index;
476         }
477     }
478     
479     /**
480      *
481      */

482     static final class AntPattern {
483         private static final int CASE_SENSITIVE_FLAGS = 0;
484         private static final int CASE_INSENSITIVE_FLAGS =
485                             Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
486         enum PatternPartType {
487             DOUBLE_STAR,
488             REGEXP,
489             PLAIN
490         }
491         final String JavaDoc[] patternParts;
492         final PatternPartType[] patternPartTypes;
493         private final Pattern JavaDoc[] patternPartMatchers;
494         AntPattern(String JavaDoc[] patternParts) {
495             if (patternParts == null) {
496                 throw new IllegalArgumentException JavaDoc(
497                                             "patternParts: null"); //NOI18N
498
}
499             
500             this.patternParts = patternParts;
501             
502             patternPartTypes = new PatternPartType[patternParts.length];
503             patternPartMatchers = new Pattern JavaDoc[patternParts.length];
504             for (int i = 0; i < patternParts.length; i++) {
505                 final String JavaDoc pattern = patternParts[i];
506                 PatternPartType patternPartType;
507                 if (pattern.equals("**")) { //NOI18N
508
patternPartType = PatternPartType.DOUBLE_STAR;
509                 } else if (pattern.indexOf('*') != -1
510                         || pattern.indexOf('?') != -1) {
511                     patternPartType = PatternPartType.REGEXP;
512                 } else {
513                     patternPartType = PatternPartType.PLAIN;
514                 }
515                 patternPartTypes[i] = patternPartType;
516             }
517         }
518         Pattern JavaDoc getPatternPartMatcher(final int partIndex,
519                                       final boolean caseSensitive) {
520             Pattern JavaDoc matcher = patternPartMatchers[partIndex];
521             if (matcher == null) {
522                 matcher = Pattern.compile(
523                                     makeJdkPattern(patternParts[partIndex]),
524                                     caseSensitive ? CASE_SENSITIVE_FLAGS
525                                                   : CASE_INSENSITIVE_FLAGS);
526                 patternPartMatchers[partIndex] = matcher;
527             }
528             return matcher;
529         }
530         /**
531          * Creates a JDK-notation regular expression accepting the same
532          * strings as the given Ant regular expression.
533          *
534          * @param antRegexp Ant-style regular expression
535          * @return JDK-style regular expression equivalent of the given
536          * Ant-style regular expression
537          */

538         static String JavaDoc makeJdkPattern(String JavaDoc antRegexp) {
539             StringBuilder JavaDoc buf = new StringBuilder JavaDoc(antRegexp.length() + 16);
540             StringTokenizer JavaDoc tokenizer =
541                     new StringTokenizer JavaDoc(antRegexp, "*?", true); //NOI18N
542
while (tokenizer.hasMoreTokens()) {
543                 String JavaDoc token = tokenizer.nextToken();
544                 if (token.length() == 0) {
545                     continue;
546                 }
547                 if (token.equals("?")) { //NOI18N
548
buf.append(token);
549                 } else if (token.equals("*")) { //NOI18N
550
buf.append(".*"); //NOI18N
551
} else {
552                     buf.append(quote(token));
553                 }
554             }
555             return buf.toString();
556         }
557         /**
558          * Makes a JDK-style regular expression accepting the given string.
559          *
560          * @param str string to be accepted by the returned regular expression
561          * @return regular expression accepting the given string and nothing
562          * else (in the JDK's {@code java.util.regex} notation)
563          * or the passed string instance if it did not contain
564          * any regexp special characters
565          */

566         static String JavaDoc quote(String JavaDoc str) {
567             final String JavaDoc SPECIAL_CHARS = "\\.[](){}+^$|?*"; //NOI18N
568
StringBuilder JavaDoc buf = null;
569             char[] chars = str.toCharArray();
570             for (int i = 0; i < chars.length; i++) {
571                 char c = chars[i];
572                 if (SPECIAL_CHARS.indexOf(c) != -1) {
573                     if (buf == null) {
574                         buf = new StringBuilder JavaDoc(str.length() + 10);
575                         buf.append(str.substring(0, i));
576                     }
577                     buf.append('\\');
578                 }
579                 if (buf != null) {
580                     buf.append(c);
581                 }
582             }
583             return buf != null ? buf.toString() : str;
584         }
585         boolean isLastPart(int index) {
586             return index == (patternParts.length - 1);
587         }
588         @Override JavaDoc
589         public boolean equals(Object JavaDoc object) {
590             return (object != null)
591                    && (object.getClass() == AntPattern.class)
592                    && Arrays.equals(patternParts,
593                                     ((AntPattern) object).patternParts);
594         }
595         @Override JavaDoc
596         public String JavaDoc toString() {
597             String JavaDoc patternsString;
598             if (patternParts.length == 0) {
599                 patternsString = "[]"; //NOI18N
600
} else {
601                 StringBuilder JavaDoc buf = new StringBuilder JavaDoc(256);
602                 buf.append('[');
603                 buf.append(patternParts[0]);
604                 for (int i = 1; i < patternParts.length; i++) {
605                     buf.append(',').append(patternParts[i]);
606                 }
607                 buf.append(']');
608                 patternsString = buf.toString();
609             }
610             return super.toString() + patternsString;
611         }
612     }
613     
614 }
615
Popular Tags