diff --git a/proposal/sandbox/selectors/README b/proposal/sandbox/selectors/README new file mode 100644 index 000000000..fce8eaa32 --- /dev/null +++ b/proposal/sandbox/selectors/README @@ -0,0 +1,34 @@ +Selector API +============ + +Currently our filesets allow us to select a set of files based on name patterns. +For instance we could create a set of all the files that end with ".java". +However there are cases when you wish to select files based on their other +attributes, such as if they are read only or if they are older than a specified +date etc. + +The selector API is one such mechanism to do this. The selector API will allow +you to build file sets based on criteria other than name. Some possible criteria +would be + +Is the file readable? +Is the file writeable? +What date was the file modified on? +What size is the file? +Does the contents contain the string "magic"? + +If we end up supporting a VFS then we could expand the number of selectors +considerably. A mock representation that has been proposed before is the +following. Of course this is subject to change as soon as someone wants to +tackle this action ;) + + + + + + + + + diff --git a/proposal/sandbox/selectors/src/main/org/apache/tools/ant/DirectoryScanner.java b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/DirectoryScanner.java new file mode 100644 index 000000000..af9fbeab2 --- /dev/null +++ b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/DirectoryScanner.java @@ -0,0 +1,1206 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant; + +import java.io.File; +import java.io.InputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; +import java.util.Properties; +import java.util.StringTokenizer; +import org.apache.tools.ant.types.PatternSet; +import org.apache.tools.ant.types.Pattern; +import org.apache.tools.ant.selectors.FileSelector; + +/** + * Class for scanning a directory for files/directories that match a certain + * criteria. + *

+ * These criteria consist of a set of include and exclude patterns. With these + * patterns, you can select which files you want to have included, and which + * files you want to have excluded. + *

+ * The idea is simple. A given directory is recursively scanned for all files + * and directories. Each file/directory is matched against a set of include + * and exclude patterns. Only files/directories that match at least one + * pattern of the include pattern list, and don't match a pattern of the + * exclude pattern list will be placed in the list of files/directories found. + *

+ * When no list of include patterns is supplied, "**" will be used, which + * means that everything will be matched. When no list of exclude patterns is + * supplied, an empty list is used, such that nothing will be excluded. + *

+ * The pattern matching is done as follows: + * The name to be matched is split up in path segments. A path segment is the + * name of a directory or file, which is bounded by + * File.separator ('/' under UNIX, '\' under Windows). + * E.g. "abc/def/ghi/xyz.java" is split up in the segments "abc", "def", "ghi" + * and "xyz.java". + * The same is done for the pattern against which should be matched. + *

+ * Then the segments of the name and the pattern will be matched against each + * other. When '**' is used for a path segment in the pattern, then it matches + * zero or more path segments of the name. + *

+ * There are special case regarding the use of File.separators at + * the beginningof the pattern and the string to match:
+ * When a pattern starts with a File.separator, the string + * to match must also start with a File.separator. + * When a pattern does not start with a File.separator, the + * string to match may not start with a File.separator. + * When one of these rules is not obeyed, the string will not + * match. + *

+ * When a name path segment is matched against a pattern path segment, the + * following special characters can be used: + * '*' matches zero or more characters, + * '?' matches one character. + *

+ * Examples: + *

+ * "**\*.class" matches all .class files/dirs in a directory tree. + *

+ * "test\a??.java" matches all files/dirs which start with an 'a', then two + * more characters and then ".java", in a directory called test. + *

+ * "**" matches everything in a directory tree. + *

+ * "**\test\**\XYZ*" matches all files/dirs that start with "XYZ" and where + * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123"). + *

+ * Case sensitivity may be turned off if necessary. By default, it is + * turned on. + *

+ * Example of usage: + *

+ *   String[] includes = {"**\\*.class"};
+ *   String[] excludes = {"modules\\*\\**"};
+ *   ds.setIncludes(includes);
+ *   ds.setExcludes(excludes);
+ *   ds.setBasedir(new File("test"));
+ *   ds.setCaseSensitive(true);
+ *   ds.scan();
+ *
+ *   System.out.println("FILES:");
+ *   String[] files = ds.getIncludedFiles();
+ *   for (int i = 0; i < files.length;i++) {
+ *     System.out.println(files[i]);
+ *   }
+ * 
+ * This will scan a directory called test for .class files, but excludes all + * .class files in all directories under a directory called "modules" + * + * @author Arnout J. Kuiper ajkuiper@wxs.nl + * @author Magesh Umasankar + */ +public class DirectoryScanner implements FileScanner { + + /** + * Patterns that should be excluded by default. + * + * @see #addDefaultExcludes() + */ + protected final static String[] DEFAULTEXCLUDES = { + "**/*~", + "**/#*#", + "**/.#*", + "**/%*%", + "**/CVS", + "**/CVS/**", + "**/.cvsignore", + "**/SCCS", + "**/SCCS/**", + "**/vssver.scc" + }; + + /** + * The base directory which should be scanned. + */ + protected File basedir; + + /** + * The patterns for the files that should be included. + */ + protected Pattern[] includes; + + /** + * The patterns for the files that should be excluded. + */ + protected Pattern[] excludes; + + /** + * The files that where found and matched at least one includes, and matched + * no excludes. + */ + protected Vector filesIncluded; + + /** + * The files that where found and did not match any includes. + */ + protected Vector filesNotIncluded; + + /** + * The files that where found and matched at least one includes, and also + * matched at least one excludes. + */ + protected Vector filesExcluded; + + /** + * The directories that where found and matched at least one includes, and + * matched no excludes. + */ + protected Vector dirsIncluded; + + /** + * The directories that where found and did not match any includes. + */ + protected Vector dirsNotIncluded; + + /** + * The files that where found and matched at least one includes, and also + * matched at least one excludes. + */ + protected Vector dirsExcluded; + + /** + * Have the Vectors holding our results been built by a slow scan? + */ + protected boolean haveSlowResults = false; + + /** + * Should the file system be treated as a case sensitive one? + */ + protected boolean isCaseSensitive = true; + + /** + * Is everything we've seen so far included? + */ + protected boolean everythingIncluded = true; + + private static Hashtable selectorClasses = null; + + static { + String defs = "/org/apache/tools/ant/selectors/defaults.properties"; + selectorClasses = new Hashtable(); + + try { + Properties props = new Properties(); + InputStream in = DirectoryScanner.class.getResourceAsStream(defs); + if (in == null) { + throw new BuildException("Can't load default selector list"); + } + props.load(in); + in.close(); + + Enumeration enum = props.propertyNames(); + while (enum.hasMoreElements()) { + String key = (String) enum.nextElement(); + String value = props.getProperty(key); + try { + selectorClasses.put(key, Class.forName(value)); + } catch (NoClassDefFoundError ncdfe) { + } catch (ClassNotFoundException cnfe) { + } + } + } catch (IOException ioe) { + throw new BuildException("Can't load default selector list"); + } + } + + /** + * Constructor. + */ + public DirectoryScanner() { + } + + + /** + * Does the path match the start of this pattern up to the first "**". + * + *

This is not a general purpose test and should only be used if you + * can live with false positives.

+ * + *

pattern=**\\a and str=b will yield true. + * + * @param pattern the (non-null) pattern to match against + * @param str the (non-null) string (path) to match + */ + protected static boolean matchPatternStart(String pattern, String str) { + return matchPatternStart(pattern, str, true); + } + + /** + * Does the path match the start of this pattern up to the first "**". + * + *

This is not a general purpose test and should only be used if you + * can live with false positives.

+ * + *

pattern=**\\a and str=b will yield true. + * + * @param pattern the (non-null) pattern to match against + * @param str the (non-null) string (path) to match + * @param isCaseSensitive must matches be case sensitive? + */ + protected static boolean matchPatternStart(String pattern, String str, + boolean isCaseSensitive) { + // When str starts with a File.separator, pattern has to start with a + // File.separator. + // When pattern starts with a File.separator, str has to start with a + // File.separator. + if (str.startsWith(File.separator) != + pattern.startsWith(File.separator)) { + return false; + } + + Vector patDirs = new Vector(); + StringTokenizer st = new StringTokenizer(pattern,File.separator); + while (st.hasMoreTokens()) { + patDirs.addElement(st.nextToken()); + } + + Vector strDirs = new Vector(); + st = new StringTokenizer(str,File.separator); + while (st.hasMoreTokens()) { + strDirs.addElement(st.nextToken()); + } + + int patIdxStart = 0; + int patIdxEnd = patDirs.size()-1; + int strIdxStart = 0; + int strIdxEnd = strDirs.size()-1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = (String)patDirs.elementAt(patIdxStart); + if (patDir.equals("**")) { + break; + } + if (!match(patDir,(String)strDirs.elementAt(strIdxStart), isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + + if (strIdxStart > strIdxEnd) { + // String is exhausted + return true; + } else if (patIdxStart > patIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } else { + // pattern now holds ** while string is not exhausted + // this will generate false positives but we can live with that. + return true; + } + } + + protected static boolean isSelected(String fileToScan, Vector selectorList) { + boolean isInclusive = true; + if (selectorList != null ) { + PatternSet.SelectorEntry[] selectorEntries = + new PatternSet.SelectorEntry[selectorList.size()]; + selectorList.copyInto(selectorEntries); + boolean[] selectorIndices = new boolean[selectorEntries.length]; + + for (int i = 0; i < selectorEntries.length; i++) { + String type = selectorEntries[i].getType(); + String value = selectorEntries[i].getValue(); + String operation = selectorEntries[i].getOperation(); + Class c = (Class) selectorClasses.get(type); + if (c != null) { + FileSelector s = null; + try { + s = (FileSelector) c.newInstance(); + s.setValue(value); + s.setOperation(operation); + isInclusive = s.isSelected(fileToScan); + } catch (InstantiationException ie) { + } catch (IllegalAccessException ie) { + } + if (!isInclusive) { + break; + } + } + } + } + return isInclusive; + } + + /** + * Matches a path against a pattern. + * + * @param pattern the (non-null) pattern to match against + * @param str the (non-null) string (path) to match + * + * @return true when the pattern matches against the string. + * false otherwise. + */ + protected static boolean matchPath(String pattern, String str) { + return matchPath(pattern, null, str, true); + } + + protected static boolean matchPath(String pattern, + String str, boolean isCaseSensitive) { + return matchPath(pattern, null, str, isCaseSensitive); + } + + /** + * Matches a path against a pattern. + * + * @param pattern the (non-null) pattern to match against + * @param str the (non-null) string (path) to match + * @param isCaseSensitive must a case sensitive match be done? + * + * @return true when the pattern matches against the string. + * false otherwise. + */ + protected static boolean matchPath(String pattern, Vector selectorList, + String str, boolean isCaseSensitive) { + // When str starts with a File.separator, pattern has to start with a + // File.separator. + // When pattern starts with a File.separator, str has to start with a + // File.separator. + if (str.startsWith(File.separator) != + pattern.startsWith(File.separator)) { + return false; + } + + Vector patDirs = new Vector(); + StringTokenizer st = new StringTokenizer(pattern,File.separator); + while (st.hasMoreTokens()) { + patDirs.addElement(st.nextToken()); + } + + Vector strDirs = new Vector(); + st = new StringTokenizer(str,File.separator); + while (st.hasMoreTokens()) { + strDirs.addElement(st.nextToken()); + } + + int patIdxStart = 0; + int patIdxEnd = patDirs.size()-1; + int strIdxStart = 0; + int strIdxEnd = strDirs.size()-1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = (String)patDirs.elementAt(patIdxStart); + if (patDir.equals("**")) { + break; + } + if (!match(patDir,(String)strDirs.elementAt(strIdxStart), isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!patDirs.elementAt(i).equals("**")) { + return false; + } + } + return isSelected(str, selectorList); + } else { + if (patIdxStart > patIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } + } + + // up to last '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = (String)patDirs.elementAt(patIdxEnd); + if (patDir.equals("**")) { + break; + } + if (!match(patDir,(String)strDirs.elementAt(strIdxEnd), isCaseSensitive)) { + return false; + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!patDirs.elementAt(i).equals("**")) { + return false; + } + } + return isSelected(str, selectorList); + } + + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart+1; i <= patIdxEnd; i++) { + if (patDirs.elementAt(i).equals("**")) { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart+1) { + // '**/**' situation, so skip one + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp-patIdxStart-1); + int strLength = (strIdxEnd-strIdxStart+1); + int foundIdx = -1; +strLoop: + for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + String subPat = (String)patDirs.elementAt(patIdxStart+j+1); + String subStr = (String)strDirs.elementAt(strIdxStart+i+j); + if (!match(subPat,subStr, isCaseSensitive)) { + continue strLoop; + } + } + + foundIdx = strIdxStart+i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx+patLength; + } + + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!patDirs.elementAt(i).equals("**")) { + return false; + } + } + + return isSelected(str, selectorList); + } + + + /** + * Matches a string against a pattern. The pattern contains two special + * characters: + * '*' which means zero or more characters, + * '?' which means one and only one character. + * + * @param pattern the (non-null) pattern to match against + * @param str the (non-null) string that must be matched against the + * pattern + * + * @return true when the string matches against the pattern, + * false otherwise. + */ + public static boolean match(String pattern, String str) { + return match(pattern, str, true); + } + + + /** + * Matches a string against a pattern. The pattern contains two special + * characters: + * '*' which means zero or more characters, + * '?' which means one and only one character. + * + * @param pattern the (non-null) pattern to match against + * @param str the (non-null) string that must be matched against the + * pattern + * + * @return true when the string matches against the pattern, + * false otherwise. + */ + protected static boolean match(String pattern, String str, boolean isCaseSensitive) { + char[] patArr = pattern.toCharArray(); + char[] strArr = str.toCharArray(); + int patIdxStart = 0; + int patIdxEnd = patArr.length-1; + int strIdxStart = 0; + int strIdxEnd = strArr.length-1; + char ch; + + boolean containsStar = false; + for (int i = 0; i < patArr.length; i++) { + if (patArr[i] == '*') { + containsStar = true; + break; + } + } + + if (!containsStar) { + // No '*'s, so we make a shortcut + if (patIdxEnd != strIdxEnd) { + return false; // Pattern and string do not have the same size + } + for (int i = 0; i <= patIdxEnd; i++) { + ch = patArr[i]; + if (ch != '?') { + if (isCaseSensitive && ch != strArr[i]) { + return false;// Character mismatch + } + if (!isCaseSensitive && Character.toUpperCase(ch) != + Character.toUpperCase(strArr[i])) { + return false; // Character mismatch + } + } + } + return true; // String matches against pattern + } + + if (patIdxEnd == 0) { + return true; // Pattern contains only '*', which matches anything + } + + // Process characters before first star + while((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) { + if (ch != '?') { + if (isCaseSensitive && ch != strArr[strIdxStart]) { + return false;// Character mismatch + } + if (!isCaseSensitive && Character.toUpperCase(ch) != + Character.toUpperCase(strArr[strIdxStart])) { + return false;// Character mismatch + } + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (patArr[i] != '*') { + return false; + } + } + return true; + } + + // Process characters after last star + while((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) { + if (ch != '?') { + if (isCaseSensitive && ch != strArr[strIdxEnd]) { + return false;// Character mismatch + } + if (!isCaseSensitive && Character.toUpperCase(ch) != + Character.toUpperCase(strArr[strIdxEnd])) { + return false;// Character mismatch + } + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (patArr[i] != '*') { + return false; + } + } + return true; + } + + // process pattern between stars. padIdxStart and patIdxEnd point + // always to a '*'. + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart+1; i <= patIdxEnd; i++) { + if (patArr[i] == '*') { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart+1) { + // Two stars next to each other, skip the first one. + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp-patIdxStart-1); + int strLength = (strIdxEnd-strIdxStart+1); + int foundIdx = -1; + strLoop: + for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + ch = patArr[patIdxStart+j+1]; + if (ch != '?') { + if (isCaseSensitive && ch != strArr[strIdxStart+i+j]) { + continue strLoop; + } + if (!isCaseSensitive && Character.toUpperCase(ch) != + Character.toUpperCase(strArr[strIdxStart+i+j])) { + continue strLoop; + } + } + } + + foundIdx = strIdxStart+i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx+patLength; + } + + // All characters in the string are used. Check if only '*'s are left + // in the pattern. If so, we succeeded. Otherwise failure. + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (patArr[i] != '*') { + return false; + } + } + return true; + } + + + + /** + * Sets the basedir for scanning. This is the directory that is scanned + * recursively. All '/' and '\' characters are replaced by + * File.separatorChar. So the separator used need not match + * File.separatorChar. + * + * @param basedir the (non-null) basedir for scanning + */ + public void setBasedir(String basedir) { + setBasedir(new File(basedir.replace('/',File.separatorChar).replace('\\',File.separatorChar))); + } + + + + /** + * Sets the basedir for scanning. This is the directory that is scanned + * recursively. + * + * @param basedir the basedir for scanning + */ + public void setBasedir(File basedir) { + this.basedir = basedir; + } + + + + /** + * Gets the basedir that is used for scanning. This is the directory that + * is scanned recursively. + * + * @return the basedir that is used for scanning + */ + public File getBasedir() { + return basedir; + } + + + + /** + * Sets the case sensitivity of the file system + * + * @param specifies if the filesystem is case sensitive + */ + public void setCaseSensitive(boolean isCaseSensitive) { + this.isCaseSensitive = isCaseSensitive; + } + + /** + * Sets the set of include patterns to use. All '/' and '\' characters are + * replaced by File.separatorChar. So the separator used need + * not match File.separatorChar. + *

+ * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param includes list of include patterns + */ + public void setIncludes(String[] includes) { + if (includes == null) { + this.includes = null; + } else { + Pattern[] p = new Pattern[includes.length]; + for (int i = 0; i < includes.length; i++) { + p[i] = new Pattern(); + p[i].setPattern(includes[i]); + } + setIncludes(p); + } + } + + public void setIncludes(Pattern[] includes) { + if (includes == null) { + this.includes = null; + } else { + this.includes = new Pattern[includes.length]; + for (int i = 0; i < includes.length; i++) { + String pattern; + pattern = includes[i].getPattern().replace('/',File.separatorChar).replace('\\',File.separatorChar); + if (pattern.endsWith(File.separator)) { + pattern += "**"; + } + this.includes[i] = new Pattern(); + this.includes[i].setPattern(pattern); + this.includes[i].setSelectorList(includes[i].getSelectorList()); + } + } + } + + /** + * Sets the set of exclude patterns to use. All '/' and '\' characters are + * replaced by File.separatorChar. So the separator used need + * not match File.separatorChar. + *

+ * When a pattern ends with a '/' or '\', "**" is appended. + * + * @param excludes list of exclude patterns + */ + public void setExcludes(String[] excludes) { + if (excludes == null) { + this.excludes = null; + } else { + Pattern[] p = new Pattern[excludes.length]; + for (int i = 0; i < excludes.length; i++) { + p[i] = new Pattern(); + p[i].setPattern(excludes[i]); + } + setExcludes(p); + } + } + + public void setExcludes(Pattern[] excludes) { + if (excludes == null) { + this.excludes = null; + } else { + this.excludes = new Pattern[excludes.length]; + for (int i = 0; i < excludes.length; i++) { + String pattern; + pattern = excludes[i].getPattern().replace('/',File.separatorChar).replace('\\',File.separatorChar); + if (pattern.endsWith(File.separator)) { + pattern += "**"; + } + this.excludes[i] = new Pattern(); + this.excludes[i].setPattern(pattern); + this.excludes[i].setSelectorList(excludes[i].getSelectorList()); + } + } + } + + /** + * Has the scanner excluded or omitted any files or directories it + * came accross? + * + * @return true if all files and directories that have been found, + * are included. + */ + public boolean isEverythingIncluded() { + return everythingIncluded; + } + + + /** + * Scans the base directory for files that match at least one include + * pattern, and don't match any exclude patterns. + * + * @exception IllegalStateException when basedir was set incorrecly + */ + public void scan() { + if (basedir == null) { + throw new IllegalStateException("No basedir set"); + } + if (!basedir.exists()) { + throw new IllegalStateException("basedir " + basedir + + " does not exist"); + } + if (!basedir.isDirectory()) { + throw new IllegalStateException("basedir " + basedir + + " is not a directory"); + } + + if (includes == null) { + // No includes supplied, so set it to 'matches all' + includes = new Pattern[1]; + includes[0] = new Pattern(); + includes[0].setPattern("**"); + } + if (excludes == null) { + excludes = new Pattern[0]; + } + + filesIncluded = new Vector(); + filesNotIncluded = new Vector(); + filesExcluded = new Vector(); + dirsIncluded = new Vector(); + dirsNotIncluded = new Vector(); + dirsExcluded = new Vector(); + + if (isIncluded("")) { + if (!isExcluded("")) { + dirsIncluded.addElement(""); + } else { + dirsExcluded.addElement(""); + } + } else { + dirsNotIncluded.addElement(""); + } + scandir(basedir, "", true); + } + + /** + * Toplevel invocation for the scan. + * + *

Returns immediately if a slow scan has already been requested. + */ + protected void slowScan() { + if (haveSlowResults) { + return; + } + + String[] excl = new String[dirsExcluded.size()]; + dirsExcluded.copyInto(excl); + + String[] notIncl = new String[dirsNotIncluded.size()]; + dirsNotIncluded.copyInto(notIncl); + + for (int i=0; itrue when the name matches against at least one + * include pattern, false otherwise. + */ + protected boolean isIncluded(String name) { + for (int i = 0; i < includes.length; i++) { + if (matchPath(includes[i].getPattern(), + includes[i].getSelectorList(), + name, isCaseSensitive)) { + return true; + } + } + return false; + } + + /** + * Tests whether a name matches the start of at least one include pattern. + * + * @param name the name to match + * @return true when the name matches against at least one + * include pattern, false otherwise. + */ + protected boolean couldHoldIncluded(String name) { + for (int i = 0; i < includes.length; i++) { + if (matchPatternStart(includes[i].getPattern(),name, isCaseSensitive)) { + return true; + } + } + return false; + } + + /** + * Tests whether a name matches against at least one exclude pattern. + * + * @param name the name to match + * @return true when the name matches against at least one + * exclude pattern, false otherwise. + */ + protected boolean isExcluded(String name) { + for (int i = 0; i < excludes.length; i++) { + if (matchPath(excludes[i].getPattern(), + excludes[i].getSelectorList(), + name, isCaseSensitive)) { + return true; + } + } + return false; + } + + + /** + * Get the names of the files that matched at least one of the include + * patterns, and matched none of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the files + */ + public String[] getIncludedFiles() { + int count = filesIncluded.size(); + String[] files = new String[count]; + for (int i = 0; i < count; i++) { + files[i] = (String)filesIncluded.elementAt(i); + } + return files; + } + + + + /** + * Get the names of the files that matched at none of the include patterns. + * The names are relative to the basedir. + * + * @return the names of the files + */ + public String[] getNotIncludedFiles() { + slowScan(); + int count = filesNotIncluded.size(); + String[] files = new String[count]; + for (int i = 0; i < count; i++) { + files[i] = (String)filesNotIncluded.elementAt(i); + } + return files; + } + + + + /** + * Get the names of the files that matched at least one of the include + * patterns, an matched also at least one of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the files + */ + public String[] getExcludedFiles() { + slowScan(); + int count = filesExcluded.size(); + String[] files = new String[count]; + for (int i = 0; i < count; i++) { + files[i] = (String)filesExcluded.elementAt(i); + } + return files; + } + + + + /** + * Get the names of the directories that matched at least one of the include + * patterns, an matched none of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the directories + */ + public String[] getIncludedDirectories() { + int count = dirsIncluded.size(); + String[] directories = new String[count]; + for (int i = 0; i < count; i++) { + directories[i] = (String)dirsIncluded.elementAt(i); + } + return directories; + } + + + + /** + * Get the names of the directories that matched at none of the include + * patterns. + * The names are relative to the basedir. + * + * @return the names of the directories + */ + public String[] getNotIncludedDirectories() { + slowScan(); + int count = dirsNotIncluded.size(); + String[] directories = new String[count]; + for (int i = 0; i < count; i++) { + directories[i] = (String)dirsNotIncluded.elementAt(i); + } + return directories; + } + + + + /** + * Get the names of the directories that matched at least one of the include + * patterns, an matched also at least one of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the directories + */ + public String[] getExcludedDirectories() { + slowScan(); + int count = dirsExcluded.size(); + String[] directories = new String[count]; + for (int i = 0; i < count; i++) { + directories[i] = (String)dirsExcluded.elementAt(i); + } + return directories; + } + + + + /** + * Adds the array with default exclusions to the current exclusions set. + * + */ + public void addDefaultExcludes() { + int excludesLength = excludes == null ? 0 : excludes.length; + Pattern[] newExcludes; + newExcludes = new Pattern[excludesLength + DEFAULTEXCLUDES.length]; + if (excludesLength > 0) { + System.arraycopy(excludes,0,newExcludes,0,excludesLength); + } + for (int i = 0; i < DEFAULTEXCLUDES.length; i++) { + newExcludes[i+excludesLength] = new Pattern(); + newExcludes[i+excludesLength].setPattern(DEFAULTEXCLUDES[i].replace('/',File.separatorChar).replace('\\',File.separatorChar)); + } + + Pattern[] ep = new Pattern[newExcludes.length]; + for (int i = 0; i < ep.length; i++) { + ep[i] = new Pattern(); + ep[i].setPattern(newExcludes[i].getPattern()); + } + + excludes = ep; + } +} diff --git a/proposal/sandbox/selectors/src/main/org/apache/tools/ant/FileScanner.java b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/FileScanner.java new file mode 100644 index 000000000..a971a8c8d --- /dev/null +++ b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/FileScanner.java @@ -0,0 +1,175 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant; + +import java.io.File; +import org.apache.tools.ant.types.Pattern; + +/** + * An interface used to describe the actions required by any type of + * directory scanner. + */ +public interface FileScanner { + /** + * Adds an array with default exclusions to the current exclusions set. + * + */ + void addDefaultExcludes(); + /** + * Gets the basedir that is used for scanning. This is the directory that + * is scanned recursively. + * + * @return the basedir that is used for scanning + */ + File getBasedir(); + /** + * Get the names of the directories that matched at least one of the include + * patterns, an matched also at least one of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the directories + */ + String[] getExcludedDirectories(); + /** + * Get the names of the files that matched at least one of the include + * patterns, an matched also at least one of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the files + */ + String[] getExcludedFiles(); + /** + * Get the names of the directories that matched at least one of the include + * patterns, an matched none of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the directories + */ + String[] getIncludedDirectories(); + /** + * Get the names of the files that matched at least one of the include + * patterns, an matched none of the exclude patterns. + * The names are relative to the basedir. + * + * @return the names of the files + */ + String[] getIncludedFiles(); + /** + * Get the names of the directories that matched at none of the include + * patterns. + * The names are relative to the basedir. + * + * @return the names of the directories + */ + String[] getNotIncludedDirectories(); + /** + * Get the names of the files that matched at none of the include patterns. + * The names are relative to the basedir. + * + * @return the names of the files + */ + String[] getNotIncludedFiles(); + /** + * Scans the base directory for files that match at least one include + * pattern, and don't match any exclude patterns. + * + * @exception IllegalStateException when basedir was set incorrecly + */ + void scan(); + /** + * Sets the basedir for scanning. This is the directory that is scanned + * recursively. + * + * @param basedir the (non-null) basedir for scanning + */ + void setBasedir(String basedir); + /** + * Sets the basedir for scanning. This is the directory that is scanned + * recursively. + * + * @param basedir the basedir for scanning + */ + void setBasedir(File basedir); + /** + * Sets the set of exclude patterns to use. + * + * @param excludes list of exclude patterns + */ + void setExcludes(String[] excludes); + /** + * Sets the set of include patterns to use. + * + * @param includes list of include patterns + */ + void setIncludes(String[] includes); + /** + * Sets the set of exclude patterns to use. + * + * @param excludes list of exclude patterns + */ + void setExcludes(Pattern[] excludes); + /** + * Sets the set of include patterns to use. + * + * @param includes list of include patterns + */ + void setIncludes(Pattern[] includes); + + /** + * Sets the case sensitivity of the file system + * + * @param specifies if the filesystem is case sensitive + */ + void setCaseSensitive(boolean isCaseSensitive); +} diff --git a/proposal/sandbox/selectors/src/main/org/apache/tools/ant/selectors/FileSelector.java b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/selectors/FileSelector.java new file mode 100644 index 000000000..8b37c759e --- /dev/null +++ b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/selectors/FileSelector.java @@ -0,0 +1,67 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.selectors; + +import java.io.File; + +/** + * File Selector API. + * + * @author Magesh Umasankar + */ +public interface FileSelector { + public void setValue(final String value); + public void setOperation(final String operation); + public boolean isSelected(final String file); +} diff --git a/proposal/sandbox/selectors/src/main/org/apache/tools/ant/selectors/FileTypeSelector.java b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/selectors/FileTypeSelector.java new file mode 100644 index 000000000..009a5e6cd --- /dev/null +++ b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/selectors/FileTypeSelector.java @@ -0,0 +1,128 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + package org.apache.tools.ant.selectors; + +import java.io.File; + +/** + * File selector that performs selection based on + * file type - file and directory. + * + * @author Magesh Umasankar + */ +public class FileTypeSelector implements FileSelector { + private String value = null; + private String operation = "equals"; + private boolean cached = false; + private boolean negate = false; + private boolean checkForFile = false; + private boolean checkForDir = false; + + public void setCached(final boolean cached) { + this.cached = cached; + } + + public boolean isCached() { + return cached; + } + + public void setValue(final String value) { + this.value = value; + setCached(false); + } + + public void setOperation(final String operation) { + this.operation = operation; + setCached(false); + } + + public void doCache() { + if (!isCached()) { + if (value == null) { + throw new NullPointerException("value must not be null."); + } + if (value.equalsIgnoreCase("file")) { + checkForFile = true; + checkForDir = false; + } else if (value.equalsIgnoreCase("directory")) { + checkForDir = true; + checkForFile = false; + } + if (!operation.equalsIgnoreCase("equals")) { + negate = true; + } else { + negate = false; + } + setCached(true); + } + } + + public boolean isSelected(final String file) { + doCache(); + if (file == null) { + throw new NullPointerException("file must not be null."); + } + boolean retValue = false; + File f = new File(file); + if (checkForFile) { + retValue = f.isFile(); + } else if (checkForDir) { + retValue = f.isDirectory(); + } + if (negate) { + retValue = !retValue; + } + return retValue; + } +} diff --git a/proposal/sandbox/selectors/src/main/org/apache/tools/ant/selectors/defaults.properties b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/selectors/defaults.properties new file mode 100644 index 000000000..4887f1131 --- /dev/null +++ b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/selectors/defaults.properties @@ -0,0 +1 @@ +type=org.apache.tools.ant.selectors.FileTypeSelector diff --git a/proposal/sandbox/selectors/src/main/org/apache/tools/ant/types/FileSet.java b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/types/FileSet.java new file mode 100644 index 000000000..8f983ec09 --- /dev/null +++ b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/types/FileSet.java @@ -0,0 +1,341 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant.types; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.FileScanner; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; + +import java.io.File; +import java.util.Stack; +import java.util.Vector; + +/** + * Moved out of MatchingTask to make it a standalone object that could + * be referenced (by scripts for example). + * + * @author Arnout J. Kuiper ajkuiper@wxs.nl + * @author Stefano Mazzocchi stefano@apache.org + * @author Sam Ruby rubys@us.ibm.com + * @author Jon S. Stevens jon@clearink.com + * @author Stefan Bodewig + * @author Magesh Umasankar + */ +public class FileSet extends DataType implements Cloneable { + + private PatternSet defaultPatterns = new PatternSet(); + private Vector additionalPatterns = new Vector(); + + private File dir; + private boolean useDefaultExcludes = true; + private boolean isCaseSensitive = true; + + public FileSet() { + super(); + } + + protected FileSet(FileSet fileset) { + this.dir = fileset.dir; + this.defaultPatterns = fileset.defaultPatterns; + this.additionalPatterns = fileset.additionalPatterns; + this.useDefaultExcludes = fileset.useDefaultExcludes; + this.isCaseSensitive = fileset.isCaseSensitive; + setProject(getProject()); + } + + + + /** + * Makes this instance in effect a reference to another PatternSet + * instance. + * + *

You must not set another attribute or nest elements inside + * this element if you make it a reference.

+ */ + public void setRefid(Reference r) throws BuildException { + if (dir != null || defaultPatterns.hasPatterns()) { + throw tooManyAttributes(); + } + if (!additionalPatterns.isEmpty()) { + throw noChildrenAllowed(); + } + super.setRefid(r); + } + + public void setDir(File dir) throws BuildException { + if (isReference()) { + throw tooManyAttributes(); + } + + this.dir = dir; + } + + public File getDir(Project p) { + if (isReference()) { + return getRef(p).getDir(p); + } + return dir; + } + + public PatternSet createPatternSet() { + if (isReference()) { + throw noChildrenAllowed(); + } + PatternSet patterns = new PatternSet(); + additionalPatterns.addElement(patterns); + return patterns; + } + + /** + * add a name entry on the include list + */ + public PatternSet.NameEntry createInclude() { + if (isReference()) { + throw noChildrenAllowed(); + } + return defaultPatterns.createInclude(); + } + + /** + * add a name entry on the include files list + */ + public PatternSet.NameEntry createIncludesFile() { + if (isReference()) { + throw noChildrenAllowed(); + } + return defaultPatterns.createIncludesFile(); + } + + /** + * add a name entry on the exclude list + */ + public PatternSet.NameEntry createExclude() { + if (isReference()) { + throw noChildrenAllowed(); + } + return defaultPatterns.createExclude(); + } + + /** + * add a name entry on the include files list + */ + public PatternSet.NameEntry createExcludesFile() { + if (isReference()) { + throw noChildrenAllowed(); + } + return defaultPatterns.createExcludesFile(); + } + + /** + * Sets the set of include patterns. Patterns may be separated by a comma + * or a space. + * + * @param includes the string containing the include patterns + */ + public void setIncludes(String includes) { + if (isReference()) { + throw tooManyAttributes(); + } + + defaultPatterns.setIncludes(includes); + } + + /** + * Sets the set of exclude patterns. Patterns may be separated by a comma + * or a space. + * + * @param excludes the string containing the exclude patterns + */ + public void setExcludes(String excludes) { + if (isReference()) { + throw tooManyAttributes(); + } + + defaultPatterns.setExcludes(excludes); + } + + /** + * Sets the name of the file containing the includes patterns. + * + * @param incl The file to fetch the include patterns from. + */ + public void setIncludesfile(File incl) throws BuildException { + if (isReference()) { + throw tooManyAttributes(); + } + + defaultPatterns.setIncludesfile(incl); + } + + /** + * Sets the name of the file containing the includes patterns. + * + * @param excl The file to fetch the exclude patterns from. + */ + public void setExcludesfile(File excl) throws BuildException { + if (isReference()) { + throw tooManyAttributes(); + } + + defaultPatterns.setExcludesfile(excl); + } + + /** + * Sets whether default exclusions should be used or not. + * + * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions + * should be used, "false"|"off"|"no" when they + * shouldn't be used. + */ + public void setDefaultexcludes(boolean useDefaultExcludes) { + if (isReference()) { + throw tooManyAttributes(); + } + + this.useDefaultExcludes = useDefaultExcludes; + } + + /** + * Sets case sensitivity of the file system + * + * @param isCaseSensitive "true"|"on"|"yes" if file system is case + * sensitive, "false"|"off"|"no" when not. + */ + public void setCaseSensitive(boolean isCaseSensitive) { + this.isCaseSensitive = isCaseSensitive; + } + + /** + * Returns the directory scanner needed to access the files to process. + */ + public DirectoryScanner getDirectoryScanner(Project p) { + if (isReference()) { + return getRef(p).getDirectoryScanner(p); + } + + if (dir == null) { + throw new BuildException("No directory specified for fileset."); + } + + if (!dir.exists()) { + throw new BuildException(dir.getAbsolutePath()+" not found."); + } + if (!dir.isDirectory()) { + throw new BuildException(dir.getAbsolutePath()+" is not a directory."); + } + + DirectoryScanner ds = new DirectoryScanner(); + setupDirectoryScanner(ds, p); + ds.scan(); + return ds; + } + + public void setupDirectoryScanner(FileScanner ds, Project p) { + if (ds == null) { + throw new IllegalArgumentException("ds cannot be null"); + } + + ds.setBasedir(dir); + + for (int i=0; i. + */ + package org.apache.tools.ant.types; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectHelper; +import org.apache.tools.ant.BuildException; + +import java.io.*; +import java.util.Enumeration; +import java.util.Stack; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + * Named collection of include/exclude tags. + * + * @author Magesh Umasankar + */ +public class Pattern { + private String pattern = null; + private Vector selectorList = null; + + /** + * Set the pattern + * @param pattern the pattern to match + */ + public void setPattern(String pattern) { + this.pattern = pattern; + } + + /** + * Set the list of Selector entries + * @param selectorList the vector list of 'SelectorEntry's + */ + public void setSelectorList(Vector selectorList) { + this.selectorList = selectorList; + } + + /** + * Get the pattern + */ + public String getPattern() { + return pattern; + } + + /** + * Get the list of Selector entries + */ + public Vector getSelectorList() { + return selectorList; + } +} diff --git a/proposal/sandbox/selectors/src/main/org/apache/tools/ant/types/PatternSet.java b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/types/PatternSet.java new file mode 100644 index 000000000..276d5b079 --- /dev/null +++ b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/types/PatternSet.java @@ -0,0 +1,593 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant.types; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectHelper; +import org.apache.tools.ant.BuildException; + +import java.io.File; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Stack; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + * Named collection of include/exclude tags. + * + *

Moved out of MatchingTask to make it a standalone object that + * could be referenced (by scripts for example). + * + * @author Arnout J. Kuiper ajkuiper@wxs.nl + * @author Stefano Mazzocchi stefano@apache.org + * @author Sam Ruby rubys@us.ibm.com + * @author Jon S. Stevens jon@clearink.com + * @author Stefan Bodewig + * @author Magesh Umasankar + */ +public class PatternSet extends DataType { + private Vector includeList = new Vector(); + private Vector excludeList = new Vector(); + private Vector includesFileList = new Vector(); + private Vector excludesFileList = new Vector(); + + + /** + * inner class to hold a selector list. A SelectorEntry + * is made up of the pattern and selection detail. + */ + public static class SelectorEntry { + private String type; + private String value; + private String operation; + + public void setType(String t) { + this.type = t; + } + + public void setValue(String val) { + this.value = val; + } + + public void setOperation(String op) { + this.operation = op; + } + + public String getType() { + return type; + } + + public String getValue() { + return value; + } + + public String getOperation() { + return operation; + } + } + + /** + * inner class to hold a name on list. "If" and "Unless" attributes + * may be used to invalidate the entry based on the existence of a + * property (typically set thru the use of the Available task). + */ + public class NameEntry { + private String name; + private String ifCond; + private String unlessCond; + private Vector selectorList = new Vector(); + + public void setName(String name) { + this.name = name; + } + + public void setIf(String cond) { + ifCond = cond; + } + + public void setUnless(String cond) { + unlessCond = cond; + } + + /** + * Include/Exclude can contain nested selectors + */ + public SelectorEntry createSelector() { + if (isReference()) { + throw noChildrenAllowed(); + } + return addSelectorToList(selectorList); + } + + /** + * add a selector entry to the given list + */ + private SelectorEntry addSelectorToList(final Vector list) { + final SelectorEntry result = new SelectorEntry(); + list.addElement(result); + return result; + } + + public String getName() { + return name; + } + + public Vector getSelectorList() { + return selectorList; + } + + public String evalName(Project p) { + return valid(p) ? name : null; + } + + private boolean valid(Project p) { + if (ifCond != null && p.getProperty(ifCond) == null) { + return false; + } else if (unlessCond != null && p.getProperty(unlessCond) != null) { + return false; + } + return true; + } + + public String toString() { + StringBuffer buf = new StringBuffer(name); + if ((ifCond != null) || (unlessCond != null)) { + buf.append(":"); + String connector = ""; + + if (ifCond != null) { + buf.append("if->"); + buf.append(ifCond); + connector = ";"; + } + if (unlessCond != null) { + buf.append(connector); + buf.append("unless->"); + buf.append(unlessCond); + } + } + + return buf.toString(); + } + + public void setSelectorList(Vector list) { + this.selectorList = list; + } + } + + public PatternSet() { + super(); + } + + /** + * Makes this instance in effect a reference to another PatternSet + * instance. + * + *

You must not set another attribute or nest elements inside + * this element if you make it a reference.

+ */ + public void setRefid(Reference r) throws BuildException { + if (!includeList.isEmpty() || !excludeList.isEmpty()) { + throw tooManyAttributes(); + } + super.setRefid(r); + } + + /** + * add a name entry on the include list + */ + public NameEntry createInclude() { + if (isReference()) { + throw noChildrenAllowed(); + } + return addPatternToList(includeList); + } + + /** + * add a name entry on the include files list + */ + public NameEntry createIncludesFile() { + if (isReference()) { + throw noChildrenAllowed(); + } + return addPatternToList(includesFileList); + } + + /** + * add a name entry on the exclude list + */ + public NameEntry createExclude() { + if (isReference()) { + throw noChildrenAllowed(); + } + return addPatternToList(excludeList); + } + + /** + * add a name entry on the exclude files list + */ + public NameEntry createExcludesFile() { + if (isReference()) { + throw noChildrenAllowed(); + } + return addPatternToList(excludesFileList); + } + + /** + * Sets the set of include patterns. Patterns may be separated by a comma + * or a space. + * + * @param includes the string containing the include patterns + */ + public void setIncludes(String includes) { + if (isReference()) { + throw tooManyAttributes(); + } + if (includes != null && includes.length() > 0) { + StringTokenizer tok = new StringTokenizer(includes, ", ", false); + while (tok.hasMoreTokens()) { + createInclude().setName(tok.nextToken()); + } + } + } + + /** + * Sets the set of exclude patterns. Patterns may be separated by a comma + * or a space. + * + * @param excludes the string containing the exclude patterns + */ + public void setExcludes(String excludes) { + if (isReference()) { + throw tooManyAttributes(); + } + if (excludes != null && excludes.length() > 0) { + StringTokenizer tok = new StringTokenizer(excludes, ", ", false); + while (tok.hasMoreTokens()) { + createExclude().setName(tok.nextToken()); + } + } + } + + /** + * add a name entry to the given list + */ + private NameEntry addPatternToList(Vector list) { + NameEntry result = new NameEntry(); + list.addElement(result); + return result; + } + + /** + * Sets the name of the file containing the includes patterns. + * + * @param includesFile The file to fetch the include patterns from. + */ + public void setIncludesfile(File includesFile) throws BuildException { + if (isReference()) { + throw tooManyAttributes(); + } + createIncludesFile().setName(includesFile.getAbsolutePath()); + } + + /** + * Sets the name of the file containing the excludes patterns. + * + * @param excludesFile The file to fetch the exclude patterns from. + */ + public void setExcludesfile(File excludesFile) throws BuildException { + if (isReference()) { + throw tooManyAttributes(); + } + createExcludesFile().setName(excludesFile.getAbsolutePath()); + } + + /** + * Reads path matching patterns from a file and adds them to the + * includes or excludes list (as appropriate). + */ + private void readPatterns(File patternfile, Vector patternlist, Project p) + throws BuildException { + + BufferedReader patternReader = null; + try { + // Get a FileReader + patternReader = + new BufferedReader(new FileReader(patternfile)); + + // Create one NameEntry in the appropriate pattern list for each + // line in the file. + String line = patternReader.readLine(); + while (line != null) { + if (line.length() > 0) { + line = p.replaceProperties(line); + addPatternToList(patternlist).setName(line); + } + line = patternReader.readLine(); + } + } catch(IOException ioe) { + String msg = "An error occured while reading from pattern file: " + + patternfile; + throw new BuildException(msg, ioe); + } finally { + if( null != patternReader ) { + try { + patternReader.close(); + } catch(IOException ioe) { + //Ignore exception + } + } + } + } + + public void append2(PatternSet other, Project p) { + if (isReference()) { + throw new BuildException("Cannot append to a reference"); + } + Pattern[] incl = other.getIncludePatterns2(p); + if (incl != null) { + for (int i=0; i 0 || excludesFileList.size() > 0 + || includeList.size() > 0 || excludeList.size() > 0; + } + + /** + * Performs the check for circular references and returns the + * referenced PatternSet. + */ + private PatternSet getRef(Project p) { + if (!checked) { + Stack stk = new Stack(); + stk.push(this); + dieOnCircularReference(stk, p); + } + + Object o = ref.getReferencedObject(p); + if (!(o instanceof PatternSet)) { + String msg = ref.getRefId()+" doesn\'t denote a patternset"; + throw new BuildException(msg); + } else { + return (PatternSet) o; + } + } + + + /** + * Convert a vector of NameEntry elements into an array of Patterns + */ + private Pattern[] makeArray2(Vector list, Project p) { + if (list.size() == 0) return null; + + Vector tmpPatterns = new Vector(); + for (Enumeration e = list.elements() ; e.hasMoreElements() ;) { + NameEntry ne = (NameEntry)e.nextElement(); + String pattern = ne.evalName(p); + if (pattern != null && pattern.length() > 0) { + Pattern pat = new Pattern(); + pat.setPattern(pattern); + pat.setSelectorList(ne.getSelectorList()); + tmpPatterns.addElement(pat); + } + } + + Pattern result[] = new Pattern[tmpPatterns.size()]; + tmpPatterns.copyInto(result); + return result; + } + + /** + * Convert a vector of NameEntry elements into an array of Strings. + */ + private String[] makeArray(Vector list, Project p) { + if (list.size() == 0) { + return null; + } + + Vector tmpNames = new Vector(); + for (Enumeration e = list.elements() ; e.hasMoreElements() ;) { + NameEntry ne = (NameEntry)e.nextElement(); + String pattern = ne.evalName(p); + if (pattern != null && pattern.length() > 0) { + tmpNames.addElement(pattern); + } + } + + String result[] = new String[tmpNames.size()]; + tmpNames.copyInto(result); + return result; + } + + /** + * Read includesfile ot excludesfile if not already done so. + */ + private void readFiles(Project p) { + if (includesFileList.size() > 0) { + Enumeration e = includesFileList.elements(); + while (e.hasMoreElements()) { + NameEntry ne = (NameEntry)e.nextElement(); + String fileName = ne.evalName(p); + if (fileName != null) { + File inclFile = p.resolveFile(fileName); + if (!inclFile.exists()) { + throw new BuildException("Includesfile " + + inclFile.getAbsolutePath() + + " not found."); + } + readPatterns(inclFile, includeList, p); + } + } + includesFileList.removeAllElements(); + } + + if (excludesFileList.size() > 0) { + Enumeration e = excludesFileList.elements(); + while (e.hasMoreElements()) { + NameEntry ne = (NameEntry)e.nextElement(); + String fileName = ne.evalName(p); + if (fileName != null) { + File exclFile = p.resolveFile(fileName); + if (!exclFile.exists()) { + throw new BuildException("Excludesfile " + + exclFile.getAbsolutePath() + + " not found."); + } + readPatterns(exclFile, excludeList, p); + } + } + excludesFileList.removeAllElements(); + } + } + + public String toString() + { + return "patternSet{ includes: " + includeList + + " excludes: " + excludeList + " }"; + } + +} diff --git a/proposal/sandbox/selectors/src/main/org/apache/tools/ant/types/ZipScanner.java b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/types/ZipScanner.java new file mode 100644 index 000000000..9c2e4101d --- /dev/null +++ b/proposal/sandbox/selectors/src/main/org/apache/tools/ant/types/ZipScanner.java @@ -0,0 +1,140 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.tools.ant.types; + +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.types.Pattern; +import java.io.File; + +/** + * ZipScanner accesses the pattern matching algorithm in DirectoryScanner, + * which are protected methods that can only be accessed by subclassing. + * + * This implementation of FileScanner defines getIncludedFiles to return + * only the Zip File which is being scanned, not the matching Zip entries. + * Arguably, it should return the matching entries, however this would + * complicate existing code which assumes that FileScanners return a + * set of file system files that can be accessed directly. + * + * @author Don Ferguson don@bea.com + */ +public class ZipScanner extends DirectoryScanner { + + /** + * The zip file which should be scanned. + */ + protected File srcFile; + + /** + * Sets the srcFile for scanning. This is the jar or zip file that is scanned + * for matching entries. + * + * @param srcFile the (non-null) zip file name for scanning + */ + public void setSrc(File srcFile) { + this.srcFile = srcFile; + } + + /** + * Returns the zip file itself, not the matching entries within the zip file. + * This keeps the uptodate test in the Zip task simple; otherwise we'd need + * to treat zip filesets specially. + * + * @return the source file from which entries will be extracted. + */ + public String[] getIncludedFiles() { + String[] result = new String[1]; + result[0] = srcFile.getAbsolutePath(); + return result; + } + + /** + * Returns an empty list of directories to create. + */ + public String[] getIncludedDirectories() { + return new String[0]; + } + + /** + * Initialize DirectoryScanner data structures. + */ + public void init() { + if (includes == null) { + // No includes supplied, so set it to 'matches all' + includes = new Pattern[1]; + includes[0] = new Pattern(); + includes[0].setPattern("**"); + } + if (excludes == null) { + excludes = new Pattern[0]; + } + } + + /** + * Matches a jar entry against the includes/excludes list, + * normalizing the path separator. + * + * @param path the (non-null) path name to test for inclusion + * + * @return true if the path should be included + * false otherwise. + */ + public boolean match(String path) { + String vpath = path.replace('/', File.separatorChar). + replace('\\', File.separatorChar); + return isIncluded(vpath) && !isExcluded(vpath); + } + +}