1 package org.apache.maven.shared.model.fileset.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.LinkedHashMap;
30 import java.util.LinkedList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 import org.apache.maven.plugin.logging.Log;
36 import org.apache.maven.shared.io.logging.DefaultMessageHolder;
37 import org.apache.maven.shared.io.logging.MessageHolder;
38 import org.apache.maven.shared.io.logging.MessageLevels;
39 import org.apache.maven.shared.io.logging.MojoLogSink;
40 import org.apache.maven.shared.io.logging.PlexusLoggerSink;
41 import org.apache.maven.shared.model.fileset.FileSet;
42 import org.apache.maven.shared.model.fileset.mappers.FileNameMapper;
43 import org.apache.maven.shared.model.fileset.mappers.MapperException;
44 import org.apache.maven.shared.model.fileset.mappers.MapperUtil;
45 import org.apache.maven.shared.utils.io.DirectoryScanner;
46 import org.apache.maven.shared.utils.io.FileUtils;
47 import org.codehaus.plexus.logging.Logger;
48
49
50
51
52
53
54
55
56 public class FileSetManager
57 {
58 private static final String[] EMPTY_STRING_ARRAY = new String[0];
59
60 private final boolean verbose;
61
62 private MessageHolder messages;
63
64
65
66
67
68
69
70
71
72
73
74 public FileSetManager( Log log, boolean verbose )
75 {
76 if ( verbose )
77 {
78 this.messages =
79 new DefaultMessageHolder( MessageLevels.LEVEL_DEBUG, MessageLevels.LEVEL_INFO, new MojoLogSink( log ) );
80 }
81 else
82 {
83 this.messages =
84 new DefaultMessageHolder( MessageLevels.LEVEL_INFO, MessageLevels.LEVEL_INFO, new MojoLogSink( log ) );
85 }
86
87 this.verbose = verbose;
88 }
89
90
91
92
93
94
95 public FileSetManager( Log log )
96 {
97 this.messages =
98 new DefaultMessageHolder( MessageLevels.LEVEL_INFO, MessageLevels.LEVEL_INFO, new MojoLogSink( log ) );
99 this.verbose = false;
100 }
101
102
103
104
105
106
107
108 public FileSetManager( Logger log, boolean verbose )
109 {
110 if ( verbose )
111 {
112 this.messages = new DefaultMessageHolder( MessageLevels.LEVEL_DEBUG, MessageLevels.LEVEL_INFO,
113 new PlexusLoggerSink( log ) );
114 }
115 else
116 {
117 this.messages = new DefaultMessageHolder( MessageLevels.LEVEL_INFO, MessageLevels.LEVEL_INFO,
118 new PlexusLoggerSink( log ) );
119 }
120
121 this.verbose = verbose;
122 }
123
124
125
126
127
128
129 public FileSetManager( Logger log )
130 {
131 this.messages =
132 new DefaultMessageHolder( MessageLevels.LEVEL_INFO, MessageLevels.LEVEL_INFO, new PlexusLoggerSink( log ) );
133 this.verbose = false;
134 }
135
136
137
138
139 public FileSetManager()
140 {
141 this.verbose = false;
142 }
143
144
145
146
147
148
149
150
151
152
153
154 public Map<String, String> mapIncludedFiles( FileSet fileSet )
155 throws MapperException
156 {
157 String[] sourcePaths = getIncludedFiles( fileSet );
158 Map<String, String> mappedPaths = new LinkedHashMap<String, String>();
159
160 FileNameMapper fileMapper = MapperUtil.getFileNameMapper( fileSet.getMapper() );
161
162 for ( int i = 0; i < sourcePaths.length; i++ )
163 {
164 String sourcePath = sourcePaths[i];
165
166 String destPath;
167 if ( fileMapper != null )
168 {
169 destPath = fileMapper.mapFileName( sourcePath );
170 }
171 else
172 {
173 destPath = sourcePath;
174 }
175
176 mappedPaths.put( sourcePath, destPath );
177 }
178
179 return mappedPaths;
180 }
181
182
183
184
185
186
187
188 public String[] getIncludedFiles( FileSet fileSet )
189 {
190 DirectoryScanner scanner = scan( fileSet );
191
192 if ( scanner != null )
193 {
194 return scanner.getIncludedFiles();
195 }
196
197 return EMPTY_STRING_ARRAY;
198 }
199
200
201
202
203
204
205
206 public String[] getIncludedDirectories( FileSet fileSet )
207 {
208 DirectoryScanner scanner = scan( fileSet );
209
210 if ( scanner != null )
211 {
212 return scanner.getIncludedDirectories();
213 }
214
215 return EMPTY_STRING_ARRAY;
216 }
217
218
219
220
221
222
223
224 public String[] getExcludedFiles( FileSet fileSet )
225 {
226 DirectoryScanner scanner = scan( fileSet );
227
228 if ( scanner != null )
229 {
230 return scanner.getExcludedFiles();
231 }
232
233 return EMPTY_STRING_ARRAY;
234 }
235
236
237
238
239
240
241
242 public String[] getExcludedDirectories( FileSet fileSet )
243 {
244 DirectoryScanner scanner = scan( fileSet );
245
246 if ( scanner != null )
247 {
248 return scanner.getExcludedDirectories();
249 }
250
251 return EMPTY_STRING_ARRAY;
252 }
253
254
255
256
257
258
259
260 public void delete( FileSet fileSet )
261 throws IOException
262 {
263 delete( fileSet, true );
264 }
265
266
267
268
269
270
271
272
273
274 public void delete( FileSet fileSet, boolean throwsError )
275 throws IOException
276 {
277 Set<String> deletablePaths = findDeletablePaths( fileSet );
278
279 if ( messages != null && messages.isDebugEnabled() )
280 {
281 String paths = String.valueOf( deletablePaths ).replace( ',', '\n' );
282 messages.addDebugMessage( "Found deletable paths: " + paths ).flush();
283 }
284
285 List<String> warnMessages = new LinkedList<String>();
286
287 for ( Iterator<String> it = deletablePaths.iterator(); it.hasNext(); )
288 {
289 String path = it.next();
290
291 File file = new File( fileSet.getDirectory(), path );
292
293 if ( file.exists() )
294 {
295 if ( file.isDirectory() )
296 {
297 if ( fileSet.isFollowSymlinks() || !isSymlink( file ) )
298 {
299 if ( verbose && messages != null )
300 {
301 messages.addInfoMessage( "Deleting directory: " + file ).flush();
302 }
303
304 removeDir( file, fileSet.isFollowSymlinks(), throwsError, warnMessages );
305 }
306 else
307 {
308 if ( verbose && messages != null )
309 {
310 messages.addInfoMessage( "Deleting symlink to directory: " + file ).flush();
311 }
312
313 if ( !file.delete() )
314 {
315 String message = "Unable to delete symlink " + file.getAbsolutePath();
316 if ( throwsError )
317 {
318 throw new IOException( message );
319 }
320
321 if ( !warnMessages.contains( message ) )
322 {
323 warnMessages.add( message );
324 }
325 }
326 }
327 }
328 else
329 {
330 if ( verbose && messages != null )
331 {
332 messages.addInfoMessage( "Deleting file: " + file ).flush();
333 }
334
335 if ( !delete( file ) )
336 {
337 String message = "Failed to delete file " + file.getAbsolutePath() + ". Reason is unknown.";
338 if ( throwsError )
339 {
340 throw new IOException( message );
341 }
342
343 warnMessages.add( message );
344 }
345 }
346 }
347 }
348
349 if ( messages != null && messages.isWarningEnabled() && !throwsError && ( warnMessages.size() > 0 ) )
350 {
351 for ( Iterator<String> it = warnMessages.iterator(); it.hasNext(); )
352 {
353 messages.addWarningMessage( it.next() ).flush();
354 }
355 }
356 }
357
358
359
360
361
362 private boolean isSymlink( File file )
363 throws IOException
364 {
365 File fileInCanonicalParent = null;
366 File parentDir = file.getParentFile();
367 if ( parentDir == null )
368 {
369 fileInCanonicalParent = file;
370 }
371 else
372 {
373 fileInCanonicalParent = new File( parentDir.getCanonicalPath(), file.getName() );
374 }
375 if ( messages != null && messages.isDebugEnabled() )
376 {
377 messages.addDebugMessage( "Checking for symlink:\nFile's canonical path: "
378 + fileInCanonicalParent.getCanonicalPath() + "\nFile's absolute path with canonical parent: "
379 + fileInCanonicalParent.getPath() ).flush();
380 }
381 return !fileInCanonicalParent.getCanonicalFile().equals( fileInCanonicalParent.getAbsoluteFile() );
382 }
383
384 private Set<String> findDeletablePaths( FileSet fileSet )
385 {
386 Set<String> includes = findDeletableDirectories( fileSet );
387 includes.addAll( findDeletableFiles( fileSet, includes ) );
388
389 return includes;
390 }
391
392 private Set<String> findDeletableDirectories( FileSet fileSet )
393 {
394 if ( verbose && messages != null )
395 {
396 messages.addInfoMessage( "Scanning for deletable directories." ).flush();
397 }
398
399 DirectoryScanner scanner = scan( fileSet );
400
401 if ( scanner == null )
402 {
403 return Collections.<String>emptySet();
404 }
405
406 Set<String> includes = new HashSet<String>( Arrays.asList( scanner.getIncludedDirectories() ) );
407 List<String> excludes = new ArrayList<String>( Arrays.asList( scanner.getExcludedDirectories() ) );
408 List<String> linksForDeletion = new ArrayList<String>();
409
410 if ( !fileSet.isFollowSymlinks() )
411 {
412 if ( verbose && messages != null )
413 {
414 messages.addInfoMessage( "Adding symbolic link dirs which were previously excluded"
415 + " to the list being deleted." ).flush();
416 }
417
418
419 scanner.setFollowSymlinks( true );
420 scanner.scan();
421
422 if ( messages != null && messages.isDebugEnabled() )
423 {
424 messages.addDebugMessage( "Originally marked for delete: " + includes ).flush();
425 messages.addDebugMessage( "Marked for preserve (with followSymlinks == false): " + excludes ).flush();
426 }
427
428 List<String> includedDirsAndSymlinks = Arrays.asList( scanner.getIncludedDirectories() );
429
430 linksForDeletion.addAll( excludes );
431 linksForDeletion.retainAll( includedDirsAndSymlinks );
432
433 if ( messages != null && messages.isDebugEnabled() )
434 {
435 messages.addDebugMessage( "Symlinks marked for deletion (originally mismarked): "
436 + linksForDeletion ).flush();
437 }
438
439 excludes.removeAll( includedDirsAndSymlinks );
440 }
441
442 excludeParentDirectoriesOfExcludedPaths( excludes, includes );
443
444 includes.addAll( linksForDeletion );
445
446 return includes;
447 }
448
449 private Set<String> findDeletableFiles( FileSet fileSet, Set<String> deletableDirectories )
450 {
451 if ( verbose && messages != null )
452 {
453 messages.addInfoMessage( "Re-scanning for deletable files." ).flush();
454 }
455
456 DirectoryScanner scanner = scan( fileSet );
457
458 if ( scanner == null )
459 {
460 return deletableDirectories;
461 }
462
463 Set<String> includes = deletableDirectories;
464 includes.addAll( Arrays.asList( scanner.getIncludedFiles() ) );
465 List<String> excludes = new ArrayList<String>( Arrays.asList( scanner.getExcludedFiles() ) );
466 List<String> linksForDeletion = new ArrayList<String>();
467
468 if ( !fileSet.isFollowSymlinks() )
469 {
470 if ( verbose && messages != null )
471 {
472 messages.addInfoMessage( "Adding symbolic link files which were previously excluded "
473 + "to the list being deleted." ).flush();
474 }
475
476
477 scanner.setFollowSymlinks( true );
478 scanner.scan();
479
480 if ( messages != null && messages.isDebugEnabled() )
481 {
482 messages.addDebugMessage( "Originally marked for delete: " + includes ).flush();
483 messages.addDebugMessage( "Marked for preserve (with followSymlinks == false): " + excludes ).flush();
484 }
485
486 List<String> includedFilesAndSymlinks = Arrays.asList( scanner.getIncludedFiles() );
487
488 linksForDeletion.addAll( excludes );
489 linksForDeletion.retainAll( includedFilesAndSymlinks );
490
491 if ( messages != null && messages.isDebugEnabled() )
492 {
493 messages.addDebugMessage( "Symlinks marked for deletion (originally mismarked): "
494 + linksForDeletion ).flush();
495 }
496
497 excludes.removeAll( includedFilesAndSymlinks );
498 }
499
500 excludeParentDirectoriesOfExcludedPaths( excludes, includes );
501
502 includes.addAll( linksForDeletion );
503
504 return includes;
505 }
506
507
508
509
510
511
512
513
514
515
516 private void excludeParentDirectoriesOfExcludedPaths( List<String> excludedPaths, Set<String> deletablePaths )
517 {
518 for ( Iterator<String> it = excludedPaths.iterator(); it.hasNext(); )
519 {
520 String path = it.next();
521
522 String parentPath = new File( path ).getParent();
523
524 while ( parentPath != null )
525 {
526 if ( messages != null && messages.isDebugEnabled() )
527 {
528 messages.addDebugMessage( "Verifying path " + parentPath
529 + " is not present; contains file which is excluded." ).flush();
530 }
531
532 boolean removed = deletablePaths.remove( parentPath );
533
534 if ( removed && messages != null && messages.isDebugEnabled() )
535 {
536 messages.addDebugMessage( "Path " + parentPath + " was removed from delete list." ).flush();
537 }
538
539 parentPath = new File( parentPath ).getParent();
540 }
541 }
542
543 if ( !excludedPaths.isEmpty() )
544 {
545 if ( messages != null && messages.isDebugEnabled() )
546 {
547 messages.addDebugMessage( "Verifying path " + "."
548 + " is not present; contains file which is excluded." ).flush();
549 }
550
551 boolean removed = deletablePaths.remove( "" );
552
553 if ( removed && messages != null && messages.isDebugEnabled() )
554 {
555 messages.addDebugMessage( "Path " + "." + " was removed from delete list." ).flush();
556 }
557 }
558 }
559
560
561
562
563
564
565
566
567
568
569 private void removeDir( File dir, boolean followSymlinks, boolean throwsError, List<String> warnMessages )
570 throws IOException
571 {
572 String[] list = dir.list();
573 if ( list == null )
574 {
575 list = new String[0];
576 }
577
578 for ( int i = 0; i < list.length; i++ )
579 {
580 String s = list[i];
581 File f = new File( dir, s );
582 if ( f.isDirectory() && ( followSymlinks || !isSymlink( f ) ) )
583 {
584 removeDir( f, followSymlinks, throwsError, warnMessages );
585 }
586 else
587 {
588 if ( !delete( f ) )
589 {
590 String message = "Unable to delete file " + f.getAbsolutePath();
591 if ( throwsError )
592 {
593 throw new IOException( message );
594 }
595
596 if ( !warnMessages.contains( message ) )
597 {
598 warnMessages.add( message );
599 }
600 }
601 }
602 }
603
604 if ( !delete( dir ) )
605 {
606 String message = "Unable to delete directory " + dir.getAbsolutePath();
607 if ( throwsError )
608 {
609 throw new IOException( message );
610 }
611
612 if ( !warnMessages.contains( message ) )
613 {
614 warnMessages.add( message );
615 }
616 }
617 }
618
619
620
621
622
623
624 private boolean delete( File f )
625 {
626 try
627 {
628 FileUtils.forceDelete( f );
629 }
630 catch ( IOException e )
631 {
632 return false;
633 }
634
635 return true;
636 }
637
638 private DirectoryScanner scan( FileSet fileSet )
639 {
640 File basedir = new File( fileSet.getDirectory() );
641 if ( !basedir.exists() || !basedir.isDirectory() )
642 {
643 return null;
644 }
645
646 DirectoryScanner scanner = new DirectoryScanner();
647
648 String[] includesArray = fileSet.getIncludesArray();
649 String[] excludesArray = fileSet.getExcludesArray();
650
651 if ( includesArray.length > 0 )
652 {
653 scanner.setIncludes( includesArray );
654 }
655
656 if ( excludesArray.length > 0 )
657 {
658 scanner.setExcludes( excludesArray );
659 }
660
661 if ( fileSet.isUseDefaultExcludes() )
662 {
663 scanner.addDefaultExcludes();
664 }
665
666 scanner.setBasedir( basedir );
667 scanner.setFollowSymlinks( fileSet.isFollowSymlinks() );
668
669 scanner.scan();
670
671 return scanner;
672 }
673
674 }