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     */
017    package org.apache.commons.transaction.util;
018    
019    import java.io.File;
020    import java.io.FileInputStream;
021    import java.io.FileOutputStream;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import 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     */
032    public 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    }