View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  
21  package org.apache.directory.api.util;
22  
23  
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.FileNotFoundException;
27  import java.io.FileOutputStream;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.OutputStream;
31  import java.nio.channels.FileChannel;
32  import java.nio.charset.Charset;
33  import java.nio.file.Files;
34  import java.nio.file.Paths;
35  import java.nio.file.StandardOpenOption;
36  import java.util.List;
37  
38  
39  /**
40   * This code comes from Apache commons.io library.
41   * 
42   * Origin of code: Excalibur, Alexandria, Tomcat, Commons-Utils.
43   *
44   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
45   */
46  public final class FileUtils
47  {
48      /**
49       * The Windows separator character.
50       */
51      private static final char WINDOWS_SEPARATOR = '\\';
52  
53      /**
54       * The system separator character.
55       */
56      private static final char SYSTEM_SEPARATOR = File.separatorChar;
57  
58      /**
59       * The number of bytes in a kilobyte.
60       */
61      public static final long ONE_KB = 1024;
62  
63      /**
64       * The number of bytes in a megabyte.
65       */
66      public static final long ONE_MB = ONE_KB * ONE_KB;
67  
68      /**
69       * The file copy buffer size (30 MB)
70       */
71      private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30;
72  
73  
74      /**
75       * Creates a new instance of FileUtils.
76       */
77      private FileUtils()
78      {
79          // Nothing to do.
80      }
81  
82  
83      /**
84       * Deletes a directory recursively.
85       *
86       * @param directory  directory to delete
87       * @throws IOException in case deletion is unsuccessful
88       */
89      public static void deleteDirectory( File directory ) throws IOException
90      {
91          if ( !directory.exists() )
92          {
93              return;
94          }
95  
96          if ( !isSymlink( directory ) )
97          {
98              cleanDirectory( directory );
99          }
100 
101         if ( !directory.delete() )
102         {
103             String message = "Unable to delete directory " + directory + ".";
104             throw new IOException( message );
105         }
106     }
107 
108 
109     /**
110      * Determines whether the specified file is a Symbolic Link rather than an actual file.
111      * <p>
112      * Will not return true if there is a Symbolic Link anywhere in the path,
113      * only if the specific file is.
114      * <p>
115      * <b>Note:</b> the current implementation always returns {@code false} if the system
116      * is detected as Windows.
117      * <p>
118      * For code that runs on Java 1.7 or later, use the following method instead:
119      * <br>
120      * {@code boolean java.nio.file.Files.isSymbolicLink(Path path)}
121      * @param file the file to check
122      * @return true if the file is a Symbolic Link
123      * @throws IOException if an IO error occurs while checking the file
124      * @since 2.0
125      */
126     public static boolean isSymlink( File file ) throws IOException
127     {
128         if ( file == null )
129         {
130             throw new NullPointerException( "File must not be null" );
131         }
132 
133         if ( SYSTEM_SEPARATOR == WINDOWS_SEPARATOR )
134         {
135             return false;
136         }
137 
138         File fileInCanonicalDir = null;
139 
140         if ( file.getParent() == null )
141         {
142             fileInCanonicalDir = file;
143         }
144         else
145         {
146             File canonicalDir = file.getParentFile().getCanonicalFile();
147             fileInCanonicalDir = new File( canonicalDir, file.getName() );
148         }
149 
150         return !fileInCanonicalDir.getCanonicalFile().equals( fileInCanonicalDir.getAbsoluteFile() );
151     }
152 
153 
154     /**
155      * Deletes a directory recursively.
156      *
157      * @param directory  directory to delete
158      * @throws IOException in case deletion is unsuccessful
159      */
160     public static void cleanDirectory( File directory ) throws IOException
161     {
162         if ( !directory.exists() )
163         {
164             String message = directory + " does not exist";
165             throw new IllegalArgumentException( message );
166         }
167 
168         if ( !directory.isDirectory() )
169         {
170             String message = directory + " is not a directory";
171             throw new IllegalArgumentException( message );
172         }
173 
174         File[] files = directory.listFiles();
175 
176         if ( files == null )
177         {
178             // null if security restricted
179             String message = "Failed to list contents of " + directory;
180             throw new IOException( message );
181         }
182 
183         IOException exception = null;
184 
185         for ( File file : files )
186         {
187             try
188             {
189                 forceDelete( file );
190             }
191             catch ( IOException ioe )
192             {
193                 exception = ioe;
194             }
195         }
196 
197         if ( null != exception )
198         {
199             throw exception;
200         }
201     }
202 
203 
204     /**
205      * Deletes a file. If file is a directory, delete it and all sub-directories.
206      * <p>
207      * The difference between File.delete() and this method are:
208      * <ul>
209      * <li>A directory to be deleted does not have to be empty.</li>
210      * <li>You get exceptions when a file or directory cannot be deleted.
211      *      (java.io.File methods returns a boolean)</li>
212      * </ul>
213      *
214      * @param file  file or directory to delete, must not be {@code null}
215      * @throws NullPointerException if the directory is {@code null}
216      * @throws FileNotFoundException if the file was not found
217      * @throws IOException in case deletion is unsuccessful
218      */
219     public static void forceDelete( File file ) throws IOException
220     {
221         if ( file.isDirectory() )
222         {
223             deleteDirectory( file );
224         }
225         else
226         {
227             boolean filePresent = file.exists();
228 
229             if ( !file.delete() )
230             {
231                 if ( !filePresent )
232                 {
233                     String message = "File does not exist: " + file;
234                     throw new FileNotFoundException( message );
235                 }
236 
237                 String message = "Unable to delete file: " + file;
238                 throw new IOException( message );
239             }
240         }
241     }
242 
243 
244     /**
245      * Returns the path to the system temporary directory.
246      *
247      * @return the path to the system temporary directory.
248      *
249      * @since 2.0
250      */
251     public static String getTempDirectoryPath()
252     {
253         return System.getProperty( "java.io.tmpdir" );
254     }
255 
256 
257     /**
258      * Reads the contents of a file into a String using the default encoding for the VM.
259      * The file is always closed.
260      *
261      * @param file  the file to read, must not be {@code null}
262      * @return the file contents, never {@code null}
263      * @throws IOException in case of an I/O error
264      * @since 1.3.1
265      * @deprecated 2.5 use {@link #readFileToString(File, Charset)} instead
266      */
267     @Deprecated
268     public static String readFileToString( File file ) throws IOException
269     {
270         return readFileToString( file, Charset.defaultCharset() );
271     }
272 
273 
274     /**
275      * Reads the contents of a file into a String.
276      * The file is always closed.
277      *
278      * @param file  the file to read, must not be {@code null}
279      * @param encoding  the encoding to use, {@code null} means platform default
280      * @return the file contents, never {@code null}
281      * @throws IOException in case of an I/O error
282      * @since 2.3
283      */
284     public static String readFileToString( File file, Charset encoding ) throws IOException
285     {
286         InputStream in = null;
287 
288         try
289         {
290             in = openInputStream( file );
291             return IOUtils.toString( in, IOUtils.toCharset( encoding ) );
292         }
293         finally
294         {
295             IOUtils.closeQuietly( in );
296         }
297     }
298 
299 
300     /**
301      * Reads the contents of a file into a String. The file is always closed.
302      *
303      * @param file the file to read, must not be {@code null}
304      * @param encoding the encoding to use, {@code null} means platform default
305      * @return the file contents, never {@code null}
306      * @throws IOException in case of an I/O error
307      * @since 2.3
308      */
309     public static String readFileToString( File file, String encoding ) throws IOException
310     {
311         InputStream in = null;
312 
313         try
314         {
315             in = openInputStream( file );
316             return IOUtils.toString( in, IOUtils.toCharset( encoding ) );
317         }
318         finally
319         {
320             IOUtils.closeQuietly( in );
321         }
322     }
323 
324 
325     /**
326      * Opens a {@link FileInputStream} for the specified file, providing better
327      * error messages than simply calling <code>new FileInputStream(file)</code>.
328      * <p>
329      * At the end of the method either the stream will be successfully opened,
330      * or an exception will have been thrown.
331      * <p>
332      * An exception is thrown if the file does not exist.
333      * An exception is thrown if the file object exists but is a directory.
334      * An exception is thrown if the file exists but cannot be read.
335      *
336      * @param file  the file to open for input, must not be {@code null}
337      * @return a new {@link FileInputStream} for the specified file
338      * @throws FileNotFoundException if the file does not exist
339      * @throws IOException if the file object is a directory
340      * @throws IOException if the file cannot be read
341      * @since 1.3
342      */
343     public static InputStream openInputStream( File file ) throws IOException
344     {
345         if ( file.exists() )
346         {
347             if ( file.isDirectory() )
348             {
349                 throw new IOException( "File '" + file + "' exists but is a directory" );
350             }
351 
352             if ( !file.canRead() )
353             {
354                 throw new IOException( "File '" + file + "' cannot be read" );
355             }
356         }
357         else
358         {
359             throw new FileNotFoundException( "File '" + file + "' does not exist" );
360         }
361 
362         return Files.newInputStream( Paths.get( file.getPath() ) );
363     }
364 
365 
366     /**
367      * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
368      *
369      * @param file  the file to write
370      * @param data  the content to write to the file
371      * @throws IOException in case of an I/O error
372      * @deprecated 2.5 use {@link #writeStringToFile(File, String, Charset, boolean)} instead
373      */
374     @Deprecated
375     public static void writeStringToFile( File file, String data ) throws IOException
376     {
377         writeStringToFile( file, data, Charset.defaultCharset(), false );
378     }
379 
380 
381     /**
382      * Writes a String to a file creating the file if it does not exist.
383      *
384      * NOTE: As from v1.3, the parent directories of the file will be created
385      * if they do not exist.
386      *
387      * @param file  the file to write
388      * @param data  the content to write to the file
389      * @param encoding  the encoding to use, {@code null} means platform default
390      * @throws IOException in case of an I/O error
391      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
392      */
393     public static void writeStringToFile( File file, String data, String encoding ) throws IOException
394     {
395         writeStringToFile( file, data, IOUtils.toCharset( encoding ), false );
396     }
397 
398 
399     /**
400      * Writes a String to a file creating the file if it does not exist.
401      *
402      * @param file  the file to write
403      * @param data  the content to write to the file
404      * @param encoding  the encoding to use, {@code null} means platform default
405      * @param append if {@code true}, then the String will be added to the
406      * end of the file rather than overwriting
407      * @throws IOException in case of an I/O error
408      * @since 2.3
409      */
410     public static void writeStringToFile( File file, String data, Charset encoding, boolean append ) throws IOException
411     {
412         OutputStream out = null;
413 
414         try
415         {
416             out = openOutputStream( file, append );
417             IOUtils.write( data, out, encoding );
418             out.close(); // don't swallow close Exception if copy completes normally
419         }
420         finally
421         {
422             IOUtils.closeQuietly( out );
423         }
424     }
425 
426 
427     /**
428      * Opens a {@link FileOutputStream} for the specified file, checking and
429      * creating the parent directory if it does not exist.
430      * <p>
431      * At the end of the method either the stream will be successfully opened,
432      * or an exception will have been thrown.
433      * <p>
434      * The parent directory will be created if it does not exist.
435      * The file will be created if it does not exist.
436      * An exception is thrown if the file object exists but is a directory.
437      * An exception is thrown if the file exists but cannot be written to.
438      * An exception is thrown if the parent directory cannot be created.
439      *
440      * @param file  the file to open for output, must not be {@code null}
441      * @param append if {@code true}, then bytes will be added to the
442      * end of the file rather than overwriting
443      * @return a new {@link FileOutputStream} for the specified file
444      * @throws IOException if the file object is a directory
445      * @throws IOException if the file cannot be written to
446      * @throws IOException if a parent directory needs creating but that fails
447      * @since 2.1
448      */
449     public static OutputStream openOutputStream( File file, boolean append ) throws IOException
450     {
451         if ( file.exists() )
452         {
453             if ( file.isDirectory() )
454             {
455                 throw new IOException( "File '" + file + "' exists but is a directory" );
456             }
457 
458             if ( !file.canWrite() )
459             {
460                 throw new IOException( "File '" + file + "' cannot be written to" );
461             }
462         }
463         else
464         {
465             File parent = file.getParentFile();
466 
467             if ( parent != null )
468             {
469                 if ( !parent.mkdirs() && !parent.isDirectory() )
470                 {
471                     throw new IOException( "Directory '" + parent + "' could not be created" );
472                 }
473             }
474         }
475 
476         if ( append )
477         {
478             return Files.newOutputStream( Paths.get( file.getPath() ), StandardOpenOption.CREATE, StandardOpenOption.APPEND );
479         }
480         else
481         {
482             return Files.newOutputStream( Paths.get( file.getPath() ) );
483         }
484     }
485 
486 
487     /**
488      * Returns a {@link File} representing the system temporary directory.
489      *
490      * @return the system temporary directory.
491      *
492      * @since 2.0
493      */
494     public static File getTempDirectory()
495     {
496         return new File( getTempDirectoryPath() );
497     }
498 
499 
500     /**
501      * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
502      * <p>
503      * The difference between File.delete() and this method are:
504      * <ul>
505      * <li>A directory to be deleted does not have to be empty.</li>
506      * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
507      * </ul>
508      *
509      * @param file  file or directory to delete, can be {@code null}
510      * @return {@code true} if the file or directory was deleted, otherwise
511      * {@code false}
512      *
513      * @since 1.4
514      */
515     public static boolean deleteQuietly( File file )
516     {
517         if ( file == null )
518         {
519             return false;
520         }
521 
522         try
523         {
524             if ( file.isDirectory() )
525             {
526                 cleanDirectory( file );
527             }
528         }
529         catch ( Exception ignored )
530         {
531         }
532 
533         try
534         {
535             return file.delete();
536         }
537         catch ( Exception ignored )
538         {
539             return false;
540         }
541     }
542 
543 
544     /**
545      * Copies a file to a new location preserving the file date.
546      * <p>
547      * This method copies the contents of the specified source file to the
548      * specified destination file. The directory holding the destination file is
549      * created if it does not exist. If the destination file exists, then this
550      * method will overwrite it.
551      * <p>
552      * <strong>Note:</strong> This method tries to preserve the file's last
553      * modified date/times using {@link File#setLastModified(long)}, however
554      * it is not guaranteed that the operation will succeed.
555      * If the modification operation fails, no indication is provided.
556      *
557      * @param srcFile  an existing file to copy, must not be {@code null}
558      * @param destFile  the new file, must not be {@code null}
559      *
560      * @throws NullPointerException if source or destination is {@code null}
561      * @throws IOException if source or destination is invalid
562      * @throws IOException if an IO error occurs during copying
563      * @throws IOException if the output file length is not the same as the input file length after the copy completes
564      * @see #copyFile(File, File, boolean)
565      */
566     public static void copyFile( File srcFile, File destFile ) throws IOException
567     {
568         copyFile( srcFile, destFile, true );
569     }
570 
571 
572     /**
573      * Copies a file to a new location.
574      * <p>
575      * This method copies the contents of the specified source file
576      * to the specified destination file.
577      * The directory holding the destination file is created if it does not exist.
578      * If the destination file exists, then this method will overwrite it.
579      * <p>
580      * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
581      * {@code true} tries to preserve the file's last modified
582      * date/times using {@link File#setLastModified(long)}, however it is
583      * not guaranteed that the operation will succeed.
584      * If the modification operation fails, no indication is provided.
585      *
586      * @param srcFile  an existing file to copy, must not be {@code null}
587      * @param destFile  the new file, must not be {@code null}
588      * @param preserveFileDate  true if the file date of the copy
589      *  should be the same as the original
590      *
591      * @throws NullPointerException if source or destination is {@code null}
592      * @throws IOException if source or destination is invalid
593      * @throws IOException if an IO error occurs during copying
594      * @throws IOException if the output file length is not the same as the input file length after the copy completes
595      */
596     public static void copyFile( File srcFile, File destFile, boolean preserveFileDate ) throws IOException
597     {
598         if ( srcFile == null )
599         {
600             throw new NullPointerException( "Source must not be null" );
601         }
602 
603         if ( destFile == null )
604         {
605             throw new NullPointerException( "Destination must not be null" );
606         }
607 
608         if ( !srcFile.exists() )
609         {
610             throw new FileNotFoundException( "Source '" + srcFile + "' does not exist" );
611         }
612 
613         if ( srcFile.isDirectory() )
614         {
615             throw new IOException( "Source '" + srcFile + "' exists but is a directory" );
616         }
617 
618         if ( srcFile.getCanonicalPath().equals( destFile.getCanonicalPath() ) )
619         {
620             throw new IOException( "Source '" + srcFile + "' and destination '" + destFile + "' are the same" );
621         }
622 
623         File parentFile = destFile.getParentFile();
624 
625         if ( parentFile != null )
626         {
627             if ( !parentFile.mkdirs() && !parentFile.isDirectory() )
628             {
629                 throw new IOException( "Destination '" + parentFile + "' directory cannot be created" );
630             }
631         }
632 
633         if ( destFile.exists() && !destFile.canWrite() )
634         {
635             throw new IOException( "Destination '" + destFile + "' exists but is read-only" );
636         }
637 
638         doCopyFile( srcFile, destFile, preserveFileDate );
639     }
640 
641 
642     /**
643      * Internal copy file method.
644      * This caches the original file length, and throws an IOException 
645      * if the output file length is different from the current input file length.
646      * So it may fail if the file changes size.
647      * It may also fail with "IllegalArgumentException: Negative size" if the input file is truncated part way
648      * through copying the data and the new file size is less than the current position.
649      *
650      * @param srcFile  the validated source file, must not be {@code null}
651      * @param destFile  the validated destination file, must not be {@code null}
652      * @param preserveFileDate  whether to preserve the file date
653      * @throws IOException if an error occurs
654      * @throws IOException if the output file length is not the same as the input file length after the copy completes
655      * @throws IllegalArgumentException "Negative size" if the file is truncated so that the size is less than the position
656      */
657     private static void doCopyFile( File srcFile, File destFile, boolean preserveFileDate ) throws IOException
658     {
659         if ( destFile.exists() && destFile.isDirectory() )
660         {
661             throw new IOException( "Destination '" + destFile + "' exists but is a directory" );
662         }
663 
664         FileInputStream fis = null;
665         FileOutputStream fos = null;
666         FileChannel input = null;
667         FileChannel output = null;
668 
669         try
670         {
671             fis = ( FileInputStream ) Files.newInputStream( Paths.get( srcFile.getPath() ) );
672             fos = ( FileOutputStream ) Files.newOutputStream( Paths.get( destFile.getPath() ) );
673             input = fis.getChannel();
674             output = fos.getChannel();
675             long size = input.size(); // TODO See IO-386
676             long pos = 0;
677             long count = 0;
678 
679             while ( pos < size )
680             {
681                 long remain = size - pos;
682                 count = remain > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : remain;
683                 long bytesCopied = output.transferFrom( input, pos, count );
684 
685                 if ( bytesCopied == 0 )
686                 { // IO-385 - can happen if file is truncated after caching the size
687                     break; // ensure we don't loop forever
688                 }
689 
690                 pos += bytesCopied;
691             }
692         }
693         finally
694         {
695             IOUtils.closeQuietly( output, fos, input, fis );
696         }
697 
698         long srcLen = srcFile.length(); // TODO See IO-386
699         long dstLen = destFile.length(); // TODO See IO-386
700 
701         if ( srcLen != dstLen )
702         {
703             throw new IOException( "Failed to copy full contents from '"
704                 + srcFile + "' to '" + destFile + "' Expected length: " + srcLen + " Actual: " + dstLen );
705         }
706 
707         if ( preserveFileDate )
708         {
709             destFile.setLastModified( srcFile.lastModified() );
710         }
711     }
712 
713 
714     /**
715      * Writes a byte array to a file creating the file if it does not exist.
716      * <p>
717      * NOTE: As from v1.3, the parent directories of the file will be created
718      * if they do not exist.
719      *
720      * @param file  the file to write to
721      * @param data  the content to write to the file
722      * @throws IOException in case of an I/O erroe
723      * @since 1.1
724      */
725     public static void writeByteArrayToFile( final File file, final byte[] data ) throws IOException
726     {
727         writeByteArrayToFile( file, data, false );
728     }
729 
730 
731     /**
732      * Writes a byte array to a file creating the file if it does not exist.
733      *
734      * @param file  the file to write to
735      * @param data  the content to write to the file
736      * @param append if {@code true}, then bytes will be added to the
737      * end of the file rather than overwriting
738      * @throws IOException in case of an I/O error
739      * @since 2.1
740      */
741     public static void writeByteArrayToFile( File file, byte[] data, boolean append ) throws IOException
742     {
743         writeByteArrayToFile( file, data, 0, data.length, append );
744     }
745 
746 
747     /**
748      * Writes {@code len} bytes from the specified byte array starting
749      * at offset {@code off} to a file, creating the file if it does
750      * not exist.
751      *
752      * @param file  the file to write to
753      * @param data  the content to write to the file
754      * @param off   the start offset in the data
755      * @param len   the number of bytes to write
756      * @param append if {@code true}, then bytes will be added to the
757      * end of the file rather than overwriting
758      * @throws IOException in case of an I/O error
759      * @since 2.5
760      */
761     public static void writeByteArrayToFile( File file, byte[] data, int off, int len, boolean append ) throws IOException
762     {
763         OutputStream out = null;
764         
765         try
766         {
767             out = openOutputStream( file, append );
768             out.write( data, off, len );
769             out.close(); // don't swallow close Exception if copy completes normally
770         }
771         finally
772         {
773             IOUtils.closeQuietly( out );
774         }
775     }
776 
777     
778     /**
779      * Reads the contents of a file into a byte array.
780      * The file is always closed.
781      *
782      * @param file  the file to read, must not be {@code null}
783      * @return the file contents, never {@code null}
784      * @throws IOException in case of an I/O error
785      * @since 1.1
786      */
787     public static byte[] readFileToByteArray( File file ) throws IOException 
788     {
789         InputStream in = null;
790         
791         try 
792         {
793             in = openInputStream( file );
794             return IOUtils.toByteArray( in, file.length() );
795         } 
796         finally 
797         {
798             IOUtils.closeQuietly( in );
799         }
800     }
801 
802     
803     /**
804      * Opens a {@link FileOutputStream} for the specified file, checking and
805      * creating the parent directory if it does not exist.
806      * <p>
807      * At the end of the method either the stream will be successfully opened,
808      * or an exception will have been thrown.
809      * <p>
810      * The parent directory will be created if it does not exist.
811      * The file will be created if it does not exist.
812      * An exception is thrown if the file object exists but is a directory.
813      * An exception is thrown if the file exists but cannot be written to.
814      * An exception is thrown if the parent directory cannot be created.
815      *
816      * @param file  the file to open for output, must not be {@code null}
817      * @return a new {@link FileOutputStream} for the specified file
818      * @throws IOException if the file object is a directory
819      * @throws IOException if the file cannot be written to
820      * @throws IOException if a parent directory needs creating but that fails
821      * @since 1.3
822      */
823     public static OutputStream openOutputStream( File file ) throws IOException 
824     {
825         return openOutputStream( file, false );
826     }
827     
828     
829     /**
830      * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM.
831      * The file is always closed.
832      *
833      * @param file  the file to read, must not be {@code null}
834      * @return the list of Strings representing each line in the file, never {@code null}
835      * @throws IOException in case of an I/O error
836      * @since 1.3
837      * @deprecated 2.5 use {@link #readLines(File, Charset)} instead
838      */
839     @Deprecated
840     public static List<String> readLines( File file ) throws IOException 
841     {
842         return readLines( file, Charset.defaultCharset() );
843     }
844     
845     
846     /**
847      * Reads the contents of a file line by line to a List of Strings.
848      * The file is always closed.
849      *
850      * @param file  the file to read, must not be {@code null}
851      * @param encoding  the encoding to use, {@code null} means platform default
852      * @return the list of Strings representing each line in the file, never {@code null}
853      * @throws IOException in case of an I/O error
854      * @since 2.3
855      */
856     public static List<String> readLines( File file, Charset encoding ) throws IOException 
857     {
858         InputStream in = null;
859         
860         try 
861         {
862             in = openInputStream( file );
863             return IOUtils.readLines( in, IOUtils.toCharset( encoding ) );
864         } 
865         finally 
866         {
867             IOUtils.closeQuietly( in );
868         }
869     }
870 }