View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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  	//--- Constant(s) ---
58  
59  	//--- Data field(s) ---
60  
61  	private static Logger logger = Logger.getLogger(FileHelper.class);
62  
63  	//--- Constructor(s) ---
64  
65  	//--- Method(s) ---
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 			// sanitize filename
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 		// remove trailing semicolon
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 				// this exception gets lost
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 				// this exception gets lost
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 				// this exception gets lost
632 			}
633 		}
634 	}
635 
636 }