001         package com.croftsoft.core.io;
002    
003         import java.io.*;
004         import java.util.*;
005    
006         import com.croftsoft.core.lang.NullArgumentException;
007    
008         /*********************************************************************
009         * A library of static methods to manipulate File objects.
010         *
011         * <p />
012         * 
013         * @version
014         *   2001-06-10
015         * @since
016         *   1999-08-15
017         * @author
018         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
019         *********************************************************************/
020    
021         public final class  FileLib
022         //////////////////////////////////////////////////////////////////////
023         //////////////////////////////////////////////////////////////////////
024         {
025    
026         /*********************************************************************
027         * Test method.
028         *********************************************************************/
029         public static void  main ( String [ ]  args )
030         //////////////////////////////////////////////////////////////////////
031         {
032           File  file = findFile ( args [ 0 ], args [ 1 ], true );
033           System.out.println ( file );
034         }
035    
036         /*********************************************************************
037         * Returns null if the file does not already exist.
038         *********************************************************************/
039         public static File  findFile (
040           String  dirname, String  filename, boolean  ignoreFilenameCase )
041         //////////////////////////////////////////////////////////////////////
042         {
043           File  file = new File ( dirname, filename );
044           if ( file.exists ( ) ) return file;
045           if ( !ignoreFilenameCase ) return null;
046    
047           File  dir = null;
048    
049           if ( dirname != null )
050           {
051             dir = new File ( dirname );
052             if ( !dir.exists ( ) ) return null;
053             if ( !dir.isDirectory ( ) )
054             {
055               throw new IllegalArgumentException (
056               "\"" + dirname + "\" is not a directory." );
057             }
058           }
059    
060           if ( dir == null ) dir = new File ( "." );
061    
062           String [ ]  files = dir.list ( );
063           for ( int  i = 0; i < files.length; i++ )
064           {
065             if ( files [ i ].equalsIgnoreCase ( filename ) )
066             {
067               return new File ( dir, files [ i ] );
068             }
069           }
070    
071           return null;
072         }
073    
074         /*********************************************************************
075         * Parses out the file name extension.
076         * <P>
077         * @return
078         *   Returns null if file name does not have a period;<BR>
079         *   returns the empty String ("") if the file name has only one period
080         *     and it is the last character;<BR>
081         *   otherwise returns everything after the first period.
082         *********************************************************************/
083         public static String  getExtension ( File  file )
084         //////////////////////////////////////////////////////////////////////
085         {
086           String  name = file.getName ( );
087           int  i = name.indexOf ( '.' );
088           if ( i < 0 ) return null;
089           return name.substring ( i + 1, name.length ( ) - i - 1 );
090         }
091    
092         /*********************************************************************
093         * Creates the parent directories for a file specified by a combined
094         *   path and filename String.
095         * The name after the last separator in pathFilename is not created
096         *   as a directory and is typically a filename.
097         * Used when the normal File.mkdirs() operation would mistakenly create
098         *   directories for the parent directories plus a directory with the
099         *   same name as the filename instead of just directories for the
100         *   parent directories when given the combined path and filename.
101         * The pathFilename may be just a filename without a path or a
102         *   a file in the root directory; in these cases, no directory will
103         *   be created.
104         *********************************************************************/
105         public static boolean  makeParents ( String  pathFilename )
106         //////////////////////////////////////////////////////////////////////
107         {
108           File  file = new File ( pathFilename );
109    
110           String  parent = file.getParent ( );
111    
112           if ( parent == null )
113           {
114             return false;
115           }
116    
117           File  dir = new File ( parent );
118    
119           return dir.mkdirs ( );
120         }
121    
122         /*********************************************************************
123         * Replaces instances of oldString with newString in the inFile.
124         * <P>
125         * Creates a temporary file (inFile + ".tmp") in the process.
126         *
127         * @return
128         *   Returns true if the file was updated.
129         *********************************************************************/
130         public static boolean  replaceStrings (
131           File  file, String  oldString, String  newString )
132           throws FileNotFoundException, IOException
133         //////////////////////////////////////////////////////////////////////
134         {
135           byte [ ]  oldBytes = oldString.getBytes ( );
136           byte [ ]  newBytes = newString.getBytes ( );
137           return replaceBytes ( file, oldBytes, newBytes );
138         }
139    
140         /*********************************************************************
141         * Replaces instances of oldBytes with newBytes in the inFile.
142         * <P>
143         * Creates a temporary file (inFile + ".tmp") in the process.
144         *
145         * @return
146         *   Returns true if the file was updated.
147         *********************************************************************/
148         public static boolean  replaceBytes (
149           File  file, byte [ ]  oldBytes, byte [ ]  newBytes )
150           throws FileNotFoundException, IOException
151         //////////////////////////////////////////////////////////////////////
152         {
153           String  fileName = file.getCanonicalPath ( );
154           String  tmpFileName = fileName + ".tmp";
155           File  tmpFile = new File ( tmpFileName );
156           if ( tmpFile.exists ( ) )
157           {
158             throw new IOException ( "Temporary file \"" + tmpFileName
159               + "\" already exists." );
160           }
161           try
162           {
163             boolean  hasChanged
164               = replaceBytes ( file, tmpFile, oldBytes, newBytes );
165             if ( !hasChanged ) return false;
166             if ( !file.delete ( ) )
167             {
168               throw new IOException ( "Unable to delete to replace \""
169                 + fileName + "\"." );
170             }
171             if ( !tmpFile.renameTo ( new File ( fileName ) ) )
172             {
173               throw new IOException ( "Original file deleted but unable"
174                 + " to rename the updated file to the original name (\""
175                 + fileName + "\", \"" + tmpFileName + "\")." );
176             }
177           }
178           finally
179           {
180             tmpFile.delete ( );
181           }
182           return true;
183         }
184    
185         /*********************************************************************
186         * Copies inFile to outFile with the replacement of occurrences of
187         * oldString with newString.
188         * <P>
189         * Any pre-existing outFile is overwritten.
190         *
191         * @return
192         *   Returns true if outFile differs from inFile.
193         *********************************************************************/
194         public static boolean  replaceStrings (
195           File  inFile, File  outFile, String  oldString, String  newString )
196           throws FileNotFoundException, IOException
197         //////////////////////////////////////////////////////////////////////
198         {
199           byte [ ]  oldBytes = oldString.getBytes ( );
200           byte [ ]  newBytes = newString.getBytes ( );
201           return replaceBytes ( inFile, outFile, oldBytes, newBytes );
202         }
203    
204         /*********************************************************************
205         * Copies inFile to outFile with the replacement of occurrences of
206         * oldBytes with newBytes.
207         * <P>
208         * Any pre-existing outFile is overwritten.
209         *
210         * @return
211         *   Returns true if outFile differs from inFile.
212         *********************************************************************/
213         public static boolean  replaceBytes (
214           File      inFile,
215           File      outFile,
216           byte [ ]  oldBytes,
217           byte [ ]  newBytes )
218           throws FileNotFoundException, IOException
219         //////////////////////////////////////////////////////////////////////
220         {
221           if ( ( inFile   == null )
222             || ( outFile  == null )
223             || ( oldBytes == null )
224             || ( newBytes == null ) )
225           {
226             throw new IllegalArgumentException ( "null argument" );
227           }
228    
229           if ( !inFile.isFile ( ) )
230           {
231             throw new IllegalArgumentException (
232               "\"" + inFile.getCanonicalPath ( ) + "\" is not a file." );
233           }
234    /*
235           if ( !outFile.isFile ( ) )
236           {
237             throw new IllegalArgumentException (
238               "\"" + outFile.getCanonicalPath ( ) + "\" is not a file." );
239           }
240    */
241    
242           BufferedInputStream  in = new BufferedInputStream (
243             new FileInputStream ( inFile ) );
244           BufferedOutputStream  out = new BufferedOutputStream (
245             new FileOutputStream ( outFile ) );
246    
247           boolean  hasChanged
248             = StreamLib.replaceBytes ( in, out, oldBytes, newBytes );
249    
250           out.close ( );
251           in.close ( );
252    
253           return hasChanged;
254         }
255    
256         /*********************************************************************
257         * Loads the text file content into a String.
258         *********************************************************************/
259         public static String  loadTextFile ( String  filename )
260           throws IOException
261         //////////////////////////////////////////////////////////////////////
262         {
263           if ( filename == null )
264           {
265             throw new IllegalArgumentException ( "null filename" );
266           }
267    
268           FileReader   in  = null;
269    
270           StringWriter out = null;
271    
272           try
273           {
274             in  = new FileReader ( filename );
275    
276             out = new StringWriter ( );
277    
278             int  i;
279    
280             while ( ( i = in.read ( ) ) > -1 )
281             {
282               out.write ( ( byte ) i );
283             }
284    
285             out.flush ( );
286    
287             return out.toString ( );
288           }
289           finally
290           {
291             try { in.close  ( ); } catch ( Exception  ex ) { }
292    
293             try { out.close ( ); } catch ( Exception  ex ) { }
294           }
295         }
296    
297         /*********************************************************************
298         * Strips off the file name extension.
299         *********************************************************************/
300         public static String  pareExtension ( String  fileName )
301         //////////////////////////////////////////////////////////////////////
302         {
303           if ( fileName == null )
304           {
305             throw new IllegalArgumentException ( "null" );
306           }
307    
308           int  index = fileName.lastIndexOf ( '.' );
309    
310           if ( index < 0 )
311           {
312             return fileName;
313           }
314    
315           return fileName.substring ( 0, index );
316         }
317    
318         /*********************************************************************
319         * Saves text to a file.
320         *********************************************************************/
321         public static void  saveTextFile (
322           String  filename,
323           String  text )
324           throws IOException
325         //////////////////////////////////////////////////////////////////////
326         {
327           NullArgumentException.check ( filename );
328    
329           NullArgumentException.check ( text     );
330    
331           FileWriter  fileWriter = null;
332    
333           try
334           {
335             fileWriter = new FileWriter ( filename );
336    
337             fileWriter.write ( text );
338           }
339           finally
340           {
341             if ( fileWriter != null )
342             {
343               fileWriter.close ( );
344             }
345           }
346         }
347    
348         //////////////////////////////////////////////////////////////////////
349         //////////////////////////////////////////////////////////////////////
350    
351         private  FileLib ( ) { }
352    
353         //////////////////////////////////////////////////////////////////////
354         //////////////////////////////////////////////////////////////////////
355         }