Logo Search packages:      
Sourcecode: aspectj version File versions

FileUtil.java

/* *******************************************************************
 * Copyright (c) 1999-2001 Xerox Corporation, 
 *               2002 Palo Alto Research Center, Incorporated (PARC).
 * 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: 
 *     Xerox/PARC     initial implementation 
 * ******************************************************************/

package org.aspectj.util;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * 
 */
public class FileUtil {
      /** default parent directory File when a file has a null parent */
      public static final File DEFAULT_PARENT = new File("."); // XXX user.dir?

      /** unmodifiable List of String source file suffixes (including leading ".") */
      public static final List SOURCE_SUFFIXES = Collections.unmodifiableList(Arrays.asList(new String[] { ".java", ".aj" }));

      public static final FileFilter ZIP_FILTER = new FileFilter() {
            public boolean accept(File file) {
                  return isZipFile(file);
            }

            public String toString() {
                  return "ZIP_FILTER";
            }
      };

      // public static final FileFilter SOURCE_FILTER = new FileFilter() {
      // public boolean accept(File file) {
      // return hasSourceSuffix(file);
      // }
      //
      // public String toString() {
      // return "SOURCE_FILTER";
      // }
      // };

      final static int[] INT_RA = new int[0];

      /** accept all files */
      public static final FileFilter ALL = new FileFilter() {
            public boolean accept(File f) {
                  return true;
            }
      };
      public static final FileFilter DIRS_AND_WRITABLE_CLASSES = new FileFilter() {
            public boolean accept(File file) {
                  return ((null != file) && (file.isDirectory() || (file.canWrite() && file.getName().toLowerCase().endsWith(".class"))));
            }
      };
      private static final boolean PERMIT_CVS;
      static {
            String name = FileUtil.class.getName() + ".PERMIT_CVS";
            PERMIT_CVS = LangUtil.getBoolean(name, false);
      }

      /** @return true if file exists and is a zip file */
      public static boolean isZipFile(File file) {
            try {
                  return (null != file) && new ZipFile(file) != null;
            } catch (IOException e) {
                  return false;
            }
      }

      /** @return true if path ends with .zip or .jar */
      // public static boolean hasZipSuffix(String path) {
      // return ((null != path) && (0 != zipSuffixLength(path)));
      // }
      /** @return 0 if file has no zip/jar suffix or 4 otherwise */
      public static int zipSuffixLength(File file) {
            return (null == file ? 0 : zipSuffixLength(file.getPath()));
      }

      /** @return 0 if no zip/jar suffix or 4 otherwise */
      public static int zipSuffixLength(String path) {
            if ((null != path) && (4 < path.length())) {
                  String test = path.substring(path.length() - 4).toLowerCase();
                  if (".zip".equals(test) || ".jar".equals(test)) {
                        return 4;
                  }
            }
            return 0;
      }

      /** @return true if file path has a source suffix */
      public static boolean hasSourceSuffix(File file) {
            return ((null != file) && hasSourceSuffix(file.getPath()));
      }

      /** @return true if path ends with .java or .aj */
      public static boolean hasSourceSuffix(String path) {
            return ((null != path) && (0 != sourceSuffixLength(path)));
      }

      /**
       * @return 0 if file has no source suffix or the length of the suffix
       *         otherwise
       */
      public static int sourceSuffixLength(File file) {
            return (null == file ? 0 : sourceSuffixLength(file.getPath()));
      }

      /** @return 0 if no source suffix or the length of the suffix otherwise */
      public static int sourceSuffixLength(String path) {
            if (LangUtil.isEmpty(path)) {
                  return 0;
            }

            for (Iterator iter = SOURCE_SUFFIXES.iterator(); iter.hasNext();) {
                  String suffix = (String) iter.next();
                  if (path.endsWith(suffix) || path.toLowerCase().endsWith(suffix)) {
                        return suffix.length();
                  }
            }
            return 0;
      }

      /** @return true if this is a readable directory */
      public static boolean canReadDir(File dir) {
            return ((null != dir) && dir.canRead() && dir.isDirectory());
      }

      /** @return true if this is a readable file */
      public static boolean canReadFile(File file) {
            return ((null != file) && file.canRead() && file.isFile());
      }

      /** @return true if dir is a writable directory */
      public static boolean canWriteDir(File dir) {
            return ((null != dir) && dir.canWrite() && dir.isDirectory());
      }

      /** @return true if this is a writable file */
      public static boolean canWriteFile(File file) {
            return ((null != file) && file.canWrite() && file.isFile());
      }

      // /**
      // * @throws IllegalArgumentException unless file is readable and not a
      // * directory
      // */
      // public static void throwIaxUnlessCanReadFile(File file, String label) {
      // if (!canReadFile(file)) {
      // throw new IllegalArgumentException(label + " not readable file: " +
      // file);
      // }
      // }

      /**
       * @throws IllegalArgumentException unless dir is a readable directory
       */
      public static void throwIaxUnlessCanReadDir(File dir, String label) {
            if (!canReadDir(dir)) {
                  throw new IllegalArgumentException(label + " not readable dir: " + dir);
            }
      }

      /**
       * @throws IllegalArgumentException unless file is readable and not a
       *             directory
       */
      public static void throwIaxUnlessCanWriteFile(File file, String label) {
            if (!canWriteFile(file)) {
                  throw new IllegalArgumentException(label + " not writable file: " + file);
            }
      }

      /** @throws IllegalArgumentException unless dir is a readable directory */
      public static void throwIaxUnlessCanWriteDir(File dir, String label) {
            if (!canWriteDir(dir)) {
                  throw new IllegalArgumentException(label + " not writable dir: " + dir);
            }
      }

      /** @return array same length as input, with String paths */
      public static String[] getPaths(File[] files) {
            if ((null == files) || (0 == files.length)) {
                  return new String[0];
            }
            String[] result = new String[files.length];
            for (int i = 0; i < result.length; i++) {
                  if (null != files[i]) {
                        result[i] = files[i].getPath();
                  }
            }
            return result;
      }

      /** @return array same length as input, with String paths */
      public static String[] getPaths(List files) {
            final int size = (null == files ? 0 : files.size());
            if (0 == size) {
                  return new String[0];
            }
            String[] result = new String[size];
            for (int i = 0; i < size; i++) {
                  File file = (File) files.get(i);
                  if (null != file) {
                        result[i] = file.getPath();
                  }
            }
            return result;
      }

      /**
       * Extract the name of a class from the path to its file. If the basedir is
       * null, then the class is assumed to be in the default package unless the
       * classFile has one of the top-level suffixes { com, org, java, javax } as
       * a parent directory.
       * 
       * @param basedir the File of the base directory (prefix of classFile)
       * @param classFile the File of the class to extract the name for
       * @throws IllegalArgumentException if classFile is null or does not end
       *             with ".class" or a non-null basedir is not a prefix of
       *             classFile
       */
      public static String fileToClassName(File basedir, File classFile) {
            LangUtil.throwIaxIfNull(classFile, "classFile");
            String classFilePath = normalizedPath(classFile);
            if (!classFilePath.endsWith(".class")) {
                  String m = classFile + " does not end with .class";
                  throw new IllegalArgumentException(m);
            }
            classFilePath = classFilePath.substring(0, classFilePath.length() - 6);
            if (null != basedir) {
                  String basePath = normalizedPath(basedir);
                  if (!classFilePath.startsWith(basePath)) {
                        String m = classFile + " does not start with " + basedir;
                        throw new IllegalArgumentException(m);
                  }
                  classFilePath = classFilePath.substring(basePath.length() + 1);
            } else {
                  final String[] suffixes = new String[] { "com", "org", "java", "javax" };
                  boolean found = false;
                  for (int i = 0; !found && (i < suffixes.length); i++) {
                        int loc = classFilePath.indexOf(suffixes[i] + "/");
                        if ((0 == loc) || ((-1 != loc) && ('/' == classFilePath.charAt(loc - 1)))) {
                              classFilePath = classFilePath.substring(loc);
                              found = true;
                        }
                  }
                  if (!found) {
                        int loc = classFilePath.lastIndexOf("/");
                        if (-1 != loc) { // treat as default package
                              classFilePath = classFilePath.substring(loc + 1);
                        }
                  }
            }
            return classFilePath.replace('/', '.');
      }

      /**
       * Normalize path for comparisons by rendering absolute, clipping basedir
       * prefix, trimming and changing '\\' to '/'
       * 
       * @param file the File with the path to normalize
       * @param basedir the File for the prefix of the file to normalize - ignored
       *            if null
       * @return "" if null or normalized path otherwise
       * @throws IllegalArgumentException if basedir is not a prefix of file
       */
      public static String normalizedPath(File file, File basedir) {
            String filePath = normalizedPath(file);
            if (null != basedir) {
                  String basePath = normalizedPath(basedir);
                  if (filePath.startsWith(basePath)) {
                        filePath = filePath.substring(basePath.length());
                        if (filePath.startsWith("/")) {
                              filePath = filePath.substring(1);
                        }
                  }
            }
            return filePath;
      }

      /**
       * Render a set of files to String as a path by getting absolute paths of
       * each and delimiting with infix.
       * 
       * @param files the File[] to flatten - may be null or empty
       * @param infix the String delimiter internally between entries (if null,
       *            then use File.pathSeparator). (alias to
       *            <code>flatten(getAbsolutePaths(files), infix)</code>
       * @return String with absolute paths to entries in order, delimited with
       *         infix
       */
      public static String flatten(File[] files, String infix) {
            if (LangUtil.isEmpty(files)) {
                  return "";
            }
            return flatten(getPaths(files), infix);
      }

      /**
       * Flatten File[] to String.
       * 
       * @param files the File[] of paths to flatten - null ignored
       * @param infix the String infix to use - null treated as File.pathSeparator
       */
      public static String flatten(String[] paths, String infix) {
            if (null == infix) {
                  infix = File.pathSeparator;
            }
            StringBuffer result = new StringBuffer();
            boolean first = true;
            for (int i = 0; i < paths.length; i++) {
                  String path = paths[i];
                  if (null == path) {
                        continue;
                  }
                  if (first) {
                        first = false;
                  } else {
                        result.append(infix);
                  }
                  result.append(path);
            }
            return result.toString();
      }

      /**
       * Normalize path for comparisons by rendering absolute trimming and
       * changing '\\' to '/'
       * 
       * @return "" if null or normalized path otherwise
       */
      public static String normalizedPath(File file) {
            return (null == file ? "" : weakNormalize(file.getAbsolutePath()));
      }

      /**
       * Weakly normalize path for comparisons by trimming and changing '\\' to
       * '/'
       */
      public static String weakNormalize(String path) {
            if (null != path) {
                  path = path.replace('\\', '/').trim();
            }
            return path;
      }

      /**
       * Get best File for the first-readable path in input paths, treating
       * entries prefixed "sp:" as system property keys. Safe to call in static
       * initializers.
       * 
       * @param paths the String[] of paths to check.
       * @return null if not found, or valid File otherwise
       */
      public static File getBestFile(String[] paths) {
            if (null == paths) {
                  return null;
            }
            File result = null;
            for (int i = 0; (null == result) && (i < paths.length); i++) {
                  String path = paths[i];
                  if (null == path) {
                        continue;
                  }
                  if (path.startsWith("sp:")) {
                        try {
                              path = System.getProperty(path.substring(3));
                        } catch (Throwable t) {
                              path = null;
                        }
                        if (null == path) {
                              continue;
                        }
                  }
                  try {
                        File f = new File(path);
                        if (f.exists() && f.canRead()) {
                              result = FileUtil.getBestFile(f);
                        }
                  } catch (Throwable t) {
                        // swallow
                  }
            }
            return result;
      }

      /**
       * Render as best file, canonical or absolute.
       * 
       * @param file the File to get the best File for (not null)
       * @return File of the best-available path
       * @throws IllegalArgumentException if file is null
       */
      public static File getBestFile(File file) {
            LangUtil.throwIaxIfNull(file, "file");
            if (file.exists()) {
                  try {
                        return file.getCanonicalFile();
                  } catch (IOException e) {
                        return file.getAbsoluteFile();
                  }
            } else {
                  return file;
            }
      }

      /**
       * Render as best path, canonical or absolute.
       * 
       * @param file the File to get the path for (not null)
       * @return String of the best-available path
       * @throws IllegalArgumentException if file is null
       */
      public static String getBestPath(File file) {
            LangUtil.throwIaxIfNull(file, "file");
            if (file.exists()) {
                  try {
                        return file.getCanonicalPath();
                  } catch (IOException e) {
                        return file.getAbsolutePath();
                  }
            } else {
                  return file.getPath();
            }
      }

      /** @return array same length as input, with String absolute paths */
      public static String[] getAbsolutePaths(File[] files) {
            if ((null == files) || (0 == files.length)) {
                  return new String[0];
            }
            String[] result = new String[files.length];
            for (int i = 0; i < result.length; i++) {
                  if (null != files[i]) {
                        result[i] = files[i].getAbsolutePath();
                  }
            }
            return result;
      }

      /**
       * Recursively delete the contents of dir, but not the dir itself
       * 
       * @return the total number of files deleted
       */
      public static int deleteContents(File dir) {
            return deleteContents(dir, ALL);
      }

      /**
       * Recursively delete some contents of dir, but not the dir itself. This
       * deletes any subdirectory which is empty after its files are deleted.
       * 
       * @return the total number of files deleted
       */
      public static int deleteContents(File dir, FileFilter filter) {
            return deleteContents(dir, filter, true);
      }

      /**
       * Recursively delete some contents of dir, but not the dir itself. If
       * deleteEmptyDirs is true, this deletes any subdirectory which is empty
       * after its files are deleted.
       * 
       * @param dir the File directory (if a file, the the file is deleted)
       * @return the total number of files deleted
       */
      public static int deleteContents(File dir, FileFilter filter, boolean deleteEmptyDirs) {
            if (null == dir) {
                  throw new IllegalArgumentException("null dir");
            }
            if ((!dir.exists()) || (!dir.canWrite())) {
                  return 0;
            }
            if (!dir.isDirectory()) {
                  dir.delete();
                  return 1;
            }
            String[] fromFiles = dir.list();
            int result = 0;
            for (int i = 0; i < fromFiles.length; i++) {
                  String string = fromFiles[i];
                  File file = new File(dir, string);
                  if ((null == filter) || filter.accept(file)) {
                        if (file.isDirectory()) {
                              result += deleteContents(file, filter, deleteEmptyDirs);
                              if (deleteEmptyDirs && (0 == file.list().length)) {
                                    file.delete();
                              }
                        } else {
                              /* boolean ret = */file.delete();
                              result++;
                        }
                  }
            }
            return result;
      }

      /**
       * Copy contents of fromDir into toDir
       * 
       * @param fromDir must exist and be readable
       * @param toDir must exist or be creatable and be writable
       * @return the total number of files copied
       */
      public static int copyDir(File fromDir, File toDir) throws IOException {
            return copyDir(fromDir, toDir, null, null);
      }

      /**
       * Recursively copy files in fromDir (with any fromSuffix) to toDir,
       * replacing fromSuffix with toSuffix if any. This silently ignores dirs and
       * files that are not readable but throw IOException for directories that
       * are not writable. This does not clean out the original contents of toDir.
       * (subdirectories are not renamed per directory rules)
       * 
       * @param fromSuffix select files with this suffix - select all if null or
       *            empty
       * @param toSuffix replace fromSuffix with toSuffix in the destination file
       *            name - ignored if null or empty, appended to name if
       *            fromSuffix is null or empty
       * @return the total number of files copied
       */
      public static int copyDir(File fromDir, File toDir, final String fromSuffix, String toSuffix) throws IOException {
            return copyDir(fromDir, toDir, fromSuffix, toSuffix, (FileFilter) null);
      }

      // /**
      // * Recursively copy files in fromDir (with any fromSuffix) to toDir,
      // * replacing fromSuffix with toSuffix if any, and adding the destination
      // * file to any collector. This silently ignores dirs and files that are
      // not
      // * readable but throw IOException for directories that are not writable.
      // * This does not clean out the original contents of toDir. (subdirectories
      // * are not renamed per directory rules) This calls any delegate
      // * FilenameFilter to collect any selected file.
      // *
      // * @param fromSuffix select files with this suffix - select all if null or
      // * empty
      // * @param toSuffix replace fromSuffix with toSuffix in the destination
      // file
      // * name - ignored if null or empty, appended to name if
      // * fromSuffix is null or empty
      // * @param collector the List sink for destination files - ignored if null
      // * @return the total number of files copied
      // */
      // public static int copyDir(File fromDir, File toDir, final String
      // fromSuffix, final String toSuffix, final List collector)
      // throws IOException {
      // // int before = collector.size();
      // if (null == collector) {
      // return copyDir(fromDir, toDir, fromSuffix, toSuffix);
      // } else {
      // FileFilter collect = new FileFilter() {
      // public boolean accept(File pathname) {
      // return collector.add(pathname);
      // }
      // };
      // return copyDir(fromDir, toDir, fromSuffix, toSuffix, collect);
      // }
      // }

