001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.transaction.util;
018
019import java.io.File;
020import java.io.FileInputStream;
021import java.io.FileOutputStream;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.OutputStream;
025
026/**
027 * Helper methods for file manipulation. 
028 * All methods are <em>thread safe</em>.
029 * 
030 * @version $Id: FileHelper.java 493628 2007-01-07 01:42:48Z joerg $
031 */
032public final class FileHelper {
033
034    private static int BUF_SIZE = 50000;
035    private static byte[] BUF = new byte[BUF_SIZE];
036
037    /**
038     * Deletes a file specified by a path.
039     *  
040     * @param path path of file to be deleted
041     * @return <code>true</code> if file has been deleted, <code>false</code> otherwise
042     */
043    public static boolean deleteFile(String path) {
044        File file = new File(path);
045        return file.delete();
046    }
047
048    /**
049     * Checks if a file specified by a path exits.
050     *  
051     * @param path path of file to be checked
052     * @return <code>true</code> if file exists, <code>false</code> otherwise
053     */
054    public static boolean fileExists(String path) {
055        File file = new File(path);
056        return file.exists();
057    }
058
059    /**
060     * Creates a file specified by a path. All necessary directories will be created.
061     * 
062     * @param path path of file to be created
063     * @return <code>true</code> if file has been created, <code>false</code> if the file already exists
064     * @throws  IOException
065     *          If an I/O error occurred
066     */
067    public static boolean createFile(String path) throws IOException {
068        File file = new File(path);
069        if (file.isDirectory()) {
070            return file.mkdirs();
071        } else {
072            File dir = file.getParentFile();
073            // do not check if this worked, as it may also return false, when all neccessary dirs are present
074            dir.mkdirs();
075            return file.createNewFile();
076        }
077    }
078
079    /**
080     * Removes a file. If the specified file is a directory all contained files will
081     * be removed recursively as well. 
082     * 
083     * @param toRemove file to be removed
084     */
085    public static void removeRec(File toRemove) {
086        if (toRemove.isDirectory()) {
087            File fileList[] = toRemove.listFiles();
088            for (int a = 0; a < fileList.length; a++) {
089                removeRec(fileList[a]);
090            }
091        }
092        toRemove.delete();
093    }
094
095    /**
096     * Moves one directory or file to another. Existing files will be replaced.
097     * 
098     * @param source file to move from
099     * @param target file to move to
100     * @throws IOException if an I/O error occurs (may result in partially done work)  
101     */
102    public static void moveRec(File source, File target) throws IOException {
103        byte[] sharedBuffer = new byte[BUF_SIZE];
104        moveRec(source, target, sharedBuffer);
105    }
106
107    static void moveRec(File source, File target, byte[] sharedBuffer) throws IOException {
108        if (source.isDirectory()) {
109            if (!target.exists()) {
110                target.mkdirs();
111            }
112            if (target.isDirectory()) {
113
114                File[] files = source.listFiles();
115                for (int i = 0; i < files.length; i++) {
116                    File file = files[i];
117                    File targetFile = new File(target, file.getName());
118                    if (file.isFile()) {
119                        if (targetFile.exists()) {
120                            targetFile.delete();
121                        }
122                        if (!file.renameTo(targetFile)) {
123                            copy(file, targetFile, sharedBuffer);
124                            file.delete();
125                        }
126                    } else {
127                        if (!targetFile.exists()) {
128                            if (!targetFile.mkdirs()) {
129                                throw new IOException("Could not create target directory: "
130                                        + targetFile);
131                            }
132                        }
133                        moveRec(file, targetFile);
134                    }
135                }
136                source.delete();
137            }
138        } else {
139            if (!target.isDirectory()) {
140                copy(source, target, sharedBuffer);
141                source.delete();
142            }
143        }
144    }
145
146    /**
147     * Copies one directory or file to another. Existing files will be replaced.
148     * 
149     * @param source directory or file to copy from
150     * @param target directory or file to copy to
151     * @throws IOException if an I/O error occurs (may result in partially done work)  
152     */
153    public static void copyRec(File source, File target) throws IOException {
154        byte[] sharedBuffer = new byte[BUF_SIZE];
155        copyRec(source, target, sharedBuffer);
156    }
157
158    static void copyRec(File source, File target, byte[] sharedBuffer) throws IOException {
159        if (source.isDirectory()) {
160            if (!target.exists()) {
161                target.mkdirs();
162            }
163            if (target.isDirectory()) {
164
165                File[] files = source.listFiles();
166                for (int i = 0; i < files.length; i++) {
167                    File file = files[i];
168                    File targetFile = new File(target, file.getName());
169                    if (file.isFile()) {
170                        if (targetFile.exists()) {
171                            targetFile.delete();
172                        }
173                        copy(file, targetFile, sharedBuffer);
174                    } else {
175                        targetFile.mkdirs();
176                        copyRec(file, targetFile);
177                    }
178                }
179            }
180        } else {
181            if (!target.isDirectory()) {
182                if (!target.exists()) {
183                    File dir = target.getParentFile();
184                    if(!dir.exists() && !dir.mkdirs()) {
185                        throw new IOException("Could not create target directory: " + dir);
186                    }
187                    if (!target.createNewFile()) {
188                        throw new IOException("Could not create target file: " + target);
189                    }
190                }
191                copy(source, target, sharedBuffer);
192            }
193        }
194    }
195
196    /**
197     * Copies one file to another using {@link #copy(InputStream, OutputStream)}.
198     * 
199     * @param input
200     *            source file
201     * @param output
202     *            destination file
203     * @return the number of bytes copied
204     * @throws IOException
205     *             if an I/O error occurs (may result in partially done work)
206     * @see #copy(InputStream, OutputStream)
207     */
208    public static long copy(File input, File output) throws IOException {
209        FileInputStream in = null;
210        try {
211            in = new FileInputStream(input);
212            return copy(in, output);
213        } finally {
214            if (in != null) {
215                try {
216                    in.close();
217                } catch (IOException e) {
218                }
219            }
220        }
221    }
222
223    /**
224     * Copies one file to another using the supplied buffer.
225     * 
226     * @param input source file
227     * @param output destination file
228     * @param copyBuffer buffer used for copying
229     * @return the number of bytes copied
230     * @throws IOException if an I/O error occurs (may result in partially done work)  
231     * @see #copy(InputStream, OutputStream)
232     */
233    public static long copy(File input, File output, byte[] copyBuffer) throws IOException {
234        FileInputStream in = null;
235        FileOutputStream out = null;
236        try {
237            in = new FileInputStream(input);
238            out = new FileOutputStream(output);
239            return copy(in, out, copyBuffer);
240        } finally {
241            if (in != null) {
242                try {
243                    in.close();
244                } catch (IOException e) {
245                }
246            }
247            if (out != null) {
248                try {
249                    out.close();
250                } catch (IOException e) {
251                }
252            }
253        }
254    }
255
256    /**
257     * Copies an <code>InputStream</code> to a file using {@link #copy(InputStream, OutputStream)}.
258     * 
259     * @param in stream to copy from 
260     * @param outputFile file to copy to
261     * @return the number of bytes copied
262     * @throws IOException if an I/O error occurs (may result in partially done work)  
263     * @see #copy(InputStream, OutputStream)
264     */
265    public static long copy(InputStream in, File outputFile) throws IOException {
266        FileOutputStream out = null;
267        try {
268            out = new FileOutputStream(outputFile);
269            return copy(in, out);
270        } finally {
271            if (out != null) {
272                try {
273                    out.close();
274                } catch (IOException e) {
275                }
276            }
277        }
278    }
279
280    /**
281     * Copies an <code>InputStream</code> to an <code>OutputStream</code> using a local internal buffer for performance.
282     * Compared to {@link #globalBufferCopy(InputStream, OutputStream)} this method allows for better
283     * concurrency, but each time it is called generates a buffer which will be garbage.
284     * 
285     * @param in stream to copy from 
286     * @param out stream to copy to
287     * @return the number of bytes copied
288     * @throws IOException if an I/O error occurs (may result in partially done work)  
289     * @see #globalBufferCopy(InputStream, OutputStream)
290     */
291    public static long copy(InputStream in, OutputStream out) throws IOException {
292        // we need a buffer of our own, so no one else interferes
293        byte[] buf = new byte[BUF_SIZE];
294        return copy(in, out, buf);
295    }
296
297    /**
298     * Copies an <code>InputStream</code> to an <code>OutputStream</code> using a global internal buffer for performance.
299     * Compared to {@link #copy(InputStream, OutputStream)} this method generated no garbage,
300     * but decreases concurrency.
301     * 
302     * @param in stream to copy from 
303     * @param out stream to copy to
304     * @return the number of bytes copied
305     * @throws IOException if an I/O error occurs (may result in partially done work)  
306     * @see #copy(InputStream, OutputStream)
307     */
308    public static long globalBufferCopy(InputStream in, OutputStream out) throws IOException {
309        synchronized (BUF) {
310            return copy(in, out, BUF);
311        }
312    }
313
314    /**
315     * Copies an <code>InputStream</code> to an <code>OutputStream</code> using the specified buffer. 
316     * 
317     * @param in stream to copy from 
318     * @param out stream to copy to
319     * @param copyBuffer buffer used for copying
320     * @return the number of bytes copied
321     * @throws IOException if an I/O error occurs (may result in partially done work)  
322     * @see #globalBufferCopy(InputStream, OutputStream)
323     * @see #copy(InputStream, OutputStream)
324     */
325    public static long copy(InputStream in, OutputStream out, byte[] copyBuffer) throws IOException {
326        long bytesCopied = 0;
327        int read = -1;
328
329        while ((read = in.read(copyBuffer, 0, copyBuffer.length)) != -1) {
330            out.write(copyBuffer, 0, read);
331            bytesCopied += read;
332        }
333        return bytesCopied;
334    }
335}