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