      /**
       * Recursively copy files in fromDir (with any fromSuffix) to toDir,
       * replacing fromSuffix with toSuffix if any. This silently ignores dirs and
       * files that are not readable but throw IOException for directories that
       * are not writable. This does not clean out the original contents of toDir.
       * (subdirectories are not renamed per directory rules) This calls any
       * delegate FilenameFilter to collect any selected file.
       * 
       * @param fromSuffix select files with this suffix - select all if null or
       *            empty
       * @param toSuffix replace fromSuffix with toSuffix in the destination file
       *            name - ignored if null or empty, appended to name if
       *            fromSuffix is null or empty
       * @return the total number of files copied
       */
      public static int copyDir(File fromDir, File toDir, final String fromSuffix, final String toSuffix, final FileFilter delegate)
                  throws IOException {

            if ((null == fromDir) || (!fromDir.canRead())) {
                  return 0;
            }
            final boolean haveSuffix = ((null != fromSuffix) && (0 < fromSuffix.length()));
            final int slen = (!haveSuffix ? 0 : fromSuffix.length());

            if (!toDir.exists()) {
                  toDir.mkdirs();
            }
            final String[] fromFiles;
            if (!haveSuffix) {
                  fromFiles = fromDir.list();
            } else {
                  FilenameFilter filter = new FilenameFilter() {
                        public boolean accept(File dir, String name) {
                              return (new File(dir, name).isDirectory() || (name.endsWith(fromSuffix)));
                        }
                  };
                  fromFiles = fromDir.list(filter);
            }
            int result = 0;
            final int MAX = (null == fromFiles ? 0 : fromFiles.length);
            for (int i = 0; i < MAX; i++) {
                  String filename = fromFiles[i];
                  File fromFile = new File(fromDir, filename);
                  if (fromFile.canRead()) {
                        if (fromFile.isDirectory()) {
                              result += copyDir(fromFile, new File(toDir, filename), fromSuffix, toSuffix, delegate);
                        } else if (fromFile.isFile()) {
                              if (haveSuffix) {
                                    filename = filename.substring(0, filename.length() - slen);
                              }
                              if (null != toSuffix) {
                                    filename = filename + toSuffix;
                              }
                              File targetFile = new File(toDir, filename);
                              if ((null == delegate) || delegate.accept(targetFile)) {
                                    copyFile(fromFile, targetFile);
                              }
                              result++;
                        }
                  }
            }
            return result;
      }

      /**
       * Recursively list files in srcDir.
       * 
       * @return ArrayList with String paths of File under srcDir (relative to
       *         srcDir)
       */
      public static String[] listFiles(File srcDir) {
            ArrayList result = new ArrayList();
            if ((null != srcDir) && srcDir.canRead()) {
                  listFiles(srcDir, null, result);
            }
            return (String[]) result.toArray(new String[0]);
      }

      public static final FileFilter aspectjSourceFileFilter = new FileFilter() {
            public boolean accept(File pathname) {
                  String name = pathname.getName().toLowerCase();
                  return name.endsWith(".java") || name.endsWith(".aj");
            }
      };

      /**
       * Recursively list files in srcDir.
       * 
       * @return ArrayList with String paths of File under srcDir (relative to
       *         srcDir)
       */
      public static File[] listFiles(File srcDir, FileFilter fileFilter) {
            ArrayList result = new ArrayList();
            if ((null != srcDir) && srcDir.canRead()) {
                  listFiles(srcDir, result, fileFilter);
            }
            return (File[]) result.toArray(new File[result.size()]);
      }

      /**
       * Recursively list .class files in specified directory
       * 
       * @return List of File objects
       */
      public static List listClassFiles(File dir) {
            ArrayList result = new ArrayList();
            if ((null != dir) && dir.canRead()) {
                  listClassFiles(dir, result);
            }
            return result;
      }

      /**
       * Convert String[] paths to File[] as offset of base directory
       * 
       * @param basedir the non-null File base directory for File to create with
       *            paths
       * @param paths the String[] of paths to create
       * @return File[] with same length as paths
       */
      public static File[] getBaseDirFiles(File basedir, String[] paths) {
            return getBaseDirFiles(basedir, paths, (String[]) null);
      }

      /**
       * Convert String[] paths to File[] as offset of base directory
       * 
       * @param basedir the non-null File base directory for File to create with
       *            paths
       * @param paths the String[] of paths to create
       * @param suffixes the String[] of suffixes to limit sources to - ignored if
       *            null
       * @return File[] with same length as paths
       */
      public static File[] getBaseDirFiles(File basedir, String[] paths, String[] suffixes) {
            LangUtil.throwIaxIfNull(basedir, "basedir");
            LangUtil.throwIaxIfNull(paths, "paths");
            File[] result = null;
            if (!LangUtil.isEmpty(suffixes)) {
                  ArrayList list = new ArrayList();
                  for (int i = 0; i < paths.length; i++) {
                        String path = paths[i];
                        for (int j = 0; j < suffixes.length; j++) {
                              if (path.endsWith(suffixes[j])) {
                                    list.add(new File(basedir, paths[i]));
                                    break;
                              }
                        }
                  }
                  result = (File[]) list.toArray(new File[0]);
            } else {
                  result = new File[paths.length];
                  for (int i = 0; i < result.length; i++) {
                        result[i] = newFile(basedir, paths[i]);
                  }
            }
            return result;
      }

      /**
       * Create a new File, resolving paths ".." and "." specially.
       * 
       * @param dir the File for the parent directory of the file
       * @param path the path in the parent directory (filename only?)
       * @return File for the new file.
       */
      private static File newFile(File dir, String path) {
            if (".".equals(path)) {
                  return dir;
            } else if ("..".equals(path)) {
                  File parentDir = dir.getParentFile();
                  if (null != parentDir) {
                        return parentDir;
                  } else {
                        return new File(dir, "..");
                  }
            } else {
                  return new File(dir, path);
            }
      }

      /**
       * Copy files from source dir into destination directory, creating any
       * needed directories. This differs from copyDir in not being recursive;
       * each input with the source dir creates a full path. However, if the
       * source is a directory, it is copied as such.
       * 
       * @param srcDir an existing, readable directory containing relativePaths
       *            files
       * @param relativePaths a set of paths relative to srcDir to readable File
       *            to copy
       * @param destDir an existing, writable directory to copy files to
       * @throws IllegalArgumentException if input invalid, IOException if
       *             operations fail
       */
      public static File[] copyFiles(File srcDir, String[] relativePaths, File destDir) throws IllegalArgumentException, IOException {
            final String[] paths = relativePaths;
            throwIaxUnlessCanReadDir(srcDir, "srcDir");
            throwIaxUnlessCanWriteDir(destDir, "destDir");
            LangUtil.throwIaxIfNull(paths, "relativePaths");
            File[] result = new File[paths.length];
            for (int i = 0; i < paths.length; i++) {
                  String path = paths[i];
                  LangUtil.throwIaxIfNull(path, "relativePaths-entry");
                  File src = newFile(srcDir, paths[i]);
                  File dest = newFile(destDir, path);
                  File destParent = dest.getParentFile();
                  if (!destParent.exists()) {
                        destParent.mkdirs();
                  }
                  LangUtil.throwIaxIfFalse(canWriteDir(destParent), "dest-entry-parent");
                  copyFile(src, dest); // both file-dir and dir-dir copies
                  result[i] = dest;
            }
            return result;
      }

