/** * Copyright (c) 2011 Cloudsmith Inc. and other contributors, as listed below. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Cloudsmith * */ package org.cloudsmith.geppetto.common.os; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Calendar; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class FileUtils { public static final Pattern DEFAULT_EXCLUDES = Pattern.compile("^[\\.~#].*$"); private static final Method Files_copy; private static final Method Files_isSymbolicLink; private static final Method Files_readSymbolicLink; private static final Method File_toPath; private static Object[] defaultCopyOptions; static { Method isSymbolicLink = null; Method readSymbolicLink = null; Method toPath = null; Method copy = null; Enum<?> option_COPY_ATTRIBUTES = null; Enum<?> option_NOFOLLOW_LINKS = null; Object[] dfltCopyOptions = null; try { Class<?> class_Files = Class.forName("java.nio.file.Files"); Class<?> class_Path = Class.forName("java.nio.file.Path"); Class<?> class_CopyOption = Class.forName("java.nio.file.CopyOption"); Class<?> class_LinkOption = Class.forName("java.nio.file.LinkOption"); Class<?> class_StandardCopyOption = Class.forName("java.nio.file.StandardCopyOption"); isSymbolicLink = class_Files.getMethod("isSymbolicLink", class_Path); readSymbolicLink = class_Files.getMethod("readSymbolicLink", class_Path); toPath = File.class.getMethod("toPath"); for(Object e : class_LinkOption.getEnumConstants()) { Enum<?> en = (Enum<?>) e; if("NOFOLLOW_LINKS".equals(en.name())) option_NOFOLLOW_LINKS = en; } for(Object e : class_StandardCopyOption.getEnumConstants()) { Enum<?> en = (Enum<?>) e; if("COPY_ATTRIBUTES".equals(en.name())) option_COPY_ATTRIBUTES = en; } dfltCopyOptions = (Object[]) Array.newInstance(class_CopyOption, 2); dfltCopyOptions[0] = option_COPY_ATTRIBUTES; dfltCopyOptions[1] = option_NOFOLLOW_LINKS; copy = class_Files.getMethod("copy", class_Path, class_Path, Class.forName("[Ljava.nio.file.CopyOption;")); } catch(Exception e) { } Files_isSymbolicLink = isSymbolicLink; Files_readSymbolicLink = readSymbolicLink; File_toPath = toPath; Files_copy = copy; defaultCopyOptions = dfltCopyOptions; } public static void cp(File source, File destDir, String fileName) throws IOException { cp(source, destDir, fileName, isSymlink(source)); } private static void cp(File source, File destDir, String fileName, boolean isSymlink) throws IOException { if(Files_copy != null) { File destFile = new File(destDir, fileName); try { // Use Files.copy() to preserve attributes and links. Files_copy.invoke(null, File_toPath.invoke(source), File_toPath.invoke(destFile), defaultCopyOptions); return; } catch(InvocationTargetException e) { Throwable t = e.getTargetException(); if(t instanceof IOException) throw (IOException) t; if(t instanceof RuntimeException) throw (RuntimeException) t; throw new RuntimeException(t); } catch(IllegalAccessException e) { throw new RuntimeException(e); } } if(isSymlink) { // Ouch! We're asked to copy a symlink but we don't // have access to Java 1.7. We'll have to try and copy // this using a remote execution of 'cp' (unless it's // windows in which case we're lost). // if(OsUtil.unixCopy(source, destDir)) return; throw new UnsupportedOperationException( "Sorry. Not possible to copy symlinks on Windows. This is the link: '" + source.getAbsolutePath() + '\''); } InputStream in = new FileInputStream(source); try { cp(in, destDir, fileName); if(source.canExecute()) new File(destDir, fileName).setExecutable(true, false); } finally { StreamUtil.close(in); } } public static void cp(InputStream source, File destDir, String fileName) throws IOException { cp(source, destDir, fileName, -1); } public static void cp(InputStream source, File destDir, String fileName, long timestamp) throws IOException { File target = new File(destDir, fileName); OutputStream out = new FileOutputStream(target); try { StreamUtil.copy(source, out); if(timestamp > 0) target.setLastModified(timestamp); } finally { StreamUtil.close(out); } } public static void cpR(File source, File destDir, FileFilter fileFilter, boolean createTop, boolean includeEmptyFolders) throws IOException { String name = source.getName(); boolean isSymlink = isSymlink(source); File[] children = isSymlink ? null : source.listFiles(fileFilter); if(children == null) { // File or symlink. File destFile = new File(destDir, name); if(destFile.exists()) throw new IOException(destFile.getAbsolutePath() + " already exists"); cp(source, destDir, name, isSymlink); return; } if(children.length == 0) { if(!includeEmptyFolders) return; } if(createTop) { destDir = new File(destDir, name); } if(!(destDir.mkdir() || destDir.isDirectory())) throw new IOException("Unable to create directory " + destDir.getAbsolutePath()); for(File child : children) cpR(child, destDir, fileFilter, true, includeEmptyFolders); } /** * @param file * The file to examine * @return <code>true</code> if the <code>file</code> represents a symbolic link */ public static boolean isSymlink(File file) { if(file == null) return false; try { // Use Java 7 Files.isSymbolicLink(Path) if it is available // if(Files_isSymbolicLink != null) return ((Boolean) Files_isSymbolicLink.invoke(null, File_toPath.invoke(file))).booleanValue(); File canon; if(file.getParent() == null) canon = file; else { File canonDir = file.getParentFile().getCanonicalFile(); canon = new File(canonDir, file.getName()); } return !canon.getCanonicalFile().equals(canon.getAbsoluteFile()); } catch(Exception e) { return false; } } /** * @param file * The file that represents the symbolic link source * @return The symbolic link target or <code>null</code> if file does not represent a symbolic link */ public static String readSymbolicLink(File file) { if(file == null) return null; try { if(Files_readSymbolicLink != null) { Object target = Files_readSymbolicLink.invoke(null, File_toPath.invoke(file)); return target == null ? null : target.toString(); } } catch(Exception e) { } return null; } public static void rmR(File fileOrDir) { rmR(fileOrDir, null); } public static void rmR(File fileOrDir, Pattern excludePattern) { if(excludePattern != null && excludePattern.matcher(fileOrDir.getName()).matches()) return; File[] children = fileOrDir.listFiles(); if(children != null) for(File child : children) rmR(child, excludePattern); fileOrDir.delete(); } public static void unzip(File zipFile, File destDir) throws IOException { InputStream in = new FileInputStream(zipFile); try { unzip(in, destDir); } finally { StreamUtil.close(in); } } public static void unzip(InputStream inputs, File dest) throws IOException { if(!(dest.isDirectory() || dest.mkdirs())) throw new IOException("Not a directory: " + dest.getAbsolutePath()); ZipInputStream input = new ZipInputStream(inputs); ZipEntry entry; while((entry = input.getNextEntry()) != null) { String name = entry.getName(); if(entry.isDirectory()) { File destDir = new File(dest, name); if(!(destDir.isDirectory() || destDir.mkdir())) throw new IOException("Not a directory: " + destDir.getAbsolutePath()); continue; } long entryTime = entry.getTime(); // Fix entry time by taking the time zone into account entryTime += Calendar.getInstance().getTimeZone().getOffset(entryTime); cp(input, dest, name, entryTime); } } }