package com.linkedin.util; import java.io.File; import java.io.FileFilter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Keren Jin */ public class FileUtil { private static final Logger _log = LoggerFactory.getLogger(FileUtil.class); public static class FileExtensionFilter implements FileFilter { public FileExtensionFilter(String extension) { _extension = extension; } public boolean accept(File file) { return file.getName().endsWith(_extension); } private final String _extension; } /** * Scan a directory for files. * * Recursively scans a directory for files. * Recursive into each directory. * Invoke the provided filter on each non-directory file, if the * filter accepts the file, then add this file to the list of * files to return. * * @param directory provides the directory to scan for source files. * @param fileFilter to apply to each non-directory file. * null if no filter is applied. * @return list of files found in the directory. */ public static List<File> listFiles(File directory, FileFilter fileFilter) { final List<File> result = new ArrayList<File>(); final ArrayDeque<File> deque = new ArrayDeque<File>(); deque.addFirst(directory); while (deque.isEmpty() == false) { File file = deque.removeFirst(); if (file.isDirectory()) { final File[] filesInDirectory = file.listFiles(); if (filesInDirectory == null) { _log.error("Unable to list files under " + file.getPath()); } else { for (File f : filesInDirectory) { deque.addLast(f); } } } else if (fileFilter == null || fileFilter.accept(file)) { result.add(file); } } return result; } public static String buildSystemIndependentPath(String ... pathPieces) { char separtor = File.separatorChar; StringBuilder sb = new StringBuilder(); for (String piece: pathPieces) { sb.append(separtor); sb.append(piece); } return sb.toString(); } public static String removeFileExtension(String filename) { final int idx = filename.lastIndexOf('.'); if (idx == 0) { return filename; } else { return filename.substring(0, idx); } } /** * Whether the files that would be generated into the specified target directory are more recent than the most recent source files. * <p/> * This used to check if the output file is already up-to-date and need not be overwritten with generated output. * * @param sourceFiles provides the source files that were parsed. * @param targetFiles provides the files that would have been generated. * * @return true if the files that would be generated are more recent than the most recent source files. */ public static boolean upToDate(Collection<File> sourceFiles, Collection<File> targetFiles) { final long sourceLastModified = mostRecentLastModified(sourceFiles); return filesLastModifiedMoreRecentThan(targetFiles, sourceLastModified); } /** * Compute the most recent last modified time of the provided files. * * @param files to compute most recent modified time from. * * @return the most resent last modified of the provided files. */ private static long mostRecentLastModified(Collection<File> files) { long mostRecent = 0L; for (File file : files) { final long fileLastModified = file.lastModified(); if (mostRecent < fileLastModified) { mostRecent = fileLastModified; } } return mostRecent; } /** * Determine whether the provided files has been modified more recently than the provided time. * * @param files whose last modified times will be compared to provided time. * @param time to compare the files' last modified times to. * * @return true if the provided files has been modified more recently than the provided time. */ private static boolean filesLastModifiedMoreRecentThan(Collection<File> files, long time) { for (File file : files) { if (!file.exists() || time >= file.lastModified()) { return false; } } return true; } }