1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.xnap.commons.util;
22
23 import java.io.BufferedInputStream;
24 import java.io.BufferedOutputStream;
25 import java.io.BufferedReader;
26 import java.io.BufferedWriter;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.FileNotFoundException;
30 import java.io.FileOutputStream;
31 import java.io.FileWriter;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.InputStreamReader;
35 import java.io.NotSerializableException;
36 import java.io.ObjectInputStream;
37 import java.io.ObjectOutputStream;
38 import java.io.OutputStream;
39 import java.io.RandomAccessFile;
40 import java.util.Collection;
41 import java.util.Iterator;
42 import java.util.LinkedList;
43 import java.util.List;
44 import java.util.Properties;
45 import org.apache.commons.logging.Log;
46 import org.apache.commons.logging.LogFactory;
47 import org.xnap.commons.io.NullProgressMonitor;
48 import org.xnap.commons.io.ProgressMonitor;
49
50 /***
51 * Provides a set of static methods that help with file manipulation.
52 */
53 public class FileHelper {
54
55 private static Log logger = LogFactory.getLog(FileHelper.class);
56
57 /***
58 * Creates a unique file by inserting digits into <code>pathname</code>.
59 *
60 * @return the created file
61 */
62 public static synchronized File createUnique(String pathname)
63 throws IOException
64 {
65 logger.debug("FileHelper: creating unique: " + pathname);
66
67 File f = new File(uniqueName(pathname));
68 f.createNewFile();
69
70 return f;
71 }
72
73
74 /***
75 * Creates a unique file in by inserting digits into <code>filename</code>.
76 *
77 * @param child the name of the file to create
78 * @param parent the path of the file to create
79 * @return the created file
80 */
81 public static synchronized File createUnique(File parent, String child)
82 throws IOException
83 {
84 if (parent.isDirectory() || parent.mkdirs()) {
85 return createUnique
86 (parent.getAbsolutePath() + File.separator + child);
87 }
88 throw new FileNotFoundException();
89 }
90
91 /***
92 * Moves <code>file</code> to <code>path</code> but renames
93 * <code>filename</code> if it already exists in the target path.
94 *
95 * @param file the file to move
96 * @param path the target path
97 * @param filename the new filename for <code>file</code>
98 * @return the moved file
99 */
100 public static synchronized File moveUnique
101 (File file, String path, String filename) throws IOException
102 {
103 String newFilename = appendSeparator(path) + filename;
104 logger.debug("moveUnique new name: " + newFilename);
105 if (newFilename.equals(file.getAbsolutePath())) {
106 return file;
107 }
108
109 File p = new File(path);
110 if (p.isDirectory() || p.mkdirs()) {
111 File newFile = new File(uniqueName(newFilename));
112 logger.debug("moveUnique new file: " + newFile);
113 if (move(file, newFile)) {
114 return newFile;
115 }
116 else {
117 throw new FileNotFoundException
118 ("Could not rename " + file.getAbsolutePath() + " to "
119 + newFile.getAbsolutePath());
120 }
121 }
122 else {
123 throw new FileNotFoundException
124 ("Could not create " + p.getAbsolutePath());
125 }
126 }
127
128 /***
129 * Moves <code>file</code> to <code>path</code>.
130 *
131 * @see #moveUnique(File, String, String)
132 */
133 public static synchronized File moveUnique(File file, String path)
134 throws IOException
135 {
136 return moveUnique(file, path, file.getName());
137 }
138
139 /***
140 * Moves a file. Tries a rename, if fails, copies file.
141 */
142 public static boolean move(File source, File dest) throws IOException
143 {
144 if (!source.renameTo(dest)) {
145 copy(source, dest);
146 source.delete();
147 }
148
149 return true;
150 }
151
152 public static void copy(File source, File dest) throws IOException
153 {
154 copy(source, dest, NullProgressMonitor.MONITOR);
155 }
156
157 /***
158 * Copies <code>source</code> to <code>dest</code>. If dest already exists
159 * it is overwritten.
160 *
161 * @param source the source file
162 * @param dest the destination file
163 */
164 public static void copy(File source, File dest, ProgressMonitor monitor)
165 throws IOException
166 {
167 InputStream in = new FileInputStream(source);
168 monitor.setTotalSteps(source.length());
169 try {
170 copy(in, new FileOutputStream(dest), monitor);
171
172 }
173 finally {
174 try {
175 in.close();
176 }
177 catch (IOException e) {
178 }
179 }
180 }
181
182 public static void copy(InputStream inStream, OutputStream outStream)
183 throws IOException
184 {
185 copy(inStream, outStream, NullProgressMonitor.MONITOR);
186 }
187
188 /***
189 * Copies <code>source</code> to <code>dest</code>. If dest already exists
190 * it is overwritten.
191 *
192 * @param inStream the source data
193 * @param outStream the destination data
194 */
195 public static void copy(InputStream inStream, OutputStream outStream,
196 ProgressMonitor monitor)
197 throws IOException
198 {
199 BufferedInputStream in = null;
200 BufferedOutputStream out = null;
201 try {
202 in = new BufferedInputStream(inStream);
203 out = new BufferedOutputStream(outStream);
204
205 byte buffer[] = new byte[512 * 1024];
206 int count;
207 while (!monitor.isCancelled()
208 && (count = in.read(buffer, 0, buffer.length)) != -1) {
209 out.write(buffer, 0, count);
210 monitor.work(count);
211 }
212 out.flush();
213 }
214 finally {
215 if (in != null) {
216 try {
217 in.close();
218 }
219 catch (IOException e) {
220 }
221 }
222 if (out != null) {
223 try {
224 out.close();
225 }
226 catch (IOException e) {
227 }
228 }
229 }
230 }
231
232 /***
233 * Returns the lower case extension part of <code>filename</code>.
234 *
235 * @see #name(String)
236 */
237 public static String extension(String filename)
238 {
239 return StringHelper.lastToken(filename, ".").toLowerCase();
240 }
241
242 /***
243 * Returns the name part of <code>filename</code> without
244 * its extension.
245 *
246 * @see #extension(String)
247 */
248 public static String name(String filename)
249 {
250 int i = filename.lastIndexOf(".");
251 return (i < 1) ? filename : filename.substring(0, i);
252 }
253
254 /***
255 * Creates unique filename.
256 */
257 public static String uniqueName(String filename)
258 {
259 return uniqueName(filename, "");
260 }
261
262 public static String uniqueName(String filename, String infix)
263 {
264 String extension = extension(filename);
265
266 if (extension.length() > 0) {
267 extension = "." + extension;
268 filename = name(filename);
269 }
270
271 if (infix.length() > 0) {
272 infix = "." + infix;
273 }
274
275 if (exists(filename + infix + extension)) {
276 for (int i = 1; ; i++) {
277 if (!exists(filename + infix + "." + i + extension))
278 return (filename + infix + "." + i + extension);
279 }
280 }
281
282 return filename + infix + extension;
283 }
284
285 /***
286 * Returns true, if the file denoting <code>pathname</code> exists,
287 * false otherwise.
288 */
289 public static boolean exists(String pathname)
290 {
291 return (new File(pathname)).exists();
292 }
293
294 /***
295 * Checks for existence of .xnap folder in the user's home directory and
296 * returns the absolute path with a file separator appended.
297 *
298 * @param subdir a sub directory that is located in the applications settings directory or created if
299 * it does not exist
300 * @return empty string, if subdir could not be created; absolute path,
301 * otherwise
302 */
303 public static final String getHomeDir(String appname, String subdir)
304 throws IOException
305 {
306 StringBuilder sb = new StringBuilder();
307
308 sb.append(System.getProperty("user.home"));
309 sb.append(File.separatorChar);
310
311 if (appname != null && appname.length() > 0) {
312 sb.append(".");
313 sb.append(appname);
314 sb.append(File.separatorChar);
315 }
316
317 if (subdir != null && subdir.length() > 0) {
318 sb.append(subdir);
319 sb.append(File.separatorChar);
320 }
321 String dir = sb.toString();
322
323 File file = new File(dir);
324 if (file.isDirectory() || file.mkdirs()) {
325 return dir;
326 }
327
328 throw new IOException("Could not create directory");
329 }
330
331 /***
332 * Returns the absolute path of the applications settings directory.
333 *
334 * @see #getHomeDir(String)
335 */
336 public static final String getHomeDir(String appname) throws IOException
337 {
338 return getHomeDir(appname, null);
339 }
340
341 /***
342 * Appens a file separator to <code>path</code> if it does not have a
343 * trailing one.
344 */
345 public static String appendSeparator(String path)
346 {
347 if (path.length() > 0 && !path.endsWith(File.separator)) {
348 return path + File.separator;
349 }
350 else {
351 return path;
352 }
353 }
354
355 /***
356 * Shortens <code>file</code> by <code>bytes</code> bytes.
357 */
358 public static void shorten(File file, long bytes) throws IOException
359 {
360 RandomAccessFile f = new RandomAccessFile(file, "rw");
361 try {
362 f.setLength(Math.max(f.length() - bytes, 0));
363 }
364 finally {
365 try {
366 f.close();
367 } catch (IOException e) {}
368 }
369 }
370
371 /***
372 * Stores <code>props</code> in <code>file</code>.
373 */
374 public static void writeProperties(File file, Properties props)
375 throws IOException
376 {
377 FileOutputStream out = null;
378 try {
379 out = new FileOutputStream(file);
380 props.store(out, "This file was automatically generated.");
381 }
382 catch (IOException e) {
383 throw(e);
384 }
385 finally {
386 try {
387 if (out != null) {
388 out.close();
389 }
390 }
391 catch (Exception e) {
392 }
393 }
394 }
395
396 /***
397 * Reads all objects from <code>file</code> using serialization and adds
398 * them to <code>c</code>.
399 */
400 public static void readBinary(File file, Collection<Object> c) throws IOException
401 {
402 logger.debug("reading binary file: " + file);
403
404 ObjectInputStream in = null;
405 try {
406 in = new ObjectInputStream(new FileInputStream(file));
407
408 int size = in.readInt();
409 for (int i = 0; i < size; i++) {
410 try {
411 Object o = in.readObject();
412 if (o != null) {
413 c.add(o);
414 }
415 }
416 catch (IOException e) {
417 throw e;
418 }
419 catch (Exception e) {
420 logger.warn("error while reading binary file", e);
421 }
422 }
423 }
424 finally {
425 try {
426 if (in != null) {
427 in.close();
428 }
429 }
430 catch (IOException e) {
431 }
432 }
433 }
434
435 public static String readText(File file) throws IOException
436 {
437 return readText(new FileInputStream(file));
438 }
439
440
441 /***
442 * Reads a text file.
443 */
444 public static String readText(InputStream inStream) throws IOException
445 {
446 BufferedReader in
447 = new BufferedReader(new InputStreamReader(inStream));
448 try {
449 StringBuilder sb = new StringBuilder();
450 char buffer[] = new char[4 * 1024];
451 int count;
452 while ((count = in.read(buffer, 0, buffer.length)) != -1) {
453 sb.append(buffer, 0, count);
454 }
455 return sb.toString();
456 }
457 finally {
458 try {
459 in.close();
460 }
461 catch (IOException e) {
462
463 }
464 }
465 }
466
467 /***
468 * TODO provide a symmetrical write function?
469 * @param file
470 * @throws IOException
471 */
472 public static String[] readConfig(File file) throws IOException
473 {
474 return readConfig(new FileInputStream(file));
475 }
476
477 /***
478 * Reads a text file. Ignores all lines empty lines and lines that
479 * start with a '#' character.
480 *
481 * @return always a valid array, possibly with size 0
482 */
483 public static String[] readConfig(InputStream inStream) throws IOException
484 {
485 BufferedReader in
486 = new BufferedReader(new InputStreamReader(inStream));
487 try {
488 List<String> lines = new LinkedList<String>();
489
490 String s;
491 while ((s = in.readLine()) != null) {
492 s = s.trim();
493 if (s.length() == 0 || s.startsWith("#")) {
494 continue;
495 }
496 lines.add(s);
497 }
498 return lines.toArray(new String[0]);
499 }
500 finally {
501 try {
502 in.close();
503 }
504 catch (IOException e) {
505
506 }
507 }
508 }
509
510 /***
511 * Write all items in <code>c</code> to <code>file</code> using
512 * serialization.
513 */
514 public static void writeBinary(File file, Collection c) throws IOException
515 {
516 logger.debug("writing " + c.size() + " items to binary file: " + file);
517
518 ObjectOutputStream out = null;
519 try {
520 out = new ObjectOutputStream(new FileOutputStream(file));
521
522 out.writeInt(c.size());
523 for (Iterator i = c.iterator(); i.hasNext();) {
524 try {
525 out.writeObject(i.next());
526 }
527 catch (NotSerializableException e) {
528 logger.debug("Object not serializable", e);
529 }
530 }
531 }
532 finally {
533 try {
534 if (out != null) {
535 out.close();
536 }
537 }
538 catch (IOException e) {
539 }
540 }
541 }
542
543 /***
544 * Writes a text file.
545 */
546 public static void writeText(File file, String text) throws IOException
547 {
548 BufferedWriter out = new BufferedWriter(new FileWriter(file));
549 try {
550 out.write(text);
551 }
552 finally {
553 try {
554 out.close();
555 }
556 catch (IOException e) {
557
558 }
559 }
560 }
561
562 }