      /**
       * Copy fromFile to toFile, handling file-file, dir-dir, and file-dir
       * copies.
       * 
       * @param fromFile the File path of the file or directory to copy - must be
       *            readable
       * @param toFile the File path of the target file or directory - must be
       *            writable (will be created if it does not exist)
       */
      public static void copyFile(File fromFile, File toFile) throws IOException {
            LangUtil.throwIaxIfNull(fromFile, "fromFile");
            LangUtil.throwIaxIfNull(toFile, "toFile");
            LangUtil.throwIaxIfFalse(!toFile.equals(fromFile), "same file");
            if (toFile.isDirectory()) { // existing directory
                  throwIaxUnlessCanWriteDir(toFile, "toFile");
                  if (fromFile.isFile()) { // file-dir
                        File targFile = new File(toFile, fromFile.getName());
                        copyValidFiles(fromFile, targFile);
                  } else if (fromFile.isDirectory()) { // dir-dir
                        copyDir(fromFile, toFile);
                  } else {
                        LangUtil.throwIaxIfFalse(false, "not dir or file: " + fromFile);
                  }
            } else if (toFile.isFile()) { // target file exists
                  if (fromFile.isDirectory()) {
                        LangUtil.throwIaxIfFalse(false, "can't copy to file dir: " + fromFile);
                  }
                  copyValidFiles(fromFile, toFile); // file-file
            } else { // target file is a non-existent path -- could be file or dir
                  /* File toFileParent = */ensureParentWritable(toFile);
                  if (fromFile.isFile()) {
                        copyValidFiles(fromFile, toFile);
                  } else if (fromFile.isDirectory()) {
                        toFile.mkdirs();
                        throwIaxUnlessCanWriteDir(toFile, "toFile");
                        copyDir(fromFile, toFile);
                  } else {
                        LangUtil.throwIaxIfFalse(false, "not dir or file: " + fromFile);
                  }
            }
      }

      /**
       * Ensure that the parent directory to path can be written. If the path has
       * a null parent, DEFAULT_PARENT is tested. If the path parent does not
       * exist, this tries to create it.
       * 
       * @param path the File path whose parent should be writable
       * @return the File path of the writable parent directory
       * @throws IllegalArgumentException if parent cannot be written or path is
       *             null.
       */
      public static File ensureParentWritable(File path) {
            LangUtil.throwIaxIfNull(path, "path");
            File pathParent = path.getParentFile();
            if (null == pathParent) {
                  pathParent = DEFAULT_PARENT;
            }
            if (!pathParent.canWrite()) {
                  pathParent.mkdirs();
            }
            throwIaxUnlessCanWriteDir(pathParent, "pathParent");
            return pathParent;
      }

      /**
       * Copy file to file.
       * 
       * @param fromFile the File to copy (readable, non-null file)
       * @param toFile the File to copy to (non-null, parent dir exists)
       * @throws IOException
       */
      public static void copyValidFiles(File fromFile, File toFile) throws IOException {
            FileInputStream in = null;
            FileOutputStream out = null;
            try {
                  in = new FileInputStream(fromFile);
                  out = new FileOutputStream(toFile);
                  copyStream(in, out);
            } finally {
                  if (out != null) {
                        out.close();
                  }
                  if (in != null) {
                        in.close();
                  }
            }
      }

      /** do line-based copying */
      public static void copyStream(DataInputStream in, PrintStream out) throws IOException {
            LangUtil.throwIaxIfNull(in, "in");
            LangUtil.throwIaxIfNull(in, "out");
            String s;
            while (null != (s = in.readLine())) {
                  out.println(s);
            }
      }

      public static void copyStream(InputStream in, OutputStream out) throws IOException {
            final int MAX = 4096;
            byte[] buf = new byte[MAX];
            for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
                  out.write(buf, 0, bytesRead);
            }
      }

      public static void copyStream(Reader in, Writer out) throws IOException {
            final int MAX = 4096;
            char[] buf = new char[MAX];
            for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
                  out.write(buf, 0, bytesRead);
            }
      }

      /**
       * Make a new child directory of parent
       * 
       * @param parent a File for the parent (writable)
       * @param child a prefix for the child directory
       * @return a File dir that exists with parentDir as the parent file or null
       */
      public static File makeNewChildDir(File parent, String child) {
            if (null == parent || !parent.canWrite() || !parent.isDirectory()) {
                  throw new IllegalArgumentException("bad parent: " + parent);
            } else if (null == child) {
                  child = "makeNewChildDir";
            } else if (!isValidFileName(child)) {
                  throw new IllegalArgumentException("bad child: " + child);
            }
            File result = new File(parent, child);
            int safety = 1000;
            for (String suffix = FileUtil.randomFileString(); ((0 < --safety) && result.exists()); suffix = FileUtil.randomFileString()) {
                  result = new File(parent, child + suffix);
            }
            if (result.exists()) {
                  System.err.println("exhausted files for child dir in " + parent);
                  return null;
            }
            return ((result.mkdirs() && result.exists()) ? result : null);
      }

      /**
       * Make a new temporary directory in the same directory that the system uses
       * for temporary files, or if that files, in the current directory.
       * 
       * @param name the preferred (simple) name of the directory - may be null.
       * @return File of an existing new temp dir, or null if unable to create
       */
      public static File getTempDir(String name) {
            if (null == name) {
                  name = "FileUtil_getTempDir";
            } else if (!isValidFileName(name)) {
                  throw new IllegalArgumentException(" invalid: " + name);
            }
            File result = null;
            File tempFile = null;
            try {
                  tempFile = File.createTempFile("ignoreMe", ".txt");
                  File tempParent = tempFile.getParentFile();
                  result = makeNewChildDir(tempParent, name);
            } catch (IOException t) {
                  result = makeNewChildDir(new File("."), name);
            } finally {
                  if (null != tempFile) {
                        tempFile.delete();
                  }
            }
            return result;
      }

      public static URL[] getFileURLs(File[] files) {
            if ((null == files) || (0 == files.length)) {
                  return new URL[0];
            }
            URL[] result = new URL[files.length]; // XXX dangerous non-copy...
            for (int i = 0; i < result.length; i++) {
                  result[i] = getFileURL(files[i]);
            }
            return result;
      }

      /**
       * Get URL for a File. This appends "/" for directories. prints errors to
       * System.err
       * 
       * @param file the File to convert to URL (not null)
       */
      public static URL getFileURL(File file) {
            LangUtil.throwIaxIfNull(file, "file");
            URL result = null;
            try {
                  result = file.toURL();// TODO AV - was toURI.toURL that does not
                  // works on Java 1.3
                  if (null != result) {
                        return result;
                  }
                  String url = "file:" + file.getAbsolutePath().replace('\\', '/');
                  result = new URL(url + (file.isDirectory() ? "/" : ""));
            } catch (MalformedURLException e) {
                  String m = "Util.makeURL(\"" + file.getPath() + "\" MUE " + e.getMessage();
                  System.err.println(m);
            }
            return result;
      }

      /**
       * Write contents to file, returning null on success or error message
       * otherwise. This tries to make any necessary parent directories first.
       * 
       * @param file the File to write (not null)
       * @param contents the String to write (use "" if null)
       * @return String null on no error, error otherwise
       */
      public static String writeAsString(File file, String contents) {
            LangUtil.throwIaxIfNull(file, "file");
            if (null == contents) {
                  contents = "";
            }
            Writer out = null;
            try {
                  File parentDir = file.getParentFile();
                  if (!parentDir.exists() && !parentDir.mkdirs()) {
                        return "unable to make parent dir for " + file;
                  }
                  Reader in = new StringReader(contents);
                  out = new FileWriter(file);
                  FileUtil.copyStream(in, out);
                  return null;
            } catch (IOException e) {
                  return LangUtil.unqualifiedClassName(e) + " writing " + file + ": " + e.getMessage();
            } finally {
                  if (null != out) {
                        try {
                              out.close();
                        } catch (IOException e) {
                        } // ignored
                  }
            }
      }

      /**
       * Reads a boolean array with our encoding
       */
      public static boolean[] readBooleanArray(DataInputStream s) throws IOException {
            int len = s.readInt();
            boolean[] ret = new boolean[len];
            for (int i = 0; i < len; i++)
                  ret[i] = s.readBoolean();
            return ret;
      }

      /**
       * Writes a boolean array with our encoding
       */
      public static void writeBooleanArray(boolean[] a, DataOutputStream s) throws IOException {
            int len = a.length;
            s.writeInt(len);
            for (int i = 0; i < len; i++)
                  s.writeBoolean(a[i]);
      }

      /**
       * Reads an int array with our encoding
       */
      public static int[] readIntArray(DataInputStream s) throws IOException {
            int len = s.readInt();
            int[] ret = new int[len];
            for (int i = 0; i < len; i++)
                  ret[i] = s.readInt();
            return ret;
      }

      /**
       * Writes an int array with our encoding
       */
      public static void writeIntArray(int[] a, DataOutputStream s) throws IOException {
            int len = a.length;
            s.writeInt(len);
            for (int i = 0; i < len; i++)
                  s.writeInt(a[i]);
      }

      /**
       * Reads an int array with our encoding
       */
      public static String[] readStringArray(DataInputStream s) throws IOException {
            int len = s.readInt();
            String[] ret = new String[len];
            for (int i = 0; i < len; i++)
                  ret[i] = s.readUTF();
            return ret;
      }

      /**
       * Writes an int array with our encoding
       */
      public static void writeStringArray(String[] a, DataOutputStream s) throws IOException {
            if (a == null) {
                  s.writeInt(0);
                  return;
            }
            int len = a.length;
            s.writeInt(len);
            for (int i = 0; i < len; i++)
                  s.writeUTF(a[i]);
      }

      /**
       * Returns the contents of this file as a String
       */
      public static String readAsString(File file) throws IOException {
            BufferedReader r = new BufferedReader(new FileReader(file));
            StringBuffer b = new StringBuffer();
            while (true) {
                  int ch = r.read();
                  if (ch == -1)
                        break;
                  b.append((char) ch);
            }
            r.close();
            return b.toString();
      }

      // /**
      // * Returns the contents of this stream as a String
      // */
      // public static String readAsString(InputStream in) throws IOException {
      // BufferedReader r = new BufferedReader(new InputStreamReader(in));
      // StringBuffer b = new StringBuffer();
      // while (true) {
      // int ch = r.read();
      // if (ch == -1)
      // break;
      // b.append((char) ch);
      // }
      // in.close();
      // r.close();
      // return b.toString();
      // }

      /**
       * Returns the contents of this file as a byte[]
       */
      public static byte[] readAsByteArray(File file) throws IOException {
            FileInputStream in = new FileInputStream(file);
            byte[] ret = FileUtil.readAsByteArray(in);
            in.close();
            return ret;
      }

      /**
       * Reads this input stream and returns contents as a byte[]
       */
      public static byte[] readAsByteArray(InputStream inStream) throws IOException {
            int size = 1024;
            byte[] ba = new byte[size];
            int readSoFar = 0;

            while (true) {
                  int nRead = inStream.read(ba, readSoFar, size - readSoFar);
                  if (nRead == -1)
                        break;
                  readSoFar += nRead;
                  if (readSoFar == size) {
                        int newSize = size * 2;
                        byte[] newBa = new byte[newSize];
                        System.arraycopy(ba, 0, newBa, 0, size);
                        ba = newBa;
                        size = newSize;
                  }
            }

            byte[] newBa = new byte[readSoFar];
            System.arraycopy(ba, 0, newBa, 0, readSoFar);
            return newBa;
      }

      final static String FILECHARS = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

      /** @return semi-random String of length 6 usable as filename suffix */
      static String randomFileString() {
            final double FILECHARS_length = FILECHARS.length();
            final int LEN = 6;
            final char[] result = new char[LEN];
            int index = (int) (Math.random() * 6d);
            for (int i = 0; i < LEN; i++) {
                  if (index >= LEN) {
                        index = 0;
                  }
                  result[index++] = FILECHARS.charAt((int) (Math.random() * FILECHARS_length));
            }
            return new String(result);
      }

      public static InputStream getStreamFromZip(String zipFile, String name) {
            try {
                  ZipFile zf = new ZipFile(zipFile);
                  try {
                        ZipEntry entry = zf.getEntry(name);
                        return zf.getInputStream(entry);
                  } finally {
                        // ??? is it safe not to close this zf.close();
                  }
            } catch (IOException ioe) {
                  return null;
            }
      }

      //
      // public static void extractJar(String zipFile, String outDir) throws
      // IOException {
      // ZipInputStream zs = new ZipInputStream(new FileInputStream(zipFile));
      // ZipEntry entry;
      // while ((entry = zs.getNextEntry()) != null) {
      // if (entry.isDirectory())
      // continue;
      // byte[] in = readAsByteArray(zs);
      //
      // File outFile = new File(outDir + "/" + entry.getName());
      // // if (!outFile.getParentFile().exists())
      // // System.err.println("parent: " + outFile.getParentFile());
      // // System.err.println("parent: " + outFile.getParentFile());
      // outFile.getParentFile().mkdirs();
      // FileOutputStream os = new FileOutputStream(outFile);
      // os.write(in);
      // os.close();
      // zs.closeEntry();
      // }
      // zs.close();
      // }

      /**
       * Do line-based search for literal text in source files, returning
       * file:line where found.
       * 
       * @param sought the String text to seek in the file
       * @param sources the List of String paths to the source files
       * @param listAll if false, only list first match in file
       * @param errorSink the PrintStream to print any errors to (one per line)
       *            (use null to silently ignore errors)
       * @return List of String of the form file:line for each found entry (never
       *         null, might be empty)
       */
      // OPTIMIZE only used by tests? move it out
      public static List lineSeek(String sought, List sources, boolean listAll, PrintStream errorSink) {
            if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sources)) {
                  return Collections.EMPTY_LIST;
            }
            ArrayList result = new ArrayList();
            for (Iterator iter = sources.iterator(); iter.hasNext();) {
                  String path = (String) iter.next();
                  String error = lineSeek(sought, path, listAll, result);
                  if ((null != error) && (null != errorSink)) {
                        errorSink.println(error);
                  }
            }
            return result;
      }

      /**
       * Do line-based search for literal text in source file, returning line
       * where found as a String in the form {sourcePath}:line:column submitted to
       * the collecting parameter sink. Any error is rendered to String and
       * returned as the result.
       * 
       * @param sought the String text to seek in the file
       * @param sources the List of String paths to the source files
       * @param listAll if false, only list first match in file
       * @param List sink the List for String entries of the form
       *            {sourcePath}:line:column
       * @return String error if any, or add String entries to sink
       */
      public static String lineSeek(String sought, String sourcePath, boolean listAll, ArrayList sink) {
            if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sourcePath)) {
                  return "nothing sought";
            }
            if (LangUtil.isEmpty(sourcePath)) {
                  return "no sourcePath";
            }
            final File file = new File(sourcePath);
            if (!file.canRead() || !file.isFile()) {
                  return "sourcePath not a readable file";
            }
            int lineNum = 0;
            FileReader fin = null;
            try {
                  fin = new FileReader(file);
                  BufferedReader reader = new BufferedReader(fin);
                  String line;
                  while (null != (line = reader.readLine())) {
                        lineNum++;
                        int loc = line.indexOf(sought);
                        if (-1 != loc) {
                              sink.add(sourcePath + ":" + lineNum + ":" + loc);
                              if (!listAll) {
                                    break;
                              }
                        }
                  }
            } catch (IOException e) {
                  return LangUtil.unqualifiedClassName(e) + " reading " + sourcePath + ":" + lineNum;
            } finally {
                  try {
                        if (null != fin)
                              fin.close();
                  } catch (IOException e) {
                  } // ignore
            }
            return null;
      }

      public static BufferedOutputStream makeOutputStream(File file) throws FileNotFoundException {
            File parent = file.getParentFile();
            if (parent != null)
                  parent.mkdirs();
            return new BufferedOutputStream(new FileOutputStream(file));
      }

      /**
       * Sleep until after the last last-modified stamp from the files.
       * 
       * @param files the File[] of files to inspect for last modified times (this
       *            ignores null or empty files array and null or non-existing
       *            components of files array)
       * @return true if succeeded without 100 interrupts
       */
      public static boolean sleepPastFinalModifiedTime(File[] files) {
            if ((null == files) || (0 == files.length)) {
                  return true;
            }
            long delayUntil = System.currentTimeMillis();
            for (int i = 0; i < files.length; i++) {
                  File file = files[i];
                  if ((null == file) || !file.exists()) {
                        continue;
                  }
                  long nextModTime = file.lastModified();
                  if (nextModTime > delayUntil) {
                        delayUntil = nextModTime;
                  }
            }
            return LangUtil.sleepUntil(++delayUntil);
      }

      private static void listClassFiles(final File baseDir, ArrayList result) {
            File[] files = baseDir.listFiles();
            for (int i = 0; i < files.length; i++) {
                  File f = files[i];
                  if (f.isDirectory()) {
                        listClassFiles(f, result);
                  } else {
                        if (f.getName().endsWith(".class")) {
                              result.add(f);
                        }
                  }
            }
      }

      private static void listFiles(final File baseDir, ArrayList result, FileFilter filter) {
            File[] files = baseDir.listFiles();
            // hack https://bugs.eclipse.org/bugs/show_bug.cgi?id=48650
            final boolean skipCVS = (!PERMIT_CVS && (filter == aspectjSourceFileFilter));
            for (int i = 0; i < files.length; i++) {
                  File f = files[i];
                  if (f.isDirectory()) {
                        if (skipCVS) {
                              String name = f.getName().toLowerCase();
                              if ("cvs".equals(name) || "sccs".equals(name)) {
                                    continue;
                              }
                        }
                        listFiles(f, result, filter);
                  } else {
                        if (filter.accept(f))
                              result.add(f);
                  }
            }
      }

      /** @return true if input is not null and contains no path separator */
      private static boolean isValidFileName(String input) {
            return ((null != input) && (-1 == input.indexOf(File.pathSeparator)));
      }

      private static void listFiles(final File baseDir, String dir, ArrayList result) {
            final String dirPrefix = (null == dir ? "" : dir + "/");
            final File dirFile = (null == dir ? baseDir : new File(baseDir.getPath() + "/" + dir));
            final String[] files = dirFile.list();
            for (int i = 0; i < files.length; i++) {
                  File f = new File(dirFile, files[i]);
                  String path = dirPrefix + files[i];
                  if (f.isDirectory()) {
                        listFiles(baseDir, path, result);
                  } else {
                        result.add(path);
                  }
            }
      }

      private FileUtil() {
      }

      public static List makeClasspath(URL[] urls) {
            List ret = new LinkedList();
            if (urls != null) {
                  for (int i = 0; i < urls.length; i++) {
                        ret.add(urls[i].getPath());
                  }
            }
            return ret;
      }

      /**
       * A pipe when run reads from an input stream to an output stream,
       * optionally sleeping between reads.
       * 
       * @see #copyStream(InputStream, OutputStream)
       */
