ingo@329: /* ingo@329: * Copyright (c) 2010, 2011 by Intevation GmbH ingo@329: * ingo@329: * This program is free software under the LGPL (>=v2.1) ingo@329: * Read the file LGPL.txt coming with the software for details ingo@329: * or visit http://www.gnu.org/licenses/ if it does not exist. ingo@329: */ ingo@329: package de.intevation.artifacts.common.utils; ingo@329: ingo@329: import java.io.BufferedInputStream; ingo@329: import java.io.File; ingo@329: import java.io.IOException; ingo@329: import java.io.InputStream; ingo@329: import java.io.FileInputStream; ingo@364: import java.io.FileOutputStream; ingo@329: import java.io.OutputStream; raimund@383: import java.io.BufferedOutputStream; ingo@364: import java.nio.channels.FileChannel; ingo@329: sascha@369: import java.util.Deque; sascha@369: import java.util.ArrayDeque; ingo@329: import java.util.List; ingo@329: import java.util.Set; ingo@329: import java.util.HashSet; ingo@329: import java.util.ArrayList; raimund@383: import java.util.Enumeration; raimund@383: import java.util.zip.ZipFile; ingo@329: import java.util.zip.ZipEntry; ingo@329: import java.util.zip.ZipOutputStream; ingo@329: ingo@329: import org.apache.log4j.Logger; ingo@329: ingo@329: import java.security.MessageDigest; ingo@329: import java.security.NoSuchAlgorithmException; ingo@329: ingo@329: public class FileTools ingo@329: { ingo@329: private static Logger log = Logger.getLogger(FileTools.class); ingo@329: ingo@329: public static final String DIGEST = ingo@329: System.getProperty("artifacts.common.file.cmp.digest", "MD5"); ingo@329: ingo@329: private FileTools() { ingo@329: } ingo@329: ingo@329: felix@452: /** Remove everything after dot from name. */ felix@452: public static final String removeExtension(String name) { felix@452: int index = name.lastIndexOf('.'); felix@452: return index == -1 felix@452: ? name felix@452: : name.substring(0, index); felix@452: } felix@452: felix@452: ingo@329: public static File getDirectory(String path, String name) { ingo@329: if (path == null || name == null) { ingo@329: return null; ingo@329: } ingo@329: ingo@329: File dir = new File(path, name); ingo@329: ingo@329: if (!dir.exists()) { ingo@329: log.debug( ingo@329: "Directory '" + dir.getAbsolutePath() + "' doesn't " + ingo@329: "exist. Try to create it."); ingo@329: ingo@329: return dir.mkdir() ? dir : null; ingo@329: } ingo@329: else { ingo@329: return dir.isDirectory() ? dir : null; ingo@329: } ingo@329: } ingo@329: ingo@329: public static File repair(File file) { ingo@329: file = file.getAbsoluteFile(); ingo@329: if (file.exists()) { ingo@329: return file; ingo@329: } sascha@369: Deque parts = new ArrayDeque(); ingo@329: File curr = file; ingo@329: while (curr != null) { ingo@329: String name = curr.getName(); ingo@329: if (name.length() > 0) { ingo@329: parts.push(curr.getName()); ingo@329: } ingo@329: curr = curr.getParentFile(); ingo@329: } ingo@329: ingo@329: curr = null; ingo@329: OUTER: while (!parts.isEmpty()) { ingo@329: String f = parts.pop(); aheinecke@457: log.debug("fixing: '" + f + "'"); aheinecke@456: if (f.equals(".") || f.equals("..")) { aheinecke@456: // No need to fix . or .. aheinecke@456: continue; aheinecke@456: } ingo@329: if (curr == null) { ingo@329: // XXX: Not totaly correct because there ingo@329: // more than one root on none unix systems. ingo@329: for (File root: File.listRoots()) { ingo@329: File [] files = root.listFiles(); ingo@329: if (files == null) { ingo@329: log.warn("cannot list '" + root); ingo@329: continue; ingo@329: } ingo@329: for (File candidate: files) { ingo@329: if (candidate.getName().equalsIgnoreCase(f)) { ingo@329: curr = new File(root, candidate.getName()); ingo@329: continue OUTER; ingo@329: } ingo@329: } ingo@329: } ingo@329: break; ingo@329: } ingo@329: else { ingo@329: File [] files = curr.listFiles(); ingo@329: if (files == null) { ingo@329: log.warn("cannot list: '" + curr + "'"); ingo@329: return file; ingo@329: } ingo@329: for (File candidate: files) { ingo@329: if (candidate.getName().equalsIgnoreCase(f)) { ingo@329: curr = new File(curr, candidate.getName()); ingo@329: continue OUTER; ingo@329: } ingo@329: } ingo@329: curr = null; ingo@329: break; ingo@329: } ingo@329: } ingo@329: ingo@329: if (curr == null) { ingo@329: log.warn("cannot repair path '" + file + "'"); ingo@329: return file; ingo@329: } ingo@329: ingo@329: return curr; ingo@329: } ingo@329: felix@453: /** Object that can calculate hash of file, compare two hashed files etc. */ sascha@394: public static class HashedFile ingo@329: implements Comparable ingo@329: { ingo@329: protected File file; ingo@329: protected long length; ingo@329: protected byte [] hash; ingo@329: ingo@329: public HashedFile(File file) { ingo@329: this.file = file; ingo@329: length = file.length(); ingo@329: } ingo@329: ingo@329: public File getFile() { ingo@329: return file; ingo@329: } ingo@329: ingo@329: protected byte [] getHash() { ingo@329: if (hash == null) { ingo@329: InputStream in = null; ingo@329: ingo@329: try { ingo@329: in = new FileInputStream(file); ingo@329: ingo@329: MessageDigest digest = MessageDigest.getInstance(DIGEST); ingo@329: ingo@329: byte [] buf = new byte[40*1024]; ingo@329: int r; ingo@329: ingo@329: while ((r = in.read(buf)) >= 0) { ingo@329: digest.update(buf, 0, r); ingo@329: } ingo@329: ingo@329: hash = digest.digest(); ingo@329: } ingo@329: catch (IOException ioe) { ingo@329: log.error(ioe); ingo@329: hash = new byte[0]; ingo@329: } ingo@329: catch (NoSuchAlgorithmException nsae) { ingo@329: log.error(nsae); ingo@329: hash = new byte[0]; ingo@329: } ingo@329: finally { ingo@329: if (in != null) { ingo@329: try { ingo@329: in.close(); ingo@329: } ingo@329: catch (IOException ioe) { ingo@329: log.error(ioe); ingo@329: } ingo@329: } ingo@329: } ingo@329: } ingo@329: return hash; ingo@329: } ingo@329: ingo@329: @Override ingo@329: public int compareTo(HashedFile other) { ingo@329: if (length < other.length) return -1; ingo@329: if (length > other.length) return +1; ingo@329: return compare(getHash(), other.getHash()); ingo@329: } ingo@329: ingo@329: private static int compare(byte [] a, byte [] b) { ingo@329: if (a.length < b.length) return -1; ingo@329: if (a.length > b.length) return +1; ingo@329: for (int i = 0; i < a.length; ++i) { ingo@329: int x = a[i] & 0xff; ingo@329: int y = b[i] & 0xff; ingo@329: if (x < y) return -1; ingo@329: if (x > y) return +1; ingo@329: } ingo@329: return 0; ingo@329: } ingo@329: ingo@329: @Override ingo@329: public boolean equals(Object other) { sascha@394: return other instanceof HashedFile ingo@329: && ((HashedFile)other).compareTo(this) == 0; ingo@329: } ingo@329: ingo@329: @Override ingo@329: public int hashCode() { ingo@329: return (int)(length ^ (length >>> 32)); ingo@329: } ingo@329: } // class HashedFile ingo@329: ingo@329: public static List uniqueFiles(List files) { ingo@329: ingo@329: Set set = new HashSet(); ingo@329: ingo@329: for (File file: files) { ingo@329: if (!set.add(new HashedFile(file))) { ingo@329: log.warn("file '" + file + "' is a duplicate."); ingo@329: } ingo@329: } ingo@329: ingo@329: ArrayList out = new ArrayList(set.size()); ingo@329: for (HashedFile hf: set) { ingo@329: out.add(hf.file); ingo@329: } ingo@329: ingo@329: return out; ingo@329: } ingo@329: ingo@329: public interface FileVisitor { ingo@329: boolean visit(File file); ingo@329: } // Visitor ingo@329: ingo@329: public static void walkTree(File root, FileVisitor visitor) { ingo@329: sascha@369: Deque stack = new ArrayDeque(); ingo@329: ingo@329: stack.push(root); ingo@329: ingo@329: while (!stack.isEmpty()) { ingo@329: File current = stack.pop(); ingo@329: if (!visitor.visit(current)) break; ingo@329: if (current.isDirectory()) { ingo@329: File [] subs = current.listFiles(); ingo@329: if (subs != null) { ingo@329: for (File f: subs) { ingo@329: stack.push(f); ingo@329: } ingo@329: } ingo@329: } ingo@329: } ingo@329: } ingo@329: ingo@329: /** ingo@329: * Deletes everything in a directory. ingo@329: * ingo@329: * @param dir The directory. ingo@329: */ ingo@329: public final static void deleteContent(File dir) { ingo@329: if (dir == null || !dir.isDirectory()) { ingo@329: return; ingo@329: } ingo@329: ingo@329: File[] files = dir.listFiles(); ingo@329: if (files != null) { ingo@329: for (File file: files) { ingo@329: deleteRecursive(file); ingo@329: } ingo@329: } ingo@329: ingo@329: return; ingo@329: } ingo@329: ingo@329: /** ingo@329: * Delete file and everything in file if it is a directory. ingo@329: * ingo@329: * @param file The file or directory. ingo@329: * @return true, if deletion was successful - otherwise false. ingo@329: */ ingo@329: public final static boolean deleteRecursive(File file) { ingo@329: ingo@329: if (file == null) { ingo@329: return false; ingo@329: } ingo@329: ingo@329: if (file.isDirectory()) { ingo@329: File [] files = file.listFiles(); ingo@329: if (files != null) { ingo@329: for (File sub: files) { ingo@329: if (!deleteRecursive(sub)) { ingo@329: return false; ingo@329: } ingo@329: } ingo@329: } ingo@329: } ingo@329: ingo@329: return file.delete(); ingo@329: } ingo@329: ingo@329: /** ingo@329: * Put the given file or directory into a zip archive. ingo@329: * ingo@329: * @param file The file or directory. ingo@329: * @param outputStream The stream to write the archive to. ingo@329: * @throws IOException if an error occured while zip creation or writing to ingo@329: * output stream. ingo@329: */ ingo@329: public static void createZipArchive( ingo@329: File file, ingo@329: OutputStream outputStream ingo@329: ) ingo@329: throws IOException ingo@329: { ingo@329: ZipOutputStream out = new ZipOutputStream(outputStream); ingo@329: ingo@329: if (file.isFile()) { ingo@329: copyFileToZip("", file, out); ingo@329: } ingo@329: else if (file.isDirectory()) { ingo@329: sascha@369: Deque stack = new ArrayDeque(); ingo@329: stack.push(new PrefixDir(file.getName() + "/", file)); ingo@329: ingo@329: while (!stack.isEmpty()) { ingo@329: PrefixDir pd = stack.pop(); ingo@329: ingo@329: ZipEntry dirEntry = new ZipEntry(pd.prefix); ingo@329: out.putNextEntry(dirEntry); ingo@329: out.closeEntry(); ingo@329: ingo@329: File [] files = pd.dir.listFiles(); ingo@329: if (files != null) { ingo@329: for (File sub: files) { ingo@329: if (sub.isDirectory()) { ingo@329: stack.push(new PrefixDir( ingo@329: pd.prefix + sub.getName() + "/", ingo@329: sub)); ingo@329: } ingo@329: else if (sub.isFile()) { ingo@329: copyFileToZip(pd.prefix, sub, out); ingo@329: } ingo@329: } ingo@329: } ingo@329: } ingo@329: } ingo@329: ingo@329: out.finish(); ingo@329: } ingo@329: raimund@383: raimund@383: public static void extractArchive(File archive, File destDir) raimund@383: throws IOException { raimund@383: if (!destDir.exists()) { raimund@383: destDir.mkdir(); raimund@383: } raimund@383: raimund@383: ZipFile zipFile = new ZipFile(archive); teichmann@435: try { teichmann@435: Enumeration entries = zipFile.entries(); raimund@383: teichmann@435: byte [] buffer = new byte[16384]; raimund@383: teichmann@435: while (entries.hasMoreElements()) { teichmann@435: ZipEntry entry = entries.nextElement(); raimund@383: teichmann@435: String entryFileName = entry.getName(); raimund@383: teichmann@435: File dir = buildDirectoryHierarchyFor(entryFileName, destDir); teichmann@435: if (!dir.exists()) { teichmann@435: dir.mkdirs(); raimund@383: } raimund@383: teichmann@435: if (!entry.isDirectory()) { teichmann@435: BufferedInputStream bis = new BufferedInputStream( teichmann@435: zipFile.getInputStream(entry)); teichmann@435: try { teichmann@435: BufferedOutputStream bos = new BufferedOutputStream( teichmann@435: new FileOutputStream(new File(destDir, entryFileName))); teichmann@435: teichmann@435: try { teichmann@435: int len; teichmann@435: while ((len = bis.read(buffer)) > 0) { teichmann@435: bos.write(buffer, 0, len); teichmann@435: } teichmann@435: bos.flush(); teichmann@435: } teichmann@435: finally { teichmann@435: bos.close(); teichmann@435: } teichmann@435: } teichmann@435: finally { teichmann@435: bis.close(); teichmann@435: } teichmann@435: } // is file raimund@383: } raimund@383: } teichmann@435: finally { teichmann@435: zipFile.close(); teichmann@435: } raimund@383: } raimund@383: raimund@383: private static File buildDirectoryHierarchyFor( raimund@383: String entryName, raimund@383: File destDir) raimund@383: { raimund@383: int lastIndex = entryName.lastIndexOf('/'); raimund@383: String internalPathToEntry = entryName.substring(0, lastIndex + 1); raimund@383: return new File(destDir, internalPathToEntry); raimund@383: } raimund@383: ingo@329: /** ingo@329: * A class representing a directory with a prefix. ingo@329: */ ingo@329: private static final class PrefixDir { ingo@329: ingo@329: String prefix; ingo@329: File dir; ingo@329: ingo@329: public PrefixDir(String prefix, File dir) { ingo@329: this.prefix = prefix; ingo@329: this.dir = dir; ingo@329: } ingo@329: ingo@329: } // class PrefixDir ingo@329: ingo@329: /** ingo@329: * Write a file to zip archive. ingo@329: * ingo@329: * @param prefix A prefix. ingo@329: * @param file The file. ingo@329: * @param out The output stream. ingo@329: * @throws IOException if an error occured while writing to zip output ingo@329: * stream. ingo@329: */ ingo@329: private static void copyFileToZip( ingo@329: String prefix, ingo@329: File file, ingo@329: ZipOutputStream out ingo@329: ) ingo@329: throws IOException ingo@329: { ingo@329: String entryName = prefix + file.getName(); ingo@329: ZipEntry entry = new ZipEntry(entryName); ingo@329: out.putNextEntry(entry); ingo@329: InputStream in = null; ingo@329: try { ingo@329: in = ingo@329: new BufferedInputStream( ingo@329: new FileInputStream(file), 20*1024); ingo@329: ingo@329: byte [] buf = new byte[2048]; ingo@329: ingo@329: int r; ingo@329: while ((r = in.read(buf)) > 0) { ingo@329: out.write(buf, 0, r); ingo@329: } ingo@329: } ingo@329: finally { ingo@329: if (in != null) { ingo@329: try { in.close(); } ingo@329: catch (IOException ioe) {} ingo@329: } ingo@329: } ingo@329: out.closeEntry(); ingo@329: } ingo@364: ingo@364: ingo@364: /** ingo@364: * Copies a src file to target. ingo@364: * ingo@364: * @param src A file (not a directory) that should be copied. ingo@364: * @param target The destination. This might be a file or a directory. ingo@364: * ingo@364: * @return true, if src has been successfully copied; otherwise ingo@364: * false. ingo@364: */ ingo@364: public static boolean copyFile(File src, File target) ingo@364: throws IOException ingo@364: { ingo@364: if (src == null || !src.exists()) { ingo@364: log.warn("Source file does not exist!"); ingo@364: return false; ingo@364: } ingo@364: ingo@364: if (!src.canRead()) { ingo@364: log.warn("Cannot read Source file!"); ingo@364: return false; ingo@364: } ingo@364: ingo@364: if (src.isDirectory()) { ingo@364: log.warn("Source is a directory!"); ingo@364: return false; ingo@364: } ingo@364: ingo@364: if (target.isDirectory()) { ingo@364: target = new File(target, src.getName()); ingo@364: } ingo@364: ingo@364: FileInputStream in = null; ingo@364: FileOutputStream out = null; ingo@364: ingo@364: try { ingo@364: in = new FileInputStream(src); ingo@364: out = new FileOutputStream(target); ingo@364: ingo@364: FileChannel inChannel = in.getChannel(); ingo@364: FileChannel outChannel = out.getChannel(); ingo@364: ingo@364: inChannel.transferTo(0l, inChannel.size(), outChannel); ingo@364: ingo@364: return true; ingo@364: } ingo@364: catch (IOException ioe) { ingo@364: log.warn(ioe, ioe); ingo@364: } ingo@364: finally { ingo@364: if (in != null) { ingo@364: try { ingo@364: in.close(); ingo@364: } ingo@364: catch (IOException ioe) { /* do nothing here */ } ingo@364: } ingo@364: ingo@364: if (out != null) { ingo@364: try { ingo@364: out.close(); ingo@364: } ingo@364: catch (IOException ioe) { /* do nothing here */ } ingo@364: } ingo@364: } ingo@364: ingo@364: return false; ingo@364: } ingo@364: ingo@364: ingo@364: /** ingo@364: * Copies a directory source to a destination path dest. ingo@364: * ingo@364: * @param source A directory that should be copied. ingo@364: * @param dest A destination directory which is created if it is not ingo@364: * existing yet. ingo@364: * ingo@364: * @return true, if the directory has been successfully copied; otherwise ingo@364: * false. ingo@364: */ ingo@364: public static boolean copyDirectory(final File source, final File dest) { ingo@364: if (source == null || !source.exists()) { ingo@364: log.warn("Source directory does not exist!"); ingo@364: return false; ingo@364: } ingo@364: ingo@364: if (!source.isDirectory()) { ingo@364: log.warn("Source is not a directory!"); ingo@364: return false; ingo@364: } ingo@364: ingo@364: if (dest == null) { ingo@364: log.warn("Destination directory is null!"); ingo@364: return false; ingo@364: } ingo@364: ingo@364: if (!dest.exists()) { ingo@364: if (!dest.mkdir()) { ingo@364: log.warn("Cannot create destination directory!"); ingo@364: return false; ingo@364: } ingo@364: } ingo@364: ingo@364: File[] children = source.listFiles(); ingo@364: int failed = 0; ingo@364: ingo@364: if (children != null && children.length > 0) { ingo@364: for (File child: children) { ingo@364: if (child.isFile()) { ingo@364: try { ingo@364: if (!copyFile(child, dest)) { ingo@364: failed++; ingo@364: } ingo@364: } ingo@364: catch (IOException ioe) { ingo@364: log.warn(ioe, ioe); ingo@364: failed++; ingo@364: } ingo@364: } ingo@364: else if (child.isDirectory()) { ingo@364: copyDirectory(child, new File(dest, child.getName())); ingo@364: } ingo@364: } ingo@364: } ingo@364: ingo@364: log.debug("Failed to copy " + failed + " files."); ingo@364: ingo@364: return true; ingo@364: } ingo@329: } ingo@329: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :