comparison artifacts-common/src/main/java/org/dive4elements/artifacts/common/utils/FileTools.java @ 472:783cc1b6b615

Moved directories to org.dive4elements
author Sascha L. Teichmann <teichmann@intevation.de>
date Thu, 25 Apr 2013 10:53:15 +0200
parents artifacts-common/src/main/java/de/intevation/artifacts/common/utils/FileTools.java@5c07601fe60e
children 415df0fc4fa1
comparison
equal deleted inserted replaced
471:1a87cb24a446 472:783cc1b6b615
1 /*
2 * Copyright (c) 2010, 2011 by Intevation GmbH
3 *
4 * This program is free software under the LGPL (>=v2.1)
5 * Read the file LGPL.txt coming with the software for details
6 * or visit http://www.gnu.org/licenses/ if it does not exist.
7 */
8 package de.intevation.artifacts.common.utils;
9
10 import java.io.BufferedInputStream;
11 import java.io.File;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.FileInputStream;
15 import java.io.FileOutputStream;
16 import java.io.OutputStream;
17 import java.io.BufferedOutputStream;
18 import java.nio.channels.FileChannel;
19
20 import java.util.Deque;
21 import java.util.ArrayDeque;
22 import java.util.List;
23 import java.util.Set;
24 import java.util.HashSet;
25 import java.util.ArrayList;
26 import java.util.Enumeration;
27 import java.util.zip.ZipFile;
28 import java.util.zip.ZipEntry;
29 import java.util.zip.ZipOutputStream;
30
31 import org.apache.log4j.Logger;
32
33 import java.security.MessageDigest;
34 import java.security.NoSuchAlgorithmException;
35
36 public class FileTools
37 {
38 private static Logger log = Logger.getLogger(FileTools.class);
39
40 public static final String DIGEST =
41 System.getProperty("artifacts.common.file.cmp.digest", "MD5");
42
43 private FileTools() {
44 }
45
46
47 /** Remove everything after dot from name. */
48 public static final String removeExtension(String name) {
49 int index = name.lastIndexOf('.');
50 return index == -1
51 ? name
52 : name.substring(0, index);
53 }
54
55
56 public static File getDirectory(String path, String name) {
57 if (path == null || name == null) {
58 return null;
59 }
60
61 File dir = new File(path, name);
62
63 if (!dir.exists()) {
64 log.debug(
65 "Directory '" + dir.getAbsolutePath() + "' doesn't " +
66 "exist. Try to create it.");
67
68 return dir.mkdir() ? dir : null;
69 }
70 else {
71 return dir.isDirectory() ? dir : null;
72 }
73 }
74
75 public static File repair(File file) {
76 file = file.getAbsoluteFile();
77 if (file.exists()) {
78 return file;
79 }
80 Deque<String> parts = new ArrayDeque<String>();
81 File curr = file;
82 while (curr != null) {
83 String name = curr.getName();
84 if (name.length() > 0) {
85 parts.push(curr.getName());
86 }
87 curr = curr.getParentFile();
88 }
89
90 curr = null;
91 OUTER: while (!parts.isEmpty()) {
92 String f = parts.pop();
93 log.debug("fixing: '" + f + "'");
94 if (f.equals(".") || f.equals("..")) {
95 // No need to fix . or ..
96 continue;
97 }
98 if (curr == null) {
99 // XXX: Not totaly correct because there
100 // more than one root on none unix systems.
101 for (File root: File.listRoots()) {
102 File [] files = root.listFiles();
103 if (files == null) {
104 log.warn("cannot list '" + root);
105 continue;
106 }
107 for (File candidate: files) {
108 if (candidate.getName().equalsIgnoreCase(f)) {
109 curr = new File(root, candidate.getName());
110 continue OUTER;
111 }
112 }
113 }
114 break;
115 }
116 else {
117 File [] files = curr.listFiles();
118 if (files == null) {
119 log.warn("cannot list: '" + curr + "'");
120 return file;
121 }
122 for (File candidate: files) {
123 if (candidate.getName().equalsIgnoreCase(f)) {
124 curr = new File(curr, candidate.getName());
125 continue OUTER;
126 }
127 }
128 curr = null;
129 break;
130 }
131 }
132
133 if (curr == null) {
134 log.warn("cannot repair path '" + file + "'");
135 return file;
136 }
137
138 return curr;
139 }
140
141 /** Object that can calculate hash of file, compare two hashed files etc. */
142 public static class HashedFile
143 implements Comparable<HashedFile>
144 {
145 protected File file;
146 protected long length;
147 protected byte [] hash;
148
149 public HashedFile(File file) {
150 this.file = file;
151 length = file.length();
152 }
153
154 public File getFile() {
155 return file;
156 }
157
158 protected byte [] getHash() {
159 if (hash == null) {
160 InputStream in = null;
161
162 try {
163 in = new FileInputStream(file);
164
165 MessageDigest digest = MessageDigest.getInstance(DIGEST);
166
167 byte [] buf = new byte[40*1024];
168 int r;
169
170 while ((r = in.read(buf)) >= 0) {
171 digest.update(buf, 0, r);
172 }
173
174 hash = digest.digest();
175 }
176 catch (IOException ioe) {
177 log.error(ioe);
178 hash = new byte[0];
179 }
180 catch (NoSuchAlgorithmException nsae) {
181 log.error(nsae);
182 hash = new byte[0];
183 }
184 finally {
185 if (in != null) {
186 try {
187 in.close();
188 }
189 catch (IOException ioe) {
190 log.error(ioe);
191 }
192 }
193 }
194 }
195 return hash;
196 }
197
198 @Override
199 public int compareTo(HashedFile other) {
200 if (length < other.length) return -1;
201 if (length > other.length) return +1;
202 return compare(getHash(), other.getHash());
203 }
204
205 private static int compare(byte [] a, byte [] b) {
206 if (a.length < b.length) return -1;
207 if (a.length > b.length) return +1;
208 for (int i = 0; i < a.length; ++i) {
209 int x = a[i] & 0xff;
210 int y = b[i] & 0xff;
211 if (x < y) return -1;
212 if (x > y) return +1;
213 }
214 return 0;
215 }
216
217 @Override
218 public boolean equals(Object other) {
219 return other instanceof HashedFile
220 && ((HashedFile)other).compareTo(this) == 0;
221 }
222
223 @Override
224 public int hashCode() {
225 return (int)(length ^ (length >>> 32));
226 }
227 } // class HashedFile
228
229 public static List<File> uniqueFiles(List<File> files) {
230
231 Set<HashedFile> set = new HashSet<HashedFile>();
232
233 for (File file: files) {
234 if (!set.add(new HashedFile(file))) {
235 log.warn("file '" + file + "' is a duplicate.");
236 }
237 }
238
239 ArrayList<File> out = new ArrayList<File>(set.size());
240 for (HashedFile hf: set) {
241 out.add(hf.file);
242 }
243
244 return out;
245 }
246
247 public interface FileVisitor {
248 boolean visit(File file);
249 } // Visitor
250
251 public static void walkTree(File root, FileVisitor visitor) {
252
253 Deque<File> stack = new ArrayDeque<File>();
254
255 stack.push(root);
256
257 while (!stack.isEmpty()) {
258 File current = stack.pop();
259 if (!visitor.visit(current)) break;
260 if (current.isDirectory()) {
261 File [] subs = current.listFiles();
262 if (subs != null) {
263 for (File f: subs) {
264 stack.push(f);
265 }
266 }
267 }
268 }
269 }
270
271 /**
272 * Deletes everything in a directory.
273 *
274 * @param dir The directory.
275 */
276 public final static void deleteContent(File dir) {
277 if (dir == null || !dir.isDirectory()) {
278 return;
279 }
280
281 File[] files = dir.listFiles();
282 if (files != null) {
283 for (File file: files) {
284 deleteRecursive(file);
285 }
286 }
287
288 return;
289 }
290
291 /**
292 * Delete <i>file</i> and everything in <i>file</i> if it is a directory.
293 *
294 * @param file The file or directory.
295 * @return true, if deletion was successful - otherwise false.
296 */
297 public final static boolean deleteRecursive(File file) {
298
299 if (file == null) {
300 return false;
301 }
302
303 if (file.isDirectory()) {
304 File [] files = file.listFiles();
305 if (files != null) {
306 for (File sub: files) {
307 if (!deleteRecursive(sub)) {
308 return false;
309 }
310 }
311 }
312 }
313
314 return file.delete();
315 }
316
317 /**
318 * Put the given file or directory into a zip archive.
319 *
320 * @param file The file or directory.
321 * @param outputStream The stream to write the archive to.
322 * @throws IOException if an error occured while zip creation or writing to
323 * output stream.
324 */
325 public static void createZipArchive(
326 File file,
327 OutputStream outputStream
328 )
329 throws IOException
330 {
331 ZipOutputStream out = new ZipOutputStream(outputStream);
332
333 if (file.isFile()) {
334 copyFileToZip("", file, out);
335 }
336 else if (file.isDirectory()) {
337
338 Deque<PrefixDir> stack = new ArrayDeque<PrefixDir>();
339 stack.push(new PrefixDir(file.getName() + "/", file));
340
341 while (!stack.isEmpty()) {
342 PrefixDir pd = stack.pop();
343
344 ZipEntry dirEntry = new ZipEntry(pd.prefix);
345 out.putNextEntry(dirEntry);
346 out.closeEntry();
347
348 File [] files = pd.dir.listFiles();
349 if (files != null) {
350 for (File sub: files) {
351 if (sub.isDirectory()) {
352 stack.push(new PrefixDir(
353 pd.prefix + sub.getName() + "/",
354 sub));
355 }
356 else if (sub.isFile()) {
357 copyFileToZip(pd.prefix, sub, out);
358 }
359 }
360 }
361 }
362 }
363
364 out.finish();
365 }
366
367
368 public static void extractArchive(File archive, File destDir)
369 throws IOException {
370 if (!destDir.exists()) {
371 destDir.mkdir();
372 }
373
374 ZipFile zipFile = new ZipFile(archive);
375 try {
376 Enumeration<? extends ZipEntry> entries = zipFile.entries();
377
378 byte [] buffer = new byte[16384];
379
380 while (entries.hasMoreElements()) {
381 ZipEntry entry = entries.nextElement();
382
383 String entryFileName = entry.getName();
384
385 File dir = buildDirectoryHierarchyFor(entryFileName, destDir);
386 if (!dir.exists()) {
387 dir.mkdirs();
388 }
389
390 if (!entry.isDirectory()) {
391 BufferedInputStream bis = new BufferedInputStream(
392 zipFile.getInputStream(entry));
393 try {
394 BufferedOutputStream bos = new BufferedOutputStream(
395 new FileOutputStream(new File(destDir, entryFileName)));
396
397 try {
398 int len;
399 while ((len = bis.read(buffer)) > 0) {
400 bos.write(buffer, 0, len);
401 }
402 bos.flush();
403 }
404 finally {
405 bos.close();
406 }
407 }
408 finally {
409 bis.close();
410 }
411 } // is file
412 }
413 }
414 finally {
415 zipFile.close();
416 }
417 }
418
419 private static File buildDirectoryHierarchyFor(
420 String entryName,
421 File destDir)
422 {
423 int lastIndex = entryName.lastIndexOf('/');
424 String internalPathToEntry = entryName.substring(0, lastIndex + 1);
425 return new File(destDir, internalPathToEntry);
426 }
427
428 /**
429 * A class representing a directory with a prefix.
430 */
431 private static final class PrefixDir {
432
433 String prefix;
434 File dir;
435
436 public PrefixDir(String prefix, File dir) {
437 this.prefix = prefix;
438 this.dir = dir;
439 }
440
441 } // class PrefixDir
442
443 /**
444 * Write a file to zip archive.
445 *
446 * @param prefix A prefix.
447 * @param file The file.
448 * @param out The output stream.
449 * @throws IOException if an error occured while writing to zip output
450 * stream.
451 */
452 private static void copyFileToZip(
453 String prefix,
454 File file,
455 ZipOutputStream out
456 )
457 throws IOException
458 {
459 String entryName = prefix + file.getName();
460 ZipEntry entry = new ZipEntry(entryName);
461 out.putNextEntry(entry);
462 InputStream in = null;
463 try {
464 in =
465 new BufferedInputStream(
466 new FileInputStream(file), 20*1024);
467
468 byte [] buf = new byte[2048];
469
470 int r;
471 while ((r = in.read(buf)) > 0) {
472 out.write(buf, 0, r);
473 }
474 }
475 finally {
476 if (in != null) {
477 try { in.close(); }
478 catch (IOException ioe) {}
479 }
480 }
481 out.closeEntry();
482 }
483
484
485 /**
486 * Copies a <i>src</i> file to <i>target</i>.
487 *
488 * @param src A file (not a directory) that should be copied.
489 * @param target The destination. This might be a file or a directory.
490 *
491 * @return true, if <i>src</i> has been successfully copied; otherwise
492 * false.
493 */
494 public static boolean copyFile(File src, File target)
495 throws IOException
496 {
497 if (src == null || !src.exists()) {
498 log.warn("Source file does not exist!");
499 return false;
500 }
501
502 if (!src.canRead()) {
503 log.warn("Cannot read Source file!");
504 return false;
505 }
506
507 if (src.isDirectory()) {
508 log.warn("Source is a directory!");
509 return false;
510 }
511
512 if (target.isDirectory()) {
513 target = new File(target, src.getName());
514 }
515
516 FileInputStream in = null;
517 FileOutputStream out = null;
518
519 try {
520 in = new FileInputStream(src);
521 out = new FileOutputStream(target);
522
523 FileChannel inChannel = in.getChannel();
524 FileChannel outChannel = out.getChannel();
525
526 inChannel.transferTo(0l, inChannel.size(), outChannel);
527
528 return true;
529 }
530 catch (IOException ioe) {
531 log.warn(ioe, ioe);
532 }
533 finally {
534 if (in != null) {
535 try {
536 in.close();
537 }
538 catch (IOException ioe) { /* do nothing here */ }
539 }
540
541 if (out != null) {
542 try {
543 out.close();
544 }
545 catch (IOException ioe) { /* do nothing here */ }
546 }
547 }
548
549 return false;
550 }
551
552
553 /**
554 * Copies a directory <i>source</i> to a destination path <i>dest</i>.
555 *
556 * @param source A directory that should be copied.
557 * @param dest A destination directory which is created if it is not
558 * existing yet.
559 *
560 * @return true, if the directory has been successfully copied; otherwise
561 * false.
562 */
563 public static boolean copyDirectory(final File source, final File dest) {
564 if (source == null || !source.exists()) {
565 log.warn("Source directory does not exist!");
566 return false;
567 }
568
569 if (!source.isDirectory()) {
570 log.warn("Source is not a directory!");
571 return false;
572 }
573
574 if (dest == null) {
575 log.warn("Destination directory is null!");
576 return false;
577 }
578
579 if (!dest.exists()) {
580 if (!dest.mkdir()) {
581 log.warn("Cannot create destination directory!");
582 return false;
583 }
584 }
585
586 File[] children = source.listFiles();
587 int failed = 0;
588
589 if (children != null && children.length > 0) {
590 for (File child: children) {
591 if (child.isFile()) {
592 try {
593 if (!copyFile(child, dest)) {
594 failed++;
595 }
596 }
597 catch (IOException ioe) {
598 log.warn(ioe, ioe);
599 failed++;
600 }
601 }
602 else if (child.isDirectory()) {
603 copyDirectory(child, new File(dest, child.getName()));
604 }
605 }
606 }
607
608 log.debug("Failed to copy " + failed + " files.");
609
610 return true;
611 }
612 }
613 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org