01434       public static class Pipe implements Runnable {
            private final InputStream in;
            private final OutputStream out;
            private final long sleep;
            private ByteArrayOutputStream snoop;
            private long totalWritten;
            private Throwable thrown;
            private boolean halt;
            /**
             * Seem to be unable to detect erroroneous closing of System.out...
             */
01445             private final boolean closeInput;
            private final boolean closeOutput;

            /**
             * If true, then continue processing stream until no characters are
             * returned when halting.
             */
01452             private boolean finishStream;

            private boolean done; // true after completing() completes

            /**
             * alias for <code>Pipe(in, out, 100l, false, false)</code>
             * 
             * @param in the InputStream source to read
             * @param out the OutputStream sink to write
             */
01462             Pipe(InputStream in, OutputStream out) {
                  this(in, out, 100l, false, false);
            }

            /**
             * @param in the InputStream source to read
             * @param out the OutputStream sink to write
             * @param tryClosingStreams if true, then try closing both streams when
             *            done
             * @param sleep milliseconds to delay between reads (pinned to 0..1
             *            minute)
             */
01474             Pipe(InputStream in, OutputStream out, long sleep, boolean closeInput, boolean closeOutput) {
                  LangUtil.throwIaxIfNull(in, "in");
                  LangUtil.throwIaxIfNull(out, "out");
                  this.in = in;
                  this.out = out;
                  this.closeInput = closeInput;
                  this.closeOutput = closeOutput;
                  this.sleep = Math.min(0l, Math.max(60l * 1000l, sleep));
            }

            public void setSnoop(ByteArrayOutputStream snoop) {
                  this.snoop = snoop;
            }

            /**
             * Run the pipe. This halts on the first Throwable thrown or when a read
             * returns -1 (for end-of-file) or on demand.
             */
01492             public void run() {
                  totalWritten = 0;
                  if (halt) {
                        return;
                  }
                  try {
                        final int MAX = 4096;
                        byte[] buf = new byte[MAX];
                        // TODO this blocks, hanging the harness
                        int count = in.read(buf, 0, MAX);
                        ByteArrayOutputStream mySnoop;
                        while ((halt && finishStream && (0 < count)) || (!halt && (-1 != count))) {
                              out.write(buf, 0, count);
                              mySnoop = snoop;
                              if (null != mySnoop) {
                                    mySnoop.write(buf, 0, count);
                              }
                              totalWritten += count;
                              if (halt && !finishStream) {
                                    break;
                              }
                              if (!halt && (0 < sleep)) {
                                    Thread.sleep(sleep);
                              }
                              if (halt && !finishStream) {
                                    break;
                              }
                              count = in.read(buf, 0, MAX);
                        }
                  } catch (Throwable e) {
                        thrown = e;
                  } finally {
                        halt = true;
                        if (closeInput) {
                              try {
                                    in.close();
                              } catch (IOException e) {
                                    // ignore
                              }
                        }
                        if (closeOutput) {
                              try {
                                    out.close();
                              } catch (IOException e) {
                                    // ignore
                              }
                        }
                        done = true;
                        completing(totalWritten, thrown);
                  }

            }

            /**
             * Tell the pipe to halt the next time it gains control.
             * 
             * @param wait if true, this waits synchronously until pipe is done
             * @param finishStream if true, then continue until a read from the
             *            input stream returns no bytes, then halt.
             * @return true if <code>run()</code> will return the next time it gains
             *         control
             */
01554             public boolean halt(boolean wait, boolean finishStream) {
                  if (!halt) {
                        halt = true;
                  }
                  if (wait) {
                        while (!done) {
                              synchronized (this) {
                                    notifyAll();
                              }
                              if (!done) {
                                    try {
                                          Thread.sleep(5l);
                                    } catch (InterruptedException e) {
                                          break;
                                    }
                              }
                        }
                  }
                  return halt;
            }

            /** @return the total number of bytes written */
01576             public long totalWritten() {
                  return totalWritten;
            }

            /** @return any exception thrown when reading/writing */
01581             public Throwable getThrown() {
                  return thrown;
            }

            /**
             * This is called when the pipe is completing. This implementation does
             * nothing. Subclasses implement this to get notice. Note that
             * halt(true, true) might or might not have completed before this method
             * is called.
             */
01591             protected void completing(long totalWritten, Throwable thrown) {
            }
      }

}

Generated by  Doxygen 1.6.0   Back to index