KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > methodhead > res > FileManager


1 /*
2  * Copyright (C) 2006 Methodhead Software LLC. All rights reserved.
3  *
4  * This file is part of TransferCM.
5  *
6  * TransferCM is free software; you can redistribute it and/or modify it under the
7  * terms of the GNU General Public License as published by the Free Software
8  * Foundation; either version 2 of the License, or (at your option) any later
9  * version.
10  *
11  * TransferCM is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * TransferCM; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
18  * Fifth Floor, Boston, MA 02110-1301 USA
19  */

20
21 package com.methodhead.res;
22
23 import java.io.File JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.Arrays JavaDoc;
27 import java.util.Comparator JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import org.apache.commons.lang.StringUtils;
32 import java.io.IOException JavaDoc;
33 import org.apache.commons.io.FileUtils;
34 import org.apache.commons.io.IOUtils;
35 import org.apache.commons.lang.exception.ExceptionUtils;
36 import java.io.InputStream JavaDoc;
37 import java.io.FileOutputStream JavaDoc;
38
39 /**
40  * Provides a logical file system and methods to manage it.
41  */

42 public class FileManager {
43
44   // constructors /////////////////////////////////////////////////////////////
45

46   // constants ////////////////////////////////////////////////////////////////
47

48   /**
49    * The destination path is blank, invalid (e.g., contains ".."), or
50    * non-existant.
51    */

52   public static final String JavaDoc VALIDATE_INVALIDDESTPATH = "res.invaliddestpath";
53
54   /**
55    * The destination path is a subdirectory of one of the files being moved or
56    * copied.
57    */

58   public static final String JavaDoc VALIDATE_SUBDIROFSELF = "res.subdirofself";
59
60   /**
61    * The move or copy would result in overwriting a directory or overwriting a
62    * file with a directory.
63    */

64   public static final String JavaDoc VALIDATE_CANTOVERWRITE = "res.cantoverwrite";
65
66   /**
67    * The destination file name is invalid.
68    */

69   public static final String JavaDoc VALIDATE_INVALIDDESTNAME = "res.invaliddestname";
70
71   // classes //////////////////////////////////////////////////////////////////
72

73   // methods //////////////////////////////////////////////////////////////////
74

75   /**
76    * Returns the directory specified by <tt>path</tt>, or <tt>null</tt> if no
77    * such directory (if a file exists, but is not a directory, <tt>null</tt> is
78    * still returned).
79    */

80   protected File JavaDoc getFileForPath(
81     String JavaDoc path )
82   throws
83     ResException {
84
85     //
86
// make sure path is valid
87
//
88
if ( !ResUtils.isValidPath( path ) )
89       throw new ResException( "Path is invalid \"" + path + "\"" );
90
91     //
92
// figure out the root directory
93
//
94
String JavaDoc[] parts = path.split( "/", 2 );
95
96     String JavaDoc rootPart = parts[ 0 ];
97     String JavaDoc pathPart = "";
98
99     if ( parts.length > 1 )
100       pathPart = parts[ 1 ];
101
102     Directory dir = ( Directory )directories_.get( rootPart );
103
104     if ( dir == null )
105       return null;
106
107     //
108
// create a file for the requested path and verify it
109
//
110
File JavaDoc file = new File JavaDoc( dir.getFile(), pathPart );
111
112     if ( !file.exists() || !file.isDirectory() )
113       return null;
114
115     return file;
116   }
117
118   /**
119    * Adds a directory to the file system. An exception is thrown if
120    * <tt>dir</tt> is not a valid directory, or if a directory named
121    * <tt>name</tt> has already been added.
122    */

123   public void addDirectory(
124     String JavaDoc name,
125     File JavaDoc dir )
126   throws
127     ResException {
128
129     if ( !dir.exists() || !dir.isDirectory() )
130       throw new ResException(
131         dir.getName() + " does not exist or is not a directory." );
132
133     if ( directories_.containsKey( name ) )
134       throw new ResException(
135         "\"" + name + "\" has already been added." );
136
137     Directory d = new Directory();
138     d.setName( name );
139     d.setFile( dir );
140
141     directories_.put( name, d );
142   }
143
144   /**
145    * Returns the directories being managed by the file manager, sorted by the
146    * directory name.
147    */

148   public Directory[] getDirectories() {
149     Directory[] directories = new Directory[ directories_.keySet().size() ];
150
151     //
152
// add directories to the array
153
//
154
int i = 0;
155     for ( Iterator JavaDoc iter = directories_.keySet().iterator(); iter.hasNext(); ) {
156       directories[ i++ ] = ( Directory )directories_.get( iter.next() );
157     }
158
159     //
160
// sort the directories
161
//
162
Arrays.sort( directories, new Comparator JavaDoc() {
163       public int compare( Object JavaDoc o1, Object JavaDoc o2 ) {
164         return
165           ( ( Directory )o1 ).getName().compareToIgnoreCase(
166             ( ( Directory )o2 ).getName() );
167       }
168     } );
169
170     return directories;
171   }
172
173   /**
174    * Returns the file specified by <tt>path</tt> and <tt>name</tt>, or
175    * <tt>null</tt> if no such file exists.
176    */

177   public File JavaDoc getFile(
178     String JavaDoc path,
179     String JavaDoc name )
180   throws
181     ResException {
182
183     //
184
// get the actual directory
185
//
186
File JavaDoc dir = getFileForPath( path );
187
188     if ( dir == null )
189       return null;
190
191     //
192
// create a file for the requested file and verify it
193
//
194
File JavaDoc file = new File JavaDoc( dir, name );
195
196     if ( !file.exists() )
197       return null;
198
199     return file;
200   }
201
202   /**
203    * Returns the files in the directory specified by <tt>path</tt>, sorted in
204    * alphabetical order, or <tt>null</tt> if no such path exists.
205    */

206   public File JavaDoc[] getFiles(
207     String JavaDoc path )
208   throws
209     ResException {
210
211     //
212
// get the actual directory
213
//
214
File JavaDoc dir = getFileForPath( path );
215
216     if ( dir == null )
217       return null;
218
219     //
220
// get the files
221
//
222
File JavaDoc[] files = dir.listFiles();
223
224     if ( files == null )
225       throw new ResException( "Could list files for path \"" + path + "\"." );
226
227     //
228
// sort the files
229
//
230
Arrays.sort( files, new Comparator JavaDoc() {
231       public int compare( Object JavaDoc o1, Object JavaDoc o2 ) {
232         return
233           ( ( File JavaDoc )o1 ).getName().compareToIgnoreCase(
234             ( ( File JavaDoc )o2 ).getName() );
235       }
236     } );
237
238     return files;
239   }
240
241   /**
242    * Returns <tt>true</tt> if <tt>destPath</tt> is a subdirectory of any files
243    * specified by <tt>srcPath</tt> and <tt>srcFiles</tt>.
244    */

245   protected boolean isDestSubdir(
246     String JavaDoc srcPath,
247     String JavaDoc[] srcFiles,
248     String JavaDoc destPath ) {
249
250     for ( int i = 0; i < srcFiles.length; i++ ) {
251       String JavaDoc filePath = srcPath + "/" + srcFiles[ i ];
252
253       if ( ResUtils.isPathDescendent(
254              ResUtils.cleanPath( filePath ), ResUtils.cleanPath( destPath ) ) )
255         return true;
256     }
257
258     return false;
259   }
260
261   /**
262    * Returns <tt>true</tt> if <tt>src</tt> can overwrite <tt>dest</tt>.
263    * Directories cannot be overwritten at all, and files can't be overwritten
264    * by directories.
265    */

266   protected boolean canOverwrite(
267     File JavaDoc src,
268     File JavaDoc dest ) {
269
270     return canOverwrite( dest, src.isDirectory() );
271
272 /*
273     if ( dest != null ) {
274       if ( dest.isDirectory() ) {
275         return false;
276       }
277       else {
278         if ( src.isDirectory() ) {
279           return false;
280         }
281       }
282     }
283
284     return true;
285 */

286   }
287
288   /**
289    * Returns <tt>true</tt> if <tt>dest</tt> can be overwritten by a file, where
290    * <tt>isDir</tt> specifies whether that file is a file or a directory. If
291    * <tt>dest</tt> is <tt>null</tt>, it is assumed the file doesn't exist, and
292    * <tt>true</tt> is returned. The same rules described in {@link
293    * #canOverwrite(java.io.File,java.io.File)} apply.
294    */

295   protected boolean canOverwrite(
296     File JavaDoc dest,
297     boolean isDir ) {
298
299     if ( dest != null ) {
300       if ( dest.isDirectory() ) {
301         return false;
302       }
303       else {
304         if ( isDir ) {
305           return false;
306         }
307       }
308     }
309
310     return true;
311   }
312
313   /**
314    * Validates moving <tt>srcFiles</tt> from <tt>srcPath</tt> to
315    * <tt>destPath</tt>. <tt>srcFiles</tt> is expected to contain a list of
316    * file names as <tt>String</tt>s. If <tt>srcFiles</tt> contains a single
317    * file name, <tt>destFile</tt> is validated as a destination file name.
318    * Returns <tt>null</tt> if the move is valid. A <tt>VALIDATE_</tt> (e.g.
319    * {@link #VALIDATE_INVALIDDESTPATH}) error code is returned if the move is
320    * invalid. Note that these error codes are designed to be used as a key in
321    * a resource bundle.
322    */

323   public String JavaDoc validateMove(
324     String JavaDoc srcPath,
325     String JavaDoc[] srcFiles,
326     String JavaDoc destPath,
327     String JavaDoc destFile ) {
328
329     //
330
// blank?
331
//
332
if ( StringUtils.isBlank( destPath ) )
333       return VALIDATE_INVALIDDESTPATH;
334
335     //
336
// valid?
337
//
338
if ( !ResUtils.isValidPath( destPath ) )
339       return VALIDATE_INVALIDDESTPATH;
340
341     //
342
// exists
343
//
344
if ( getFileForPath( destPath ) == null )
345       return VALIDATE_INVALIDDESTPATH;
346
347     //
348
// subdir of self?
349
//
350
if ( isDestSubdir( srcPath, srcFiles, destPath ) )
351       return VALIDATE_SUBDIROFSELF;
352
353     if ( srcFiles.length == 1 ) {
354
355       //
356
// blank name?
357
//
358
if ( StringUtils.isBlank( destFile ) )
359         return VALIDATE_INVALIDDESTNAME;
360
361       //
362
// valid name?
363
//
364
if ( !ResUtils.isValidFileName( destFile ) )
365         return VALIDATE_INVALIDDESTNAME;
366
367       //
368
// exists but can't overwrite?
369
//
370
if ( !canOverwrite(
371               getFile( srcPath, srcFiles[ 0 ] ),
372               getFile( destPath, destFile ) ) )
373         return VALIDATE_CANTOVERWRITE;
374     }
375     else {
376
377       //
378
// a file in the set exists but can't overwrite?
379
//
380
for ( int i = 0; i < srcFiles.length; i++ ) {
381         if ( !canOverwrite(
382                 getFile( srcPath, srcFiles[ i ] ),
383                 getFile( destPath, srcFiles[ i ] ) ) )
384           return VALIDATE_CANTOVERWRITE;
385       }
386     }
387
388     return null;
389   }
390
391   /**
392    * Validates whether the files specified by <tt>srcPath</tt> and
393    * <tt>srcFile</tt> can be created. A file may not be created in some
394    * circumstances: if the file exists and is a directory, it cannot be
395    * overwritten; if the file exists and is a normal file, it cannot be
396    * overwritten by a directory. Returns <tt>null</tt> if the create is valid.
397    * A <tt>VALIDATE_</tt> (e.g. {@link #VALIDATE_CANTOVERWRITE}) error code is
398    * returned if the create is invalid. Note that these error codes are
399    * designed to be used as a key in a resource bundle.
400    */

401   public String JavaDoc validateCreate(
402     String JavaDoc srcPath,
403     String JavaDoc srcFile,
404     boolean isDir ) {
405
406     File JavaDoc dest = getFile( srcPath, srcFile );
407
408     //
409
// does the file exist?
410
//
411
if ( ( dest != null ) && ( !canOverwrite( dest, isDir ) ) )
412       return VALIDATE_CANTOVERWRITE;
413
414     return null;
415   }
416
417   /**
418    * Returns the names of files in <tt>destPath</tt> that would be overwritten
419    * by files specified by <tt>srcPath</tt> and <tt>srcFiles</tt> in a move or
420    * copy operation. If <tt>srcFiles</tt> contains only one file name,
421    * <tt>destFile</tt> is considered the destination file name.
422    */

423   public String JavaDoc[] findOverwriteFiles(
424     String JavaDoc srcPath,
425     String JavaDoc[] srcFiles,
426     String JavaDoc destPath,
427     String JavaDoc destFile ) {
428
429     if ( srcFiles.length == 1 ) {
430       if ( getFile( destPath, destFile ) != null )
431         return new String JavaDoc[] { destFile };
432     }
433     else {
434       List JavaDoc l = new ArrayList JavaDoc();
435       for ( int i = 0; i < srcFiles.length; i++ ) {
436         if ( getFile( destPath, srcFiles[ i ] ) != null )
437           l.add( srcFiles[ i ] );
438       }
439
440       if ( l.size() > 0 ) {
441         String JavaDoc[] fileNames = new String JavaDoc[ l.size() ];
442         int i = 0;
443         for ( Iterator JavaDoc iter = l.iterator(); iter.hasNext(); )
444           fileNames[ i++ ] = ( String JavaDoc )iter.next();
445
446         return fileNames;
447       }
448     }
449
450     return null;
451   }
452
453   /**
454    * Copies <tt>from</tt> to <tt>to</tt>; if <tt>from</tt> is a directory, it
455    * is recursively copied. If <tt>to</tt> is exists and is not a directory,
456    * it is overwritten. If <tt>to</tt> is exists and is a directory, the
457    * contents of <tt>from</tt> are copied over the contents of <tt>to</tt>. If
458    * <tt>to</tt> exists, but is a directory when <tt>from</tt> is not (or vice
459    * versa), an exception is thrown.
460    */

461   protected static void copyFile(
462     File JavaDoc from,
463     File JavaDoc to )
464   throws
465     ResException {
466
467     try {
468       if ( from.isDirectory() ) {
469         if ( to.exists() ) {
470           if ( !to.isDirectory() )
471             throw new ResException(
472               "Can't copy directory over non-directory: " +
473               to.getAbsolutePath() );
474         }
475         else {
476           to.mkdir();
477         }
478
479         //
480
// recurse into directory
481
//
482
File JavaDoc[] files = from.listFiles();
483
484         for ( int i = 0; i < files.length; i++ )
485           copyFile( files[ i ], new File JavaDoc( to, files[ i ].getName() ) );
486       }
487       else {
488         if ( to.exists() && to.isDirectory() )
489           throw new ResException(
490             "Can't copy non-directory over directory: " +
491             to.getAbsolutePath() );
492
493         FileUtils.copyFile( from, to );
494       }
495     }
496     catch ( IOException JavaDoc e ) {
497       throw new ResException(
498         "Unexpected IOException copying \"" + from + "\" to \"" + to +
499         "\": " + ExceptionUtils.getStackTrace( e ) );
500     }
501   }
502
503   /**
504    * Moves the files specified by <tt>srcPath</tt> and <tt>srcFiles</tt> to
505    * <tt>destPath</tt>. If <tt>srcFiles</tt> contains exactly one file name,
506    * <tt>destFile</tt> is used as the destination file name.
507    */

508   public void move(
509     String JavaDoc srcPath,
510     String JavaDoc[] srcFiles,
511     String JavaDoc destPath,
512     String JavaDoc destFile ) {
513
514     if ( srcFiles.length == 1 ) {
515       File JavaDoc src = getFile( srcPath, srcFiles[ 0 ] );
516       File JavaDoc dest = new File JavaDoc( getFileForPath( destPath ), destFile );
517       src.renameTo( dest );
518     }
519     else {
520       for ( int i = 0; i < srcFiles.length; i++ ) {
521         File JavaDoc src = getFile( srcPath, srcFiles[ i ] );
522         File JavaDoc dest = new File JavaDoc( getFileForPath( destPath ), srcFiles[ i ] );
523         src.renameTo( dest );
524       }
525     }
526   }
527
528   /**
529    * Copies the files specified by <tt>srcPath</tt> and <tt>srcFiles</tt> to
530    * <tt>destPath</tt>. If <tt>srcFiles</tt> contains exactly one file name,
531    * <tt>destFile</tt> is used as the destination file name. Any directories
532    * are recursively copied.
533    */

534   public void copy(
535     String JavaDoc srcPath,
536     String JavaDoc[] srcFiles,
537     String JavaDoc destPath,
538     String JavaDoc destFile ) {
539
540     if ( srcFiles.length == 1 ) {
541       File JavaDoc src = getFile( srcPath, srcFiles[ 0 ] );
542       File JavaDoc dest = new File JavaDoc( getFileForPath( destPath ), destFile );
543       copyFile( src, dest );
544     }
545     else {
546       for ( int i = 0; i < srcFiles.length; i++ ) {
547         File JavaDoc src = getFile( srcPath, srcFiles[ i ] );
548         File JavaDoc dest = new File JavaDoc( getFileForPath( destPath ), srcFiles[ i ] );
549         copyFile( src, dest );
550       }
551     }
552   }
553
554   /**
555    * Deletes the files specified by <tt>srcPath</tt> and <tt>srcFiles</tt>. An
556    * exception is thrown if file(s) do not already exist.
557    */

558   public void delete(
559     String JavaDoc srcPath,
560     String JavaDoc[] srcFiles ) {
561
562     try {
563       //
564
// make sure all the files exist
565
//
566
for ( int i = 0; i < srcFiles.length; i++ ) {
567         if ( StringUtils.isBlank( srcFiles[ i ] ) )
568           throw new ResException( "Can't delete a file with a blank name." );
569
570         File JavaDoc f = getFile( srcPath, srcFiles[ i ] );
571         if ( f == null )
572           throw new ResException(
573             "\"" + srcPath + "/" + srcFiles[ i ] + "\" doesn't exist" );
574       }
575
576       //
577
// delete the files
578
//
579
for ( int i = 0; i < srcFiles.length; i++ ) {
580         File JavaDoc f = getFile( srcPath, srcFiles[ i ] );
581         if ( f.isDirectory() )
582           FileUtils.deleteDirectory( f );
583         else
584           f.delete();
585       }
586     }
587     catch ( IOException JavaDoc e ) {
588       throw new ResException(
589         "Unexpected exception: " + ExceptionUtils.getStackTrace( e ) );
590     }
591   }
592
593   /**
594    * Returns a file specified by <tt>path</tt> and <tt>name</tt>, such that the
595    * returned file can be used to create a new file. An exception is thrown if
596    * the file already exists.
597    */

598   public File JavaDoc getNewFile(
599     String JavaDoc path,
600     String JavaDoc name )
601   throws
602     ResException {
603
604     //
605
// Make sure the file doesn't already exist
606
//
607
if ( getFile( path, name ) != null )
608       throw new ResException( "File exists \"" + path + "/" + name + "\"" );
609
610     //
611
// verify the path
612
//
613
File JavaDoc dir = getFileForPath( path );
614
615     if ( dir == null )
616       throw new ResException( "Invalid path \"" + path + "\"" );
617
618     //
619
// create a file for the requested file and return it
620
//
621
return new File JavaDoc( dir, name );
622   }
623
624   /**
625    * Creates the file specified by <tt>srcPath</tt> and <tt>srcFile</tt>; a
626    * directory is created if <tt>isDir</tt> is <tt>true</tt>, otherwise an
627    * empty file is created. If the file exists, an a file is being created,
628    * the existing file is overwritten.
629    */

630   public void create(
631     String JavaDoc path,
632     String JavaDoc file,
633     boolean isDir ) {
634
635     try {
636       //
637
// verify the path
638
//
639
File JavaDoc dir = getFileForPath( path );
640
641       if ( dir == null )
642         throw new ResException( "Invalid path \"" + path + "\"" );
643
644       //
645
// create a file for the requested file and return it
646
//
647
File JavaDoc f = new File JavaDoc( dir, file );
648
649       if ( f.exists() ) {
650         if ( f.isDirectory() )
651           throw new ResException( "Can't overwrite directory." );
652         else {
653           f.delete();
654         }
655       }
656
657       if ( isDir )
658         f.mkdir();
659       else
660         f.createNewFile();
661     }
662     catch ( IOException JavaDoc e ) {
663       throw new ResException(
664         "Unexpected IOException: " + ExceptionUtils.getStackTrace( e ) );
665     }
666   }
667
668   /**
669    * Creates the file specified by <tt>srcPath</tt> and <tt>srcFile</tt>,
670    * writing the contents of <tt>in</tt> to the file. If the file exists, the
671    * existing file is overwritten.
672    */

673   public void create(
674     String JavaDoc path,
675     String JavaDoc file,
676     InputStream JavaDoc in ) {
677
678     try {
679       //
680
// verify the path
681
//
682
File JavaDoc dir = getFileForPath( path );
683
684       if ( dir == null )
685         throw new ResException( "Invalid path \"" + path + "\"" );
686
687       //
688
// create a file for the requested file and return it
689
//
690
File JavaDoc f = new File JavaDoc( dir, file );
691
692       if ( f.exists() ) {
693         if ( f.isDirectory() )
694           throw new ResException( "Can't overwrite directory." );
695         else {
696           f.delete();
697         }
698       }
699
700       FileOutputStream JavaDoc out = new FileOutputStream JavaDoc( f );
701       IOUtils.copy( in, out );
702       out.close();
703     }
704     catch ( IOException JavaDoc e ) {
705       throw new ResException(
706         "Unexpected IOException: " + ExceptionUtils.getStackTrace( e ) );
707     }
708   }
709
710   // properties ///////////////////////////////////////////////////////////////
711

712   // attributes ///////////////////////////////////////////////////////////////
713

714   protected Map JavaDoc directories_ = new HashMap JavaDoc();
715 }
716
Popular Tags