001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.io;
031
032import java.io.BufferedInputStream;
033import java.io.BufferedOutputStream;
034import java.io.BufferedReader;
035import java.io.File;
036import java.io.FileInputStream;
037import java.io.FileOutputStream;
038import java.io.FileWriter;
039import java.io.FilenameFilter;
040import java.io.IOException;
041import java.io.InputStream;
042import java.io.InputStreamReader;
043import java.io.PrintWriter;
044import java.net.URL;
045import java.net.URLConnection;
046import java.util.ArrayList;
047import java.util.Arrays;
048import java.util.List;
049import java.util.Stack;
050
051/**
052 * Utility methods for dealing with files on the filesystem
053 *
054 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
055 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
056 * @author David Dupplaw (dpd@ecs.soton.ac.uk)
057 *
058 */
059public class FileUtils {
060        /**
061         * Recursively delete a directory
062         * @param dir
063         * @return true if success; false otherwise
064         */
065        public static boolean deleteRecursive(final File dir) {
066                if (dir.isDirectory()) {
067                        final String[] children = dir.list();
068                        for (int i=0; i<children.length; i++) {
069                                final boolean success = FileUtils.deleteRecursive(new File(dir, children[i]));
070                                if (!success) {
071                                        return false;
072                                }
073                        }
074                }
075
076                // The directory is now empty so delete it
077                return dir.delete();
078        }
079
080        /**
081         * Download the contents of the given URL to the given file
082         * @param url The URL to download from
083         * @param file The target file
084         * @throws IOException if an error occurs
085         */
086        public static void downloadURL(final URL url, final File file) throws IOException {
087                final URLConnection conn = url.openConnection();
088                final InputStream stream = conn.getInputStream();
089                final FileOutputStream fos = new FileOutputStream(file);
090                final byte[] buffer = new byte[1024];
091                int read = 0;
092                while((read = stream.read(buffer)) != -1){
093                        fos.write(buffer,0,read);
094                }
095                fos.close();
096        }
097
098        /**
099         * Utility method for quickly create a {@link BufferedReader} for
100         * a given file.
101         * @param file The file
102         * @return the corresponding reader
103         * @throws IOException if an error occurs
104         */
105        public static BufferedReader read(final File file) throws IOException {
106                return new BufferedReader(new InputStreamReader(new FileInputStream(file)));
107        }
108        /**
109         * Utility method for reading a whole file into a single string.
110         * @param stream The stream
111         * @return the corresponding reader
112         * @throws IOException if an error occurs
113         */
114        public static String readall(final InputStream stream) throws IOException {
115                final BufferedReader br = new BufferedReader(new InputStreamReader(stream));
116                String line = null;
117                final StringBuilder builder = new StringBuilder();
118                while((line = br.readLine()) != null){
119                        builder.append(line);
120                        builder.append("\n");
121                }
122
123                return builder.toString();
124        }
125        /**
126         * Utility method for reading a whole file into a single string.
127         * @param stream The stream
128         * @param encoding the charset of {@link InputStreamReader}
129         * @return the corresponding reader
130         * @throws IOException if an error occurs
131         */
132        public static String readall(final InputStream stream, String encoding) throws IOException {
133                final BufferedReader br = new BufferedReader(new InputStreamReader(stream,encoding));
134                String line = null;
135                final StringBuilder builder = new StringBuilder();
136                while((line = br.readLine()) != null){
137                        builder.append(line);
138                        builder.append("\n");
139                }
140
141                return builder.toString();
142        }
143
144        /**
145         * Utility method for reading a whole file into a single string.
146         * @param file The file
147         * @return the corresponding reader
148         * @throws IOException if an error occurs
149         */
150        public static String readall(final File file) throws IOException {
151                return FileUtils.readall(new FileInputStream(file));
152        }
153
154        /**
155         * Helper function for writing a text stream to a file.
156         * @param stream the stream will be consumed
157         * @param output file
158         * @return a temporary file with the stream context written
159         * @throws IOException
160         */
161        public static File copyStreamToFile(final InputStream stream,final File output) throws IOException{
162                final BufferedReader r = new BufferedReader(new InputStreamReader(stream));
163                String l =null;
164                final PrintWriter writer = new PrintWriter(new FileWriter(output));
165                while((l = r.readLine())!=null){
166                        writer.println(l);
167                }
168                writer.close();
169                r.close();
170                return output;
171        }
172
173        /**
174         *      Helper function for writing a binary stream to a file. The stream and
175         *      the file are both closed on completion.
176         *
177         *      @param stream The stream to be consumed.
178         *      @param output The file to output to
179         *      @return The output file
180         *      @throws IOException
181         */
182        public static File copyStreamToFileBinary( final InputStream stream,
183                        final File output ) throws IOException
184        {
185                final FileOutputStream out = new FileOutputStream( output );
186                final byte buf[] = new byte[1024];
187                int len = 0;
188                while( (len = stream.read( buf )) > 0 )
189                        out.write( buf, 0, len );
190                out.close();
191                stream.close();
192
193                return output;
194        }
195
196        /**
197         *      Given a JAR Resource, this method will unpack the file to
198         *      a temporary file and return the temporary file location.
199         *      This temporary file will be deleted on the application exit.
200         *      If the given resource is not a JAR resource, the method
201         *      will return null.
202         *
203         *      @param resource The resource to unpack
204         *      @return The temporary file location
205         *      @throws IOException If the temporary file could not be created.
206         */
207        public static File unpackJarFile( final URL resource ) throws IOException
208        {
209                return FileUtils.unpackJarFile( resource, true );
210        }
211
212        /**
213         *      Given a JAR Resource, this method will unpack the file to
214         *      a temporary file and return the temporary file location.
215         *      If the given resource is not a JAR resource, the method
216         *      will return null.
217         *
218         *      @param resource The resource to unpack
219         *      @param deleteOnExit Whether to delete the temporary file on exit
220         *      @return The temporary file location
221         *      @throws IOException If the temporary file could not be created.
222         */
223        public static File unpackJarFile( final URL resource, final boolean deleteOnExit )
224                        throws IOException
225                        {
226                if( !FileUtils.isJarResource( resource ) )
227                        return null;
228
229                final String ext = resource.toString().substring(
230                                resource.toString().lastIndexOf(".") );
231                final File f = File.createTempFile( "openimaj",ext );
232                FileUtils.unpackJarFile( resource, f, deleteOnExit );
233                return f;
234                        }
235
236        /**
237         *      Given a JAR resource, this method will unpack the file
238         *      to the given destination. If the given resource is not
239         *      a JAR resource, this method will do nothing.
240         *
241         *      @param resource The resource to unpack.
242         *      @param destination The destination file
243         *      @param deleteOnExit Whether to delete the unpacked file on exit.
244         */
245        public static void unpackJarFile( final URL resource, final File destination,
246                        final boolean deleteOnExit )
247        {
248                if( deleteOnExit )
249                        destination.deleteOnExit();
250
251                BufferedInputStream urlin = null;
252                BufferedOutputStream fout = null;
253                try {
254                        final int bufSize = 8 * 1024;
255                        urlin = new BufferedInputStream(
256                                        resource.openConnection().getInputStream(),
257                                        bufSize);
258                        fout = new BufferedOutputStream(
259                                        new FileOutputStream( destination ), bufSize);
260
261                        int read = -1;
262                        final byte[] buf = new byte[ bufSize ];
263                        while ((read = urlin.read(buf, 0, bufSize)) >= 0)
264                                fout.write(buf, 0, read);
265
266                        fout.flush();
267                }
268                catch (final IOException ioex)
269                {
270                        return;
271                }
272                catch( final SecurityException sx )
273                {
274                        return;
275                }
276                finally
277                {
278                        if (urlin != null)
279                        {
280                                try
281                                {
282                                        urlin.close();
283                                }
284                                catch (final IOException cioex)
285                                {
286                                }
287                        }
288                        if (fout != null)
289                        {
290                                try
291                                {
292                                        fout.close();
293                                }
294                                catch (final IOException cioex)
295                                {
296                                }
297                        }
298                }
299        }
300
301        /**
302         *      Returns whether the given resource is a jar resource.
303         *      @param resource The resource to test.
304         *      @return TRUE if the resource is a JAR resource
305         */
306        public static boolean isJarResource( final URL resource )
307        {
308                return FileUtils.isJarResource( resource.toString() );
309        }
310
311        /**
312         *      Returns whether the given resource is a jar resource.
313         *      @param resourceURL The resource to test.
314         *      @return TRUE if the resource is in a jar.
315         */
316        public static boolean isJarResource( final String resourceURL )
317        {
318                return resourceURL.startsWith( "jar:" );
319        }
320
321        /**
322         * Count the number of newlines in the given file
323         * @param filename The file
324         * @return the number of newline characters
325         */
326        public static int countLines(final File filename)  {
327                InputStream is = null;
328                try {
329                        is = new BufferedInputStream(new FileInputStream(filename));
330                        final byte[] c = new byte[1024];
331                        int count = 0;
332                        int readChars = 0;
333                        while ((readChars = is.read(c)) != -1) {
334                                for (int i = 0; i < readChars; ++i) {
335                                        if (c[i] == '\n')
336                                                ++count;
337                                }
338                        }
339                        return count;
340                }
341                catch(final Exception e){
342                        return -1;
343                } finally {
344                        try {
345                                is.close();
346                        } catch (final IOException e) {
347                                return -1;
348                        }
349                }
350        }
351
352        /**
353         * Using {@link File#listFiles(FilenameFilter)} find a file in the directory recursively (i.e. following directories down).
354         * @param start
355         * @param filenameFilter
356         * @return list of files matching the filter
357         */
358        public static File[] findRecursive(final File start, final FilenameFilter filenameFilter) {
359                final Stack<File> filesToCheck = new Stack<File>();
360                final List<File> found = new ArrayList<File>();
361                filesToCheck .push(start);
362                while(filesToCheck.size() > 0){
363                        final File toCheck = filesToCheck.pop();
364                        final File[] afiles = toCheck.listFiles(new FilenameFilter() {
365
366                                @Override
367                                public boolean accept(final File dir, final String name) {
368                                        final File found = new File(dir,name);
369                                        final boolean accept = filenameFilter.accept(found, name);
370                                        if(toCheck != found && found .isDirectory())
371                                        {
372                                                System.out.println("Adding: " + found);
373                                                filesToCheck.push(found);
374                                        }
375                                        return accept;
376                                }
377                        });
378                        found.addAll(Arrays.asList(afiles));
379                }
380                return found.toArray(new File[found.size()]);
381        }
382
383        /**
384         * @param file the file to read from
385         * @return the lines in the file
386         * @throws IOException
387         */
388        public static String[] readlines(final File file) throws IOException {
389                final BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
390                String line = null;
391                final List<String> allLines = new ArrayList<String>();
392                while((line = br.readLine()) != null){
393                        allLines.add(line);
394                }
395                br.close();
396
397                return allLines.toArray(new String[allLines.size()]);
398        }
399
400        /**
401         * @param file the file to read from
402         * @param encoding
403         * @return the lines in the file
404         * @throws IOException
405         */
406        public static String[] readlines(final File file, String encoding) throws IOException {
407                final BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file),encoding));
408                String line = null;
409                final List<String> allLines = new ArrayList<String>();
410                while((line = br.readLine()) != null){
411                        allLines.add(line);
412                }
413                br.close();
414
415                return allLines.toArray(new String[allLines.size()]);
416        }
417
418        /**
419         * @param stream the file to read from
420         * @return the lines in the file
421         * @throws IOException
422         */
423        public static String[] readlines(final InputStream stream) throws IOException {
424                final BufferedReader br = new BufferedReader(new InputStreamReader(stream));
425                String line = null;
426                final List<String> allLines = new ArrayList<String>();
427                while((line = br.readLine()) != null){
428                        allLines.add(line);
429                }
430
431                return allLines.toArray(new String[allLines.size()]);
432        }
433
434        /**
435         * @param stream the file to read from
436         * @param encoding the inputstream encoding
437         * @return the lines in the file
438         * @throws IOException
439         */
440        public static String[] readlines(final InputStream stream, String encoding) throws IOException {
441                final BufferedReader br = new BufferedReader(new InputStreamReader(stream,encoding));
442                String line = null;
443                final List<String> allLines = new ArrayList<String>();
444                while((line = br.readLine()) != null){
445                        allLines.add(line);
446                }
447
448                return allLines.toArray(new String[allLines.size()]);
449        }
450}