Java tutorial
package org.apache.torque.generator.file; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOCase; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Selects Files in a directory and the subdirectories of the directory. * From these files, all that match an include pattern and do not * match an exclude pattern are selected. * * @version $Id: Fileset.java 1406804 2012-11-07 21:04:28Z tfischer $ */ public class Fileset { /** The class logger. */ private static Log log = LogFactory.getLog(Fileset.class); /** The base directory of the fileset. */ private File basedir; /** * The patterns for the files to include. * If null or empty, all Files are included. */ private Collection<String> includes; /** * The patterns for the files to exclude. * If null or empty, no Files are excluded. */ private Collection<String> excludes; /** * Default constructor. */ public Fileset() { } /** * All-Args constructor. * * @param basedir the basedir, or null to use the current basedir. * @param includes The patterns for the files to include. * If null or empty, all Files are included. * @param excludes The patterns for the files to exclude. * If null or empty, no Files are excluded. */ public Fileset(File basedir, Collection<String> includes, Collection<String> excludes) { this.basedir = basedir; this.includes = includes; this.excludes = excludes; } /** * Returns the base directory of the fileset. * * @return the base directory, or null if no basedir is specified. */ public File getBasedir() { return basedir; } /** * Sets the base directory of the fileset. * * @param basedir the base directory, or null. */ public void setBasedir(File basedir) { this.basedir = basedir; } /** * Returns the include patterns for the fileset. * * @return the include patterns, or null if all files should be included. */ public Collection<String> getIncludes() { return includes; } /** * Sets the include patterns for the fileset. * * @param includes the include patterns, or null if all files * should be included. */ public void setIncludes(Collection<String> includes) { this.includes = includes; } /** * Returns the exclude patterns for the fileset. * * @return the exclude patterns, or null if all files should be excluded. */ public Collection<String> getExcludes() { return excludes; } /** * Sets the exclude patterns for the fileset. * * @param excludes the exclude patterns, or null if all files * should be excluded. */ public void setExcludes(Collection<String> excludes) { this.excludes = excludes; } /** * Returns the names of all files matching this fileset. * * @return the names of all matching files, not null. * * @throws IOException if an error occurs reading the file names. */ public List<File> getFiles() throws IOException { List<File> result = new ArrayList<File>(); if (includes == null || includes.isEmpty()) { getAllFiles(basedir, result); } else { // process includes for (String includePattern : includes) { int wildcardFreeSeparatorPos = getWildcardFreeSeparatorPos(includePattern); String wildcardFreeIncludePart = getPathPartBefore(includePattern, wildcardFreeSeparatorPos); if (log.isTraceEnabled()) { log.trace("getFiles() : traversing directory " + wildcardFreeIncludePart + " in base dir " + basedir); } File wildcardFreeBaseDir = new File(basedir, wildcardFreeIncludePart); String wildcardPattern = getPathPartAfter(includePattern, wildcardFreeSeparatorPos); String[] wildcardParts = StringUtils.split(wildcardPattern, "\\/"); List<String> wildcardPartList = Arrays.asList(wildcardParts); List<File> includeFiles = getFiles(wildcardFreeBaseDir, wildcardPartList); result.addAll(includeFiles); } } // process excludes if (excludes == null) { if (log.isTraceEnabled()) { log.trace("getFiles() : no excludes are defined."); } return result; } Iterator<File> fileIt = result.iterator(); while (fileIt.hasNext()) { File file = fileIt.next(); if (log.isTraceEnabled()) { log.trace("getFiles() : checking excludes for file " + file.getPath()); } boolean excluded = false; for (String excludePattern : excludes) { File excludePatternFile = new File(basedir, excludePattern); if (matchesPattern(file, excludePatternFile.getPath())) { if (log.isTraceEnabled()) { log.trace("getFiles() : exclude pattern " + excludePatternFile.getPath() + " matches, file is excluded"); } excluded = true; break; } else if (log.isTraceEnabled()) { log.trace("getFiles() : exclude pattern " + excludePatternFile.getPath() + " does not match"); } } if (excluded) { fileIt.remove(); } } // make file order reproducable Collections.sort(result); return result; } /** * Reads the names of all files in a directory and its subdirectories. * * @param currentBaseDir the base directory from which the files are read, * not null. * @param toAddTo the list of files where the found file should be added to, * not null. * * @throws IOException if an error occurs during reading the directory. */ static void getAllFiles(File currentBaseDir, List<File> toAddTo) throws IOException { if (currentBaseDir == null) { currentBaseDir = new File("."); } if (log.isTraceEnabled()) { log.trace("getAllFiles() : traversing directory " + currentBaseDir.getAbsolutePath()); } File[] filesInDir = currentBaseDir.listFiles(new WildcardFilter("*", false, true)); if (filesInDir == null) { throw new IOException("Could not list files in the following Directory " + "while reading the sources: " + currentBaseDir.getAbsolutePath()); } if (log.isTraceEnabled()) { log.trace("getAllFiles() : Adding files " + Arrays.toString(filesInDir) + " to candidate list"); } toAddTo.addAll(Arrays.asList(filesInDir)); File[] dirsInDir = currentBaseDir.listFiles(new WildcardFilter("*", true, false)); for (File dir : dirsInDir) { getAllFiles(dir, toAddTo); } } /** * Reads the name of a set of files matching a path pattern. * * @param currentBaseDir the base directory from which the files are read, * not null. * @param pathPartList the split path to the files (split where the path * separator appears). E.g. to access resources/x.properties, * the list would be ["resources", "x.properties"] * @return the set of all files which match the pathPartList. * * @throws IOException if an error occurs during reading the directory. */ static List<File> getFiles(File currentBaseDir, List<String> pathPartList) throws IOException { if (log.isTraceEnabled()) { log.trace("getFiles(File, List) : traversing directory " + currentBaseDir.getAbsolutePath() + ", current path parts: " + pathPartList); } List<String> partsCopy = new ArrayList<String>(pathPartList); String includeToProcess = partsCopy.remove(0); if (partsCopy.size() == 0) { File[] matches = currentBaseDir.listFiles(new WildcardFilter(includeToProcess, false, true)); if (matches == null) { throw new IOException("Could not list files in the following Directory " + "while reading the sources: " + currentBaseDir.getAbsolutePath()); } List<File> result = Arrays.asList(matches); if (log.isTraceEnabled()) { log.trace("getFiles(File, List) : Returning files " + result); } return result; } if ("..".equals(includeToProcess)) { return getFiles(currentBaseDir.getParentFile(), partsCopy); } File[] matchingDirs = currentBaseDir.listFiles(new WildcardFilter(includeToProcess, true, false)); List<File> result = new ArrayList<File>(); for (File dir : matchingDirs) { result.addAll(getFiles(dir, partsCopy)); } return result; } /** * Returns the position of the separator which separates the base part * of the path which does not contain any wildcards from the rest. * Example: * <ul> * <li>*.txt returns -1(no separator)</li> * <li>schema*.xml returns -1(no separator)</li> * <li>xml\schema*.xml returns 3 (backslash position)</li> * <li>/xml/???/schema*.xml returns 4 (middle slash position)</li> * </ul> * @param path the path to compute the position from, not null. * @return the separator position, -1 if no base part exists. */ static int getWildcardFreeSeparatorPos(String path) { int asteriskIndex = path.indexOf("*"); int questionMarkIndex = path.indexOf("?"); if (asteriskIndex != -1) { if (questionMarkIndex != -1) { int min = Math.min(asteriskIndex, questionMarkIndex); return getLargestSeparatorPos(path, min); } else { return getLargestSeparatorPos(path, asteriskIndex); } } return getLargestSeparatorPos(path, questionMarkIndex); } /** * Returns the largest position of a path separator within the path * which is smaller than endIndex. An endIndex of -1 means that * the largest separator pos should be given. -1 is returned if no * separator is present in the region. * <ul> * <li>getBaseDir("/xml/xxxx", 5) returns 4</li> * <li>getBaseDir("/xml/x/y", -1) returns 6</li> * <li>getBaseDir("/xml/x/y/", -1) returns 8</li> * </ul> */ static int getLargestSeparatorPos(String path, int maxIndex) { String baseString; if (maxIndex == -1) { baseString = path; } else { baseString = path.substring(0, maxIndex); } return FilenameUtils.indexOfLastSeparator(baseString); } /** * Returns the part of the path before the cutPosition. * If this part is empty or separatorPos is -1, "." is returned. * The character at cutPosition is not included in the result. * * @param path the path to get the part from. * @param cutPosition the position where to cut. * * @return the part of the path before cutPosition, or "." if this part * does not exist. */ static String getPathPartBefore(String path, int cutPosition) { if (cutPosition == -1) { return "."; } else { String resultString = path.substring(0, cutPosition); if (StringUtils.EMPTY.equals(resultString)) { resultString = "."; } return resultString; } } /** * Returns the part of the path after the separatorPos. * The character at cutPosition is not included in the result. * * @param path the path to get the part from. * @param cutPosition the position where to cut. * * @return the part of the path before cutPosition, or "." if this part * does not exist. */ static String getPathPartAfter(String path, int cutPosition) { String resultString = path.substring(cutPosition + 1); return resultString; } static boolean matchesPattern(File file, String pattern) { String filePath = file.getPath(); List<String> fileParts = splitAndNormalize(filePath); List<String> patternParts = splitAndNormalize(pattern); if (fileParts.size() != patternParts.size()) { return false; } Iterator<String> patternPartIt = patternParts.iterator(); for (String filePart : fileParts) { String patternPart = patternPartIt.next(); if (!FilenameUtils.wildcardMatch(filePart, patternPart, IOCase.SENSITIVE)) { return false; } } return true; } /** * Splits a path in its parts and normalizes the path * (i.e. removes . and ..), if possible. * * @param path the path to normalize * * @return the normalized path in its parts. */ static List<String> splitAndNormalize(String path) { String[] parts = StringUtils.split(path, "\\/"); List<String> normalizedParts = new ArrayList<String>(); for (String part : parts) { if (".".equals(part)) { continue; } if ("..".equals(part) && !normalizedParts.isEmpty()) { normalizedParts.remove(normalizedParts.size() - 1); continue; } normalizedParts.add(part); } return normalizedParts; } @Override public String toString() { return "Fileset [basedir=" + basedir + ", excludes=" + excludes + ", includes=" + includes + "]"; } }