1 package org.apache.maven.shared.utils.io;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.nio.file.Files;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
30
31 import javax.annotation.Nonnull;
32 import javax.annotation.Nullable;
33
34 /**
35 * <p>Class for scanning a directory for files/directories which match certain criteria.</p>
36 * <p>
37 * These criteria consist of selectors and patterns which have been specified. With the selectors you can select which
38 * files you want to have included. Files which are not selected are excluded. With patterns you can include or exclude
39 * files based on their filename.
40 * </p>
41 * <p>
42 * The idea is simple. A given directory is recursively scanned for all files and directories. Each file/directory is
43 * matched against a set of selectors, including special support for matching against filenames with include and and
44 * exclude patterns. Only files/directories which match at least one pattern of the include pattern list or other file
45 * selector, and don't match any pattern of the exclude pattern list or fail to match against a required selector will
46 * be placed in the list of files/directories found.
47 * </p>
48 * <p>
49 * When no list of include patterns is supplied, "**" will be used, which means that everything will be matched. When no
50 * list of exclude patterns is supplied, an empty list is used, such that nothing will be excluded. When no selectors
51 * are supplied, none are applied.
52 * </p>
53 * <p>
54 * The filename pattern matching is done as follows: The name to be matched is split up in path segments. A path segment
55 * is the name of a directory or file, which is bounded by <code>File.separator</code> ('/' under UNIX, '\' under
56 * Windows). For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", "def","ghi" and "xyz.java". The same
57 * is done for the pattern against which should be matched.
58 * </p>
59 * <p>
60 * The segments of the name and the pattern are then matched against each other. When '**' is used for a path segment in
61 * the pattern, it matches zero or more path segments of the name.
62 * </p>
63 * <p>
64 * There is a special case regarding the use of <code>File.separator</code>s at the beginning of the pattern and the
65 * string to match:<br>
66 * When a pattern starts with a <code>File.separator</code>, the string to match must also start with a
67 * <code>File.separator</code>. When a pattern does not start with a <code>File.separator</code>, the string to match
68 * may not start with a <code>File.separator</code>. When one of these rules is not obeyed, the string will not match.
69 * </p>
70 * <p>
71 * When a name path segment is matched against a pattern path segment, the following special characters can be used:<br>
72 * '*' matches zero or more characters<br>
73 * '?' matches one character.
74 * </p>
75 * <p>
76 * Examples:
77 * <ul>
78 * <li>"**\*.class" matches all .class files/dirs in a directory tree.</li>
79 * <li>"test\a??.java" matches all files/dirs which start with an 'a', then two more characters and then ".java", in a
80 * directory called test.</li>
81 * <li>"**" matches everything in a directory tree.</li>
82 * <li>"**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent directory called test
83 * (e.g. "abc\test\def\ghi\XYZ123").</li>
84 * </ul>
85 * <p>
86 * Case sensitivity may be turned off if necessary. By default, it is turned on.
87 * </p>
88 * <p>
89 * Example of usage:
90 * </p>
91 * <pre>
92 * String[] includes = { "**\\*.class" };
93 * String[] excludes = { "modules\\*\\**" };
94 * ds.setIncludes( includes );
95 * ds.setExcludes( excludes );
96 * ds.setBasedir( new File( "test" ) );
97 * ds.setCaseSensitive( true );
98 * ds.scan();
99 *
100 * System.out.println( "FILES:" );
101 * String[] files = ds.getIncludedFiles();
102 * for ( int i = 0; i < files.length; i++ )
103 * {
104 * System.out.println( files[i] );
105 * }
106 * </pre>
107 * <p>
108 * This will scan a directory called test for .class files, but excludes all files in all proper subdirectories of a
109 * directory called "modules"
110 * </p>
111 * <p>
112 * This class must not be used from multiple Threads concurrently!
113 * </p>
114 *
115 * @author Arnout J. Kuiper <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
116 * @author Magesh Umasankar
117 * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
118 * @author <a href="mailto:levylambert@tiscali-dsl.de">Antoine Levy-Lambert</a>
119 * @deprecated use {@code java.nio.file.DirectoryStream} and related classes
120 */
121 @Deprecated
122 public class DirectoryScanner
123 {
124 /**
125 * Patterns which should be excluded by default.
126 *
127 * @see #addDefaultExcludes()
128 */
129 public static final String[] DEFAULTEXCLUDES = {
130 // Miscellaneous typical temporary files
131 "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*",
132
133 // CVS
134 "**/CVS", "**/CVS/**", "**/.cvsignore",
135
136 // Subversion
137 "**/.svn", "**/.svn/**",
138
139 // Arch
140 "**/.arch-ids", "**/.arch-ids/**",
141
142 // Bazaar
143 "**/.bzr", "**/.bzr/**",
144
145 // SurroundSCM
146 "**/.MySCMServerInfo",
147
148 // Mac
149 "**/.DS_Store",
150
151 // Serena Dimensions Version 10
152 "**/.metadata", "**/.metadata/**",
153
154 // Mercurial
155 "**/.hg", "**/.hg/**",
156
157 // git
158 "**/.git", "**/.git/**",
159
160 // BitKeeper
161 "**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**",
162
163 // darcs
164 "**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/**", "**/-darcs-backup*", "**/.darcs-temp-mail" };
165
166 /**
167 * The base directory to be scanned.
168 */
169 private File basedir;
170
171 /**
172 * The patterns for the files to be included.
173 */
174 private String[] includes;
175
176 /**
177 * The patterns for the files to be excluded.
178 */
179 private String[] excludes;
180
181 private MatchPatterns excludesPatterns;
182
183 private MatchPatterns includesPatterns;
184
185
186 /**
187 * The files which matched at least one include and no excludes and were selected.
188 */
189 private List<String> filesIncluded;
190
191 /**
192 * The files which did not match any includes or selectors.
193 */
194 private List<String> filesNotIncluded;
195
196 /**
197 * The files which matched at least one include and at least one exclude.
198 */
199 private List<String> filesExcluded;
200
201 /**
202 * The directories which matched at least one include and no excludes and were selected.
203 */
204 private List<String> dirsIncluded;
205
206 /**
207 * The directories which were found and did not match any includes.
208 */
209 private List<String> dirsNotIncluded;
210
211 /**
212 * The directories which matched at least one include and at least one exclude.
213 */
214 private List<String> dirsExcluded;
215
216 /**
217 * Whether or not our results were built by a slow scan.
218 */
219 private boolean haveSlowResults = false;
220
221 /**
222 * Whether or not the file system should be treated as a case sensitive one.
223 */
224 private boolean isCaseSensitive = true;
225
226 /**
227 * Whether or not symbolic links should be followed.
228 *
229 *
230 */
231 private boolean followSymlinks = true;
232
233
234 /**
235 * A {@link ScanConductor} an control the scanning process.
236 */
237 private ScanConductor scanConductor = null;
238
239 /**
240 * The last ScanAction. We need to store this in the instance as the scan() method doesn't return
241 */
242 private ScanConductor.ScanAction scanAction = null;
243
244 /**
245 * Sole constructor.
246 */
247 public DirectoryScanner()
248 {
249 }
250
251 /**
252 * Sets the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\'
253 * characters are replaced by <code>File.separatorChar</code>, so the separator used need not match
254 * <code>File.separatorChar</code>.
255 *
256 * @param basedir The base directory to scan. Must not be <code>null</code>.
257 */
258 public void setBasedir( final String basedir )
259 {
260 setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ) ) );
261 }
262
263 /**
264 * Sets the base directory to be scanned. This is the directory which is scanned recursively.
265 *
266 * @param basedir The base directory for scanning. Should not be <code>null</code>.
267 */
268 public void setBasedir( @Nonnull final File basedir )
269 {
270 this.basedir = basedir;
271 }
272
273 /**
274 * Returns the base directory to be scanned. This is the directory which is scanned recursively.
275 *
276 * @return the base directory to be scanned
277 */
278 public File getBasedir()
279 {
280 return basedir;
281 }
282
283 /**
284 * Sets whether or not the file system should be regarded as case sensitive.
285 *
286 * @param isCaseSensitiveParameter whether or not the file system should be regarded as a case sensitive one
287 */
288 public void setCaseSensitive( final boolean isCaseSensitiveParameter )
289 {
290 this.isCaseSensitive = isCaseSensitiveParameter;
291 }
292
293 /**
294 * Sets whether or not symbolic links should be followed.
295 *
296 * @param followSymlinks whether or not symbolic links should be followed
297 */
298 public void setFollowSymlinks( final boolean followSymlinks )
299 {
300 this.followSymlinks = followSymlinks;
301 }
302
303 /**
304 * Sets the list of include patterns to use. All '/' and '\' characters are replaced by
305 * <code>File.separatorChar</code>, so the separator used need not match <code>File.separatorChar</code>.
306 * <p>
307 * When a pattern ends with a '/' or '\', "**" is appended.
308 * </p>
309 *
310 * @param includes A list of include patterns. May be <code>null</code>, indicating that all files should be
311 * included. If a non-<code>null</code> list is given, all elements must be non-<code>null</code>.
312 */
313 public void setIncludes( final String... includes )
314 {
315 if ( includes == null )
316 {
317 this.includes = null;
318 }
319 else
320 {
321 this.includes = new String[includes.length];
322 for ( int i = 0; i < includes.length; i++ )
323 {
324 String pattern;
325 pattern = includes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
326 if ( pattern.endsWith( File.separator ) )
327 {
328 pattern += "**";
329 }
330 this.includes[i] = pattern;
331 }
332 }
333 }
334
335 /**
336 * Sets the list of exclude patterns to use. All '/' and '\' characters are replaced by
337 * <code>File.separatorChar</code>, so the separator used need not match <code>File.separatorChar</code>.
338 * <p>
339 * When a pattern ends with a '/' or '\', "**" is appended.
340 * </p>
341 *
342 * @param excludes A list of exclude patterns. May be <code>null</code>, indicating that no files should be
343 * excluded. If a non-<code>null</code> list is given, all elements must be non-<code>null</code>.
344 */
345 public void setExcludes( final String... excludes )
346 {
347 if ( excludes == null )
348 {
349 this.excludes = null;
350 }
351 else
352 {
353 this.excludes = new String[excludes.length];
354 for ( int i = 0; i < excludes.length; i++ )
355 {
356 String pattern;
357 pattern = excludes[i].trim().replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
358 if ( pattern.endsWith( File.separator ) )
359 {
360 pattern += "**";
361 }
362 this.excludes[i] = pattern;
363 }
364 }
365 }
366
367 /**
368 * @param scanConductor {@link #scanConductor}
369 */
370 public void setScanConductor( final ScanConductor scanConductor )
371 {
372 this.scanConductor = scanConductor;
373 }
374
375 /**
376 * Scans the base directory for files which match at least one include pattern and don't match any exclude patterns.
377 * If there are selectors then the files must pass muster there, as well.
378 *
379 * @throws IllegalStateException if the base directory was set incorrectly (i.e. if it is <code>null</code>,
380 * doesn't exist, or isn't a directory).
381 */
382 public void scan()
383 throws IllegalStateException
384 {
385 if ( basedir == null )
386 {
387 throw new IllegalStateException( "No basedir set" );
388 }
389 if ( !basedir.exists() )
390 {
391 throw new IllegalStateException( "basedir " + basedir + " does not exist" );
392 }
393 if ( !basedir.isDirectory() )
394 {
395 throw new IllegalStateException( "basedir " + basedir + " is not a directory" );
396 }
397
398 setupDefaultFilters();
399 setupMatchPatterns();
400
401 filesIncluded = new ArrayList<String>();
402 filesNotIncluded = new ArrayList<String>();
403 filesExcluded = new ArrayList<String>();
404 dirsIncluded = new ArrayList<String>();
405 dirsNotIncluded = new ArrayList<String>();
406 dirsExcluded = new ArrayList<String>();
407 scanAction = ScanConductor.ScanAction.CONTINUE;
408
409 if ( isIncluded( "" ) )
410 {
411 if ( !isExcluded( "" ) )
412 {
413 if ( scanConductor != null )
414 {
415 scanAction = scanConductor.visitDirectory( "", basedir );
416
417 if ( ScanConductor.ScanAction.ABORT.equals( scanAction )
418 || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction )
419 || ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) )
420 {
421 return;
422 }
423 }
424
425 dirsIncluded.add( "" );
426 }
427 else
428 {
429 dirsExcluded.add( "" );
430 }
431 }
432 else
433 {
434 dirsNotIncluded.add( "" );
435 }
436 scandir( basedir, "", true );
437 }
438
439 /**
440 * Determine the file differences between the currently included files and
441 * a previously captured list of files.
442 * This method will not look for a changed in content but sole in the
443 * list of files given.
444 * <p>
445 * The method will compare the given array of file Strings with the result
446 * of the last directory scan. It will execute a {@link #scan()} if no
447 * result of a previous scan could be found.
448 * </p>
449 * <p>
450 * The result of the diff can be queried by the methods
451 * {@link DirectoryScanResult#getFilesAdded()} and {@link DirectoryScanResult#getFilesRemoved()}
452 * </p>
453 *
454 * @param oldFiles the list of previously captured files names.
455 * @return the result of the directory scan.
456 */
457 public DirectoryScanResult diffIncludedFiles( String... oldFiles )
458 {
459 if ( filesIncluded == null )
460 {
461 // perform a scan if the directory didn't got scanned yet
462 scan();
463 }
464
465 return diffFiles( oldFiles, filesIncluded.toArray( new String[filesIncluded.size()] ) );
466 }
467
468 /**
469 * @param oldFiles array of old files
470 * @param newFiles array of new files
471 * @return calculated difference
472 */
473 public static DirectoryScanResult diffFiles( @Nullable String[] oldFiles, @Nullable String[] newFiles )
474 {
475 Set<String> oldFileSet = arrayAsHashSet( oldFiles );
476 Set<String> newFileSet = arrayAsHashSet( newFiles );
477
478 List<String> added = new ArrayList<String>();
479 List<String> removed = new ArrayList<String>();
480
481 for ( String oldFile : oldFileSet )
482 {
483 if ( !newFileSet.contains( oldFile ) )
484 {
485 removed.add( oldFile );
486 }
487 }
488
489 for ( String newFile : newFileSet )
490 {
491 if ( !oldFileSet.contains( newFile ) )
492 {
493 added.add( newFile );
494 }
495 }
496
497 String[] filesAdded = added.toArray( new String[added.size()] );
498 String[] filesRemoved = removed.toArray( new String[removed.size()] );
499
500 return new DirectoryScanResult( filesAdded, filesRemoved );
501 }
502
503
504 /**
505 * Take an array of type T and convert it into a HashSet of type T.
506 * If <code>null</code> or an empty array gets passed, an empty Set will be returned.
507 *
508 * @param array The array
509 * @return the filled HashSet of type T
510 */
511 private static <T> Set<T> arrayAsHashSet( @Nullable T[] array )
512 {
513 if ( array == null || array.length == 0 )
514 {
515 return Collections.emptySet();
516 }
517
518 Set<T> set = new HashSet<T>( array.length );
519 Collections.addAll( set, array );
520
521 return set;
522 }
523
524 /**
525 * Top level invocation for a slow scan. A slow scan builds up a full list of excluded/included files/directories,
526 * whereas a fast scan will only have full results for included files, as it ignores directories which can't
527 * possibly hold any included files/directories.
528 * <p/>
529 * Returns immediately if a slow scan has already been completed.
530 */
531 void slowScan()
532 {
533 if ( haveSlowResults )
534 {
535 return;
536 }
537
538 final String[] excl = dirsExcluded.toArray( new String[dirsExcluded.size()] );
539
540 final String[] notIncl = dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] );
541
542 for ( String anExcl : excl )
543 {
544 if ( !couldHoldIncluded( anExcl ) )
545 {
546 scandir( new File( basedir, anExcl ), anExcl + File.separator, false );
547 }
548 }
549
550 for ( String aNotIncl : notIncl )
551 {
552 if ( !couldHoldIncluded( aNotIncl ) )
553 {
554 scandir( new File( basedir, aNotIncl ), aNotIncl + File.separator, false );
555 }
556 }
557
558 haveSlowResults = true;
559 }
560
561 /**
562 * Scans the given directory for files and directories. Found files and directories are placed in their respective
563 * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is
564 * scanned recursively.
565 *
566 * @param dir The directory to scan. Must not be <code>null</code>.
567 * @param vpath The path relative to the base directory (needed to prevent problems with an absolute path when using
568 * dir). Must not be <code>null</code>.
569 * @param fast Whether or not this call is part of a fast scan.
570 * @see #filesIncluded
571 * @see #filesNotIncluded
572 * @see #filesExcluded
573 * @see #dirsIncluded
574 * @see #dirsNotIncluded
575 * @see #dirsExcluded
576 * @see #slowScan
577 */
578 void scandir( @Nonnull final File dir, @Nonnull final String vpath, final boolean fast )
579 {
580 String[] newfiles = dir.list();
581
582 if ( newfiles == null )
583 {
584 /*
585 * two reasons are mentioned in the API docs for File.list (1) dir is not a directory. This is impossible as
586 * we wouldn't get here in this case. (2) an IO error occurred (why doesn't it throw an exception then???)
587 */
588
589 /*
590 * [jdcasey] (2) is apparently happening to me, as this is killing one of my tests... this is affecting the
591 * assembly plugin, fwiw. I will initialize the newfiles array as zero-length for now. NOTE: I can't find
592 * the problematic code, as it appears to come from a native method in UnixFileSystem...
593 */
594 newfiles = new String[0];
595
596 // throw new IOException( "IO error scanning directory " + dir.getAbsolutePath() );
597 }
598
599 if ( !followSymlinks )
600 {
601 newfiles = doNotFollowSymbolicLinks( dir, vpath, newfiles );
602 }
603
604 for ( final String newfile : newfiles )
605 {
606 final String name = vpath + newfile;
607 final File file = new File( dir, newfile );
608 if ( file.isDirectory() )
609 {
610 if ( isIncluded( name ) )
611 {
612 if ( !isExcluded( name ) )
613 {
614 if ( scanConductor != null )
615 {
616 scanAction = scanConductor.visitDirectory( name, file );
617
618 if ( ScanConductor.ScanAction.ABORT.equals( scanAction )
619 || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) )
620 {
621 return;
622 }
623 }
624
625 if ( !ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) )
626 {
627 dirsIncluded.add( name );
628 if ( fast )
629 {
630 scandir( file, name + File.separator, fast );
631
632 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) )
633 {
634 return;
635 }
636 }
637 }
638 scanAction = null;
639
640 }
641 else
642 {
643 dirsExcluded.add( name );
644 if ( fast && couldHoldIncluded( name ) )
645 {
646 scandir( file, name + File.separator, fast );
647 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) )
648 {
649 return;
650 }
651 scanAction = null;
652 }
653 }
654 }
655 else
656 {
657 if ( fast && couldHoldIncluded( name ) )
658 {
659 if ( scanConductor != null )
660 {
661 scanAction = scanConductor.visitDirectory( name, file );
662
663 if ( ScanConductor.ScanAction.ABORT.equals( scanAction )
664 || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) )
665 {
666 return;
667 }
668 }
669 if ( !ScanConductor.ScanAction.NO_RECURSE.equals( scanAction ) )
670 {
671 dirsNotIncluded.add( name );
672
673 scandir( file, name + File.separator, fast );
674 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) )
675 {
676 return;
677 }
678 }
679 scanAction = null;
680 }
681 }
682 if ( !fast )
683 {
684 scandir( file, name + File.separator, fast );
685 if ( ScanConductor.ScanAction.ABORT.equals( scanAction ) )
686 {
687 return;
688 }
689 scanAction = null;
690 }
691 }
692 else if ( file.isFile() )
693 {
694 if ( isIncluded( name ) )
695 {
696 if ( !isExcluded( name ) )
697 {
698 if ( scanConductor != null )
699 {
700 scanAction = scanConductor.visitFile( name, file );
701 }
702
703 if ( ScanConductor.ScanAction.ABORT.equals( scanAction )
704 || ScanConductor.ScanAction.ABORT_DIRECTORY.equals( scanAction ) )
705 {
706 return;
707 }
708
709 filesIncluded.add( name );
710 }
711 else
712 {
713 filesExcluded.add( name );
714 }
715 }
716 else
717 {
718 filesNotIncluded.add( name );
719 }
720 }
721 }
722 }
723
724 private String[] doNotFollowSymbolicLinks( final File dir, final String vpath, String[] newfiles )
725 {
726 final List<String> noLinks = new ArrayList<String>();
727 for ( final String newfile : newfiles )
728 {
729 try
730 {
731 if ( isSymbolicLink( dir, newfile ) )
732 {
733 final String name = vpath + newfile;
734 final File file = new File( dir, newfile );
735 if ( file.isDirectory() )
736 {
737 dirsExcluded.add( name );
738 }
739 else
740 {
741 filesExcluded.add( name );
742 }
743 }
744 else
745 {
746 noLinks.add( newfile );
747 }
748 }
749 catch ( final IOException ioe )
750 {
751 final String msg =
752 "IOException caught while checking " + "for links, couldn't get cannonical path!";
753 // will be caught and redirected to Ant's logging system
754 System.err.println( msg );
755 noLinks.add( newfile );
756 }
757 }
758 newfiles = noLinks.toArray( new String[noLinks.size()] );
759 return newfiles;
760 }
761
762 /**
763 * Tests whether or not a name matches against at least one include pattern.
764 *
765 * @param name The name to match. Must not be <code>null</code>.
766 * @return <code>true</code> when the name matches against at least one include pattern, or <code>false</code>
767 * otherwise.
768 */
769 boolean isIncluded( final String name )
770 {
771 return includesPatterns.matches( name, isCaseSensitive );
772 }
773
774 /**
775 * Tests whether or not a name matches the start of at least one include pattern.
776 *
777 * @param name The name to match. Must not be <code>null</code>.
778 * @return <code>true</code> when the name matches against the start of at least one include pattern, or
779 * <code>false</code> otherwise.
780 */
781 boolean couldHoldIncluded( @Nonnull final String name )
782 {
783 return includesPatterns.matchesPatternStart( name, isCaseSensitive );
784 }
785
786 /**
787 * Tests whether or not a name matches against at least one exclude pattern.
788 *
789 * @param name The name to match. Must not be <code>null</code>.
790 * @return <code>true</code> when the name matches against at least one exclude pattern, or <code>false</code>
791 * otherwise.
792 */
793 boolean isExcluded( @Nonnull final String name )
794 {
795 return excludesPatterns.matches( name, isCaseSensitive );
796 }
797
798 /**
799 * Returns the names of the files which matched at least one of the include patterns and none of the exclude
800 * patterns. The names are relative to the base directory.
801 *
802 * @deprecated this method does not work correctly on Windows.
803 * @return the names of the files which matched at least one of the include patterns and none of the exclude
804 * patterns. May also contain symbolic links to files.
805 */
806 @Deprecated
807 public String[] getIncludedFiles()
808 {
809 if ( filesIncluded == null )
810 {
811 return new String[0];
812 }
813 return filesIncluded.toArray( new String[filesIncluded.size()] );
814 }
815
816 /**
817 * Returns the names of the files which matched none of the include patterns. The names are relative to the base
818 * directory. This involves performing a slow scan if one has not already been completed.
819 *
820 * @return the names of the files which matched none of the include patterns.
821 * @see #slowScan
822 */
823 public String[] getNotIncludedFiles()
824 {
825 slowScan();
826 return filesNotIncluded.toArray( new String[filesNotIncluded.size()] );
827 }
828
829 /**
830 * Returns the names of the files which matched at least one of the include patterns and at least one of the exclude
831 * patterns. The names are relative to the base directory. This involves performing a slow scan if one has not
832 * already been completed.
833 *
834 * @return the names of the files which matched at least one of the include patterns and at at least one of the
835 * exclude patterns.
836 * @see #slowScan
837 */
838 public String[] getExcludedFiles()
839 {
840 slowScan();
841 return filesExcluded.toArray( new String[filesExcluded.size()] );
842 }
843
844 /**
845 * Returns the names of the directories which matched at least one of the include patterns and none of the exclude
846 * patterns. The names are relative to the base directory.
847 *
848 * @deprecated this method is buggy. Do not depend on it.
849 * @return the names of the directories which matched at least one of the include patterns and none of the exclude
850 * patterns. May also contain symbolic links to directories.
851 */
852 @Deprecated
853 public String[] getIncludedDirectories()
854 {
855 return dirsIncluded.toArray( new String[dirsIncluded.size()] );
856 }
857
858 /**
859 * Returns the names of the directories which matched none of the include patterns. The names are relative to the
860 * base directory. This involves performing a slow scan if one has not already been completed.
861 *
862 * @return the names of the directories which matched none of the include patterns.
863 * @see #slowScan
864 */
865 public String[] getNotIncludedDirectories()
866 {
867 slowScan();
868 return dirsNotIncluded.toArray( new String[dirsNotIncluded.size()] );
869 }
870
871 /**
872 * Returns the names of the directories which matched at least one of the include patterns and at least one of the
873 * exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has
874 * not already been completed.
875 *
876 * @return the names of the directories which matched at least one of the include patterns and at least one of the
877 * exclude patterns.
878 * @see #slowScan
879 */
880 public String[] getExcludedDirectories()
881 {
882 slowScan();
883 return dirsExcluded.toArray( new String[dirsExcluded.size()] );
884 }
885
886 /**
887 * Adds default exclusions to the current exclusions set.
888 */
889 public void addDefaultExcludes()
890 {
891 final int excludesLength = excludes == null ? 0 : excludes.length;
892 String[] newExcludes;
893 newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length];
894 if ( excludesLength > 0 )
895 {
896 System.arraycopy( excludes, 0, newExcludes, 0, excludesLength );
897 }
898 for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ )
899 {
900 newExcludes[i + excludesLength] =
901 DEFAULTEXCLUDES[i].replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
902 }
903 excludes = newExcludes;
904 }
905
906 /**
907 * Checks whether a given file is a symbolic link.
908 * <p>
909 * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are identical
910 * - this may lead to false positives on some platforms.
911 * </p>
912 *
913 * @param parent the parent directory of the file to test
914 * @param name the name of the file to test.
915 *
916 */
917 boolean isSymbolicLink( final File parent, final String name )
918 throws IOException
919 {
920 return Files.isSymbolicLink( parent.toPath() );
921 }
922
923 private void setupDefaultFilters()
924 {
925 if ( includes == null )
926 {
927 // No includes supplied, so set it to 'matches all'
928 includes = new String[1];
929 includes[0] = "**";
930 }
931 if ( excludes == null )
932 {
933 excludes = new String[0];
934 }
935 }
936
937
938 private void setupMatchPatterns()
939 {
940 includesPatterns = MatchPatterns.from( includes );
941 excludesPatterns = MatchPatterns.from( excludes );
942 }
943
944 }