0001: /*
0002: * Copyright 2000-2004 The Apache Software Foundation
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: *
0016: */
0017:
0018: package org.drftpd.org.apache.tools.ant;
0019:
0020: import java.io.FileNotFoundException;
0021: import java.util.Arrays;
0022: import java.util.HashMap;
0023: import java.util.HashSet;
0024: import java.util.Hashtable;
0025: import java.util.Map;
0026: import java.util.Set;
0027: import java.util.Vector;
0028:
0029: import org.apache.log4j.Logger;
0030: import org.drftpd.org.apache.tools.ant.types.selectors.FileSelector;
0031: import org.drftpd.org.apache.tools.ant.types.selectors.SelectorUtils;
0032: import org.drftpd.org.apache.tools.ant.util.FileUtils;
0033: import org.drftpd.remotefile.LinkedRemoteFileInterface;
0034:
0035: /**
0036: * Class for scanning a directory for files/directories which match certain
0037: * criteria.
0038: * <p>
0039: * These criteria consist of selectors and patterns which have been specified.
0040: * With the selectors you can select which files you want to have included.
0041: * Files which are not selected are excluded. With patterns you can include
0042: * or exclude files based on their filename.
0043: * <p>
0044: * The idea is simple. A given directory is recursively scanned for all files
0045: * and directories. Each file/directory is matched against a set of selectors,
0046: * including special support for matching against filenames with include and
0047: * and exclude patterns. Only files/directories which match at least one
0048: * pattern of the include pattern list or other file selector, and don't match
0049: * any pattern of the exclude pattern list or fail to match against a required
0050: * selector will be placed in the list of files/directories found.
0051: * <p>
0052: * When no list of include patterns is supplied, "**" will be used, which
0053: * means that everything will be matched. When no list of exclude patterns is
0054: * supplied, an empty list is used, such that nothing will be excluded. When
0055: * no selectors are supplied, none are applied.
0056: * <p>
0057: * The filename pattern matching is done as follows:
0058: * The name to be matched is split up in path segments. A path segment is the
0059: * name of a directory or file, which is bounded by
0060: * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
0061: * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
0062: * "def","ghi" and "xyz.java".
0063: * The same is done for the pattern against which should be matched.
0064: * <p>
0065: * The segments of the name and the pattern are then matched against each
0066: * other. When '**' is used for a path segment in the pattern, it matches
0067: * zero or more path segments of the name.
0068: * <p>
0069: * There is a special case regarding the use of <code>File.separator</code>s
0070: * at the beginning of the pattern and the string to match:<br>
0071: * When a pattern starts with a <code>File.separator</code>, the string
0072: * to match must also start with a <code>File.separator</code>.
0073: * When a pattern does not start with a <code>File.separator</code>, the
0074: * string to match may not start with a <code>File.separator</code>.
0075: * When one of these rules is not obeyed, the string will not
0076: * match.
0077: * <p>
0078: * When a name path segment is matched against a pattern path segment, the
0079: * following special characters can be used:<br>
0080: * '*' matches zero or more characters<br>
0081: * '?' matches one character.
0082: * <p>
0083: * Examples:
0084: * <p>
0085: * "**\*.class" matches all .class files/dirs in a directory tree.
0086: * <p>
0087: * "test\a??.java" matches all files/dirs which start with an 'a', then two
0088: * more characters and then ".java", in a directory called test.
0089: * <p>
0090: * "**" matches everything in a directory tree.
0091: * <p>
0092: * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where
0093: * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
0094: * <p>
0095: * Case sensitivity may be turned off if necessary. By default, it is
0096: * turned on.
0097: * <p>
0098: * Example of usage:
0099: * <pre>
0100: * String[] includes = {"**\\*.class"};
0101: * String[] excludes = {"modules\\*\\**"};
0102: * ds.setIncludes(includes);
0103: * ds.setExcludes(excludes);
0104: * ds.setBasedir(new File("test"));
0105: * ds.setCaseSensitive(true);
0106: * ds.scan();
0107: *
0108: * System.out.println("FILES:");
0109: * String[] files = ds.getIncludedFiles();
0110: * for (int i = 0; i < files.length; i++) {
0111: * System.out.println(files[i]);
0112: * }
0113: * </pre>
0114: * This will scan a directory called test for .class files, but excludes all
0115: * files in all proper subdirectories of a directory called "modules"
0116: *
0117: */
0118: public class DirectoryScanner
0119: /*implements FileScanner, SelectorScanner, ResourceFactory*/{
0120:
0121: /** Is OpenVMS the operating system we're running on? */
0122: //private static final boolean ON_VMS = Os.isFamily("openvms");
0123: /**
0124: * Patterns which should be excluded by default.
0125: *
0126: * <p>Note that you can now add patterns to the list of default
0127: * excludes. Added patterns will not become part of this array
0128: * that has only been kept around for backwards compatibility
0129: * reasons.</p>
0130: *
0131: * @deprecated use the {@link #getDefaultExcludes
0132: * getDefaultExcludes} method instead.
0133: */
0134: // protected static final String[] DEFAULTEXCLUDES = {
0135: // // Miscellaneous typical temporary files
0136: // "**/*~",
0137: // "**/#*#",
0138: // "**/.#*",
0139: // "**/%*%",
0140: // "**/._*",
0141: //
0142: // // CVS
0143: // "**/CVS",
0144: // "**/CVS/**",
0145: // "**/.cvsignore",
0146: //
0147: // // SCCS
0148: // "**/SCCS",
0149: // "**/SCCS/**",
0150: //
0151: // // Visual SourceSafe
0152: // "**/vssver.scc",
0153: //
0154: // // Subversion
0155: // "**/.svn",
0156: // "**/.svn/**",
0157: //
0158: // // Mac
0159: // "**/.DS_Store"
0160: // };
0161: /**
0162: * Patterns which should be excluded by default.
0163: *
0164: * @see #addDefaultExcludes()
0165: */
0166: // private static Vector defaultExcludes = new Vector();
0167: // static {
0168: // resetDefaultExcludes();
0169: // }
0170: /** The base directory to be scanned. */
0171: protected LinkedRemoteFileInterface _basedir;
0172:
0173: /** The patterns for the files to be included. */
0174: protected String[] _includes;
0175:
0176: /** The patterns for the files to be excluded. */
0177: protected String[] _excludes;
0178:
0179: /** Selectors that will filter which files are in our candidate list. */
0180: protected FileSelector[] _selectors = null;
0181:
0182: /** The files which matched at least one include and no excludes
0183: * and were selected.
0184: */
0185: protected Vector<String> filesIncluded;
0186:
0187: /** The files which did not match any includes or selectors. */
0188: protected Vector<String> filesNotIncluded;
0189:
0190: /**
0191: * The files which matched at least one include and at least
0192: * one exclude.
0193: */
0194: protected Vector<String> filesExcluded;
0195:
0196: /** The directories which matched at least one include and no excludes
0197: * and were selected.
0198: */
0199: protected Vector<String> dirsIncluded;
0200:
0201: /** The directories which were found and did not match any includes. */
0202: protected Vector<String> dirsNotIncluded;
0203:
0204: /**
0205: * The directories which matched at least one include and at least one
0206: * exclude.
0207: */
0208: protected Vector<String> dirsExcluded;
0209:
0210: /** The files which matched at least one include and no excludes and
0211: * which a selector discarded.
0212: */
0213: protected Vector<String> filesDeselected;
0214:
0215: /** The directories which matched at least one include and no excludes
0216: * but which a selector discarded.
0217: */
0218: protected Vector<String> dirsDeselected;
0219:
0220: /** Whether or not our results were built by a slow scan. */
0221: protected boolean haveSlowResults = false;
0222:
0223: /**
0224: * Whether or not the file system should be treated as a case sensitive
0225: * one.
0226: */
0227: protected static final boolean _isCaseSensitive = false;
0228:
0229: /**
0230: * Whether or not symbolic links should be followed.
0231: *
0232: * @since Ant 1.5
0233: */
0234: private boolean _followSymlinks = true;
0235:
0236: /** Helper. */
0237: private static final FileUtils FILE_UTILS = FileUtils
0238: .newFileUtils();
0239:
0240: /** Whether or not everything tested so far has been included. */
0241: protected boolean everythingIncluded = true;
0242:
0243: private static final Logger logger = Logger
0244: .getLogger(DirectoryScanner.class);
0245:
0246: /**
0247: * Sole constructor.
0248: */
0249: public DirectoryScanner() {
0250: }
0251:
0252: /**
0253: * Tests whether or not a given path matches the start of a given
0254: * pattern up to the first "**".
0255: * <p>
0256: * This is not a general purpose test and should only be used if you
0257: * can live with false positives. For example, <code>pattern=**\a</code>
0258: * and <code>str=b</code> will yield <code>true</code>.
0259: *
0260: * @param pattern The pattern to match against. Must not be
0261: * <code>null</code>.
0262: * @param str The path to match, as a String. Must not be
0263: * <code>null</code>.
0264: *
0265: * @return whether or not a given path matches the start of a given
0266: * pattern up to the first "**".
0267: */
0268: protected static boolean matchPatternStart(String pattern,
0269: String str) {
0270: return SelectorUtils.matchPatternStart(pattern, str);
0271: }
0272:
0273: /**
0274: * Tests whether or not a given path matches the start of a given
0275: * pattern up to the first "**".
0276: * <p>
0277: * This is not a general purpose test and should only be used if you
0278: * can live with false positives. For example, <code>pattern=**\a</code>
0279: * and <code>str=b</code> will yield <code>true</code>.
0280: *
0281: * @param pattern The pattern to match against. Must not be
0282: * <code>null</code>.
0283: * @param str The path to match, as a String. Must not be
0284: * <code>null</code>.
0285: * @param isCaseSensitive Whether or not matching should be performed
0286: * case sensitively.
0287: *
0288: * @return whether or not a given path matches the start of a given
0289: * pattern up to the first "**".
0290: */
0291: protected static boolean matchPatternStart(String pattern,
0292: String str, boolean isCaseSensitive) {
0293: return SelectorUtils.matchPatternStart(pattern, str,
0294: isCaseSensitive);
0295: }
0296:
0297: /**
0298: * Tests whether or not a given path matches a given pattern.
0299: *
0300: * @param pattern The pattern to match against. Must not be
0301: * <code>null</code>.
0302: * @param str The path to match, as a String. Must not be
0303: * <code>null</code>.
0304: *
0305: * @return <code>true</code> if the pattern matches against the string,
0306: * or <code>false</code> otherwise.
0307: */
0308: protected static boolean matchPath(String pattern, String str) {
0309: return SelectorUtils.matchPath(pattern, str);
0310: }
0311:
0312: /**
0313: * Tests whether or not a given path matches a given pattern.
0314: *
0315: * @param pattern The pattern to match against. Must not be
0316: * <code>null</code>.
0317: * @param str The path to match, as a String. Must not be
0318: * <code>null</code>.
0319: * @param isCaseSensitive Whether or not matching should be performed
0320: * case sensitively.
0321: *
0322: * @return <code>true</code> if the pattern matches against the string,
0323: * or <code>false</code> otherwise.
0324: */
0325: protected static boolean matchPath(String pattern, String str,
0326: boolean isCaseSensitive) {
0327: return SelectorUtils.matchPath(pattern, str, isCaseSensitive);
0328: }
0329:
0330: /**
0331: * Tests whether or not a string matches against a pattern.
0332: * The pattern may contain two special characters:<br>
0333: * '*' means zero or more characters<br>
0334: * '?' means one and only one character
0335: *
0336: * @param pattern The pattern to match against.
0337: * Must not be <code>null</code>.
0338: * @param str The string which must be matched against the pattern.
0339: * Must not be <code>null</code>.
0340: *
0341: * @return <code>true</code> if the string matches against the pattern,
0342: * or <code>false</code> otherwise.
0343: */
0344: public static boolean match(String pattern, String str) {
0345: return SelectorUtils.match(pattern, str);
0346: }
0347:
0348: /**
0349: * Tests whether or not a string matches against a pattern.
0350: * The pattern may contain two special characters:<br>
0351: * '*' means zero or more characters<br>
0352: * '?' means one and only one character
0353: *
0354: * @param pattern The pattern to match against.
0355: * Must not be <code>null</code>.
0356: * @param str The string which must be matched against the pattern.
0357: * Must not be <code>null</code>.
0358: * @param isCaseSensitive Whether or not matching should be performed
0359: * case sensitively.
0360: *
0361: *
0362: * @return <code>true</code> if the string matches against the pattern,
0363: * or <code>false</code> otherwise.
0364: */
0365: protected static boolean match(String pattern, String str,
0366: boolean isCaseSensitive) {
0367: return SelectorUtils.match(pattern, str, isCaseSensitive);
0368: }
0369:
0370: /**
0371: * Get the list of patterns that should be excluded by default.
0372: *
0373: * @return An array of <code>String</code> based on the current
0374: * contents of the <code>defaultExcludes</code>
0375: * <code>Vector</code>.
0376: *
0377: * @since Ant 1.6
0378: */
0379: // public static String[] getDefaultExcludes() {
0380: // return (String[]) defaultExcludes.toArray(new String[defaultExcludes
0381: // .size()]);
0382: // }
0383: /**
0384: * Add a pattern to the default excludes unless it is already a
0385: * default exclude.
0386: *
0387: * @param s A string to add as an exclude pattern.
0388: * @return <code>true</code> if the string was added
0389: * <code>false</code> if it already
0390: * existed.
0391: *
0392: * @since Ant 1.6
0393: */
0394: // public static boolean addDefaultExclude(String s) {
0395: // if (defaultExcludes.indexOf(s) == -1) {
0396: // defaultExcludes.add(s);
0397: // return true;
0398: // }
0399: // return false;
0400: // }
0401: /**
0402: * Remove a string if it is a default exclude.
0403: *
0404: * @param s The string to attempt to remove.
0405: * @return <code>true</code> if <code>s</code> was a default
0406: * exclude (and thus was removed),
0407: * <code>false</code> if <code>s</code> was not
0408: * in the default excludes list to begin with
0409: *
0410: * @since Ant 1.6
0411: */
0412: // public static boolean removeDefaultExclude(String s) {
0413: // return defaultExcludes.remove(s);
0414: // }
0415: /**
0416: * Go back to the hard wired default exclude patterns
0417: *
0418: * @since Ant 1.6
0419: */
0420: // public static void resetDefaultExcludes() {
0421: // defaultExcludes = new Vector();
0422: //
0423: // for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
0424: // defaultExcludes.add(DEFAULTEXCLUDES[i]);
0425: // }
0426: // }
0427: /**
0428: * Sets the base directory to be scanned. This is the directory which is
0429: * scanned recursively.
0430: *
0431: * @param basedir The base directory for scanning.
0432: * Should not be <code>null</code>.
0433: */
0434: public void setBasedir(LinkedRemoteFileInterface basedir) {
0435: _basedir = basedir;
0436: }
0437:
0438: /**
0439: * Returns the base directory to be scanned.
0440: * This is the directory which is scanned recursively.
0441: *
0442: * @return the base directory to be scanned
0443: */
0444: public LinkedRemoteFileInterface getBasedir() {
0445: return _basedir;
0446: }
0447:
0448: /**
0449: * Find out whether include exclude patterns are matched in a
0450: * case sensitive way
0451: * @return whether or not the scanning is case sensitive
0452: * @since ant 1.6
0453: */
0454: public boolean isCaseSensitive() {
0455: return _isCaseSensitive;
0456: }
0457:
0458: /**
0459: * Sets whether or not include and exclude patterns are matched
0460: * in a case sensitive way
0461: *
0462: * @param isCaseSensitive whether or not the file system should be
0463: * regarded as a case sensitive one
0464: */
0465: // public void setCaseSensitive(boolean isCaseSensitive) {
0466: // this.isCaseSensitive = isCaseSensitive;
0467: // }
0468: /**
0469: * gets whether or not a DirectoryScanner follows symbolic links
0470: *
0471: * @return flag indicating whether symbolic links should be followed
0472: *
0473: * @since ant 1.6
0474: */
0475: public boolean isFollowSymlinks() {
0476: return _followSymlinks;
0477: }
0478:
0479: /**
0480: * Sets whether or not symbolic links should be followed.
0481: *
0482: * @param followSymlinks whether or not symbolic links should be followed
0483: */
0484: public void setFollowSymlinks(boolean followSymlinks) {
0485: _followSymlinks = followSymlinks;
0486: }
0487:
0488: /**
0489: * Sets the list of include patterns to use. All '/' and '\' characters
0490: * are replaced by <code>File.separatorChar</code>, so the separator used
0491: * need not match <code>File.separatorChar</code>.
0492: * <p>
0493: * When a pattern ends with a '/' or '\', "**" is appended.
0494: *
0495: * @param includes A list of include patterns.
0496: * May be <code>null</code>, indicating that all files
0497: * should be included. If a non-<code>null</code>
0498: * list is given, all elements must be
0499: * non-<code>null</code>.
0500: */
0501: public void setIncludes(String[] includes) {
0502: if (includes == null) {
0503: _includes = null;
0504: } else {
0505: _includes = new String[includes.length];
0506: for (int i = 0; i < includes.length; i++) {
0507: String pattern;
0508: pattern = includes[i];
0509: if (pattern.endsWith("/")) {
0510: pattern += "**";
0511: }
0512: _includes[i] = pattern;
0513: }
0514: }
0515: }
0516:
0517: /**
0518: * Sets the list of exclude patterns to use. All '/' and '\' characters
0519: * are replaced by <code>File.separatorChar</code>, so the separator used
0520: * need not match <code>File.separatorChar</code>.
0521: * <p>
0522: * When a pattern ends with a '/' or '\', "**" is appended.
0523: *
0524: * @param excludes A list of exclude patterns.
0525: * May be <code>null</code>, indicating that no files
0526: * should be excluded. If a non-<code>null</code> list is
0527: * given, all elements must be non-<code>null</code>.
0528: */
0529: public void setExcludes(String[] excludes) {
0530: if (excludes == null) {
0531: _excludes = null;
0532: } else {
0533: _excludes = new String[excludes.length];
0534: for (int i = 0; i < excludes.length; i++) {
0535: String pattern;
0536: pattern = excludes[i];
0537: if (pattern.endsWith("/")) {
0538: pattern += "**";
0539: }
0540: _excludes[i] = pattern;
0541: }
0542: }
0543: }
0544:
0545: /**
0546: * Sets the selectors that will select the filelist.
0547: *
0548: * @param selectors specifies the selectors to be invoked on a scan
0549: */
0550: public void setSelectors(FileSelector[] selectors) {
0551: _selectors = selectors;
0552: }
0553:
0554: /**
0555: * Returns whether or not the scanner has included all the files or
0556: * directories it has come across so far.
0557: *
0558: * @return <code>true</code> if all files and directories which have
0559: * been found so far have been included.
0560: */
0561: public boolean isEverythingIncluded() {
0562: return everythingIncluded;
0563: }
0564:
0565: /**
0566: * Scans the base directory for files which match at least one include
0567: * pattern and don't match any exclude patterns. If there are selectors
0568: * then the files must pass muster there, as well.
0569: *
0570: * @exception IllegalStateException if the base directory was set
0571: * incorrectly (i.e. if it is <code>null</code>, doesn't exist,
0572: * or isn't a directory).
0573: * @throws FileNotFoundException
0574: */
0575: public void scan() throws IllegalStateException,
0576: FileNotFoundException {
0577: if (_basedir == null) {
0578: throw new IllegalStateException("No basedir set");
0579: }
0580: /*
0581: if (!basedir.exists()) {
0582: throw new IllegalStateException("basedir " + basedir
0583: + " does not exist");
0584: }
0585: */
0586: if (!_basedir.isDirectory()) {
0587: throw new IllegalStateException("basedir " + _basedir
0588: + " is not a directory");
0589: }
0590:
0591: if (_includes == null) {
0592: // No includes supplied, so set it to 'matches all'
0593: _includes = new String[1];
0594: _includes[0] = "**";
0595: }
0596: if (_excludes == null) {
0597: _excludes = new String[0];
0598: }
0599:
0600: filesIncluded = new Vector<String>();
0601: filesNotIncluded = new Vector<String>();
0602: filesExcluded = new Vector<String>();
0603: filesDeselected = new Vector<String>();
0604: dirsIncluded = new Vector<String>();
0605: dirsNotIncluded = new Vector<String>();
0606: dirsExcluded = new Vector<String>();
0607: dirsDeselected = new Vector<String>();
0608:
0609: if (isIncluded("")) {
0610: if (!isExcluded("")) {
0611: if (isSelected("", _basedir)) {
0612: dirsIncluded.addElement("");
0613: } else {
0614: dirsDeselected.addElement("");
0615: }
0616: } else {
0617: dirsExcluded.addElement("");
0618: }
0619: } else {
0620: dirsNotIncluded.addElement("");
0621: }
0622: checkIncludePatterns();
0623: clearCaches();
0624: }
0625:
0626: /**
0627: * this routine is actually checking all the include patterns in
0628: * order to avoid scanning everything under base dir
0629: * @throws FileNotFoundException
0630: * @since ant1.6
0631: */
0632: private void checkIncludePatterns() {
0633: Hashtable<String, String> newroots = new Hashtable<String, String>();
0634: // put in the newroots vector the include patterns without
0635: // wildcard tokens
0636: for (int icounter = 0; icounter < _includes.length; icounter++) {
0637: String newpattern = SelectorUtils
0638: .rtrimWildcardTokens(_includes[icounter]);
0639: newroots.put(newpattern, _includes[icounter]);
0640: }
0641:
0642: if (newroots.containsKey("")) {
0643: // we are going to scan everything anyway
0644: scandir(_basedir, "", true);
0645: } else {
0646: // only scan directories that can include matched files or
0647: // directories
0648: //Enumeration enum2 = newroots.keySet().iterator();
0649:
0650: //LinkedRemoteFileInterface canonBase = null;
0651: //try {
0652: // canonBase = basedir.getCanonicalFile();
0653: //} catch (IOException ex) {
0654: // throw new BuildException(ex);
0655: //}
0656: for (Map.Entry entry : newroots.entrySet()) {
0657: //while (enum2.hasMoreElements()) {
0658: // String currentelement = (String) enum2.nextElement();
0659: // String originalpattern = (String) newroots.get(currentelement);
0660: String currentelement = (String) entry.getKey();
0661: String originalpattern = (String) entry.getValue();
0662: LinkedRemoteFileInterface myfile;
0663: try {
0664: myfile = _basedir.lookupFile(currentelement);
0665: } catch (FileNotFoundException e) {
0666: continue; // does not exist, iterate next root
0667: }
0668: currentelement = FileUtils.removeLeadingPath(_basedir,
0669: myfile);
0670:
0671: /*if (myfile.exists())*/{
0672: // may be on a case insensitive file system. We want
0673: // the results to show what's really on the disk, so
0674: // we need to double check.
0675: // try {
0676: // LinkedRemoteFileInterface canonFile = myfile.getCanonicalFile();
0677: // String path = FILE_UTILS.removeLeadingPath(canonBase,
0678: // canonFile);
0679: // if (!path.equals(currentelement)/* || ON_VMS*/) {
0680: // myfile = findFile(basedir, currentelement);
0681: // if (myfile != null) {
0682: // currentelement =
0683: // FILE_UTILS.removeLeadingPath(basedir,
0684: // myfile);
0685: // }
0686: // }
0687: // } catch (IOException ex) {
0688: // throw new BuildException(ex);
0689: // }
0690: }
0691:
0692: if ((myfile == null/* || !myfile.exists()*/)
0693: && !_isCaseSensitive) {
0694: throw new RuntimeException("DEAD CODE");
0695: // LinkedRemoteFileInterface f;
0696: // try {
0697: // f = findFileCaseInsensitive(basedir, currentelement);
0698: // } catch (FileNotFoundException e1) {
0699: // throw new RuntimeException(e1);
0700: // }
0701: // /*if (f.exists())*/ {
0702: // // adapt currentelement to the case we've
0703: // // actually found
0704: // currentelement = FILE_UTILS.removeLeadingPath(basedir,
0705: // f);
0706: // myfile = f;
0707: // }
0708: }
0709:
0710: if (myfile != null/* && myfile.exists()*/) {
0711: if (!_followSymlinks
0712: && /*isSymlink(basedir, currentelement)*/myfile
0713: .isLink()) {
0714: continue;
0715: }
0716:
0717: if (myfile.isDirectory()) {
0718: if (isIncluded(currentelement)
0719: && currentelement.length() > 0) {
0720: accountForIncludedDir(currentelement,
0721: myfile, true);
0722: } else {
0723: if (currentelement.length() > 0) {
0724: if (currentelement
0725: .charAt(currentelement.length() - 1) != '/') {
0726: currentelement = currentelement + '/';
0727: }
0728: }
0729: scandir(myfile, currentelement, true);
0730: }
0731: } else {
0732: if (_isCaseSensitive
0733: && originalpattern
0734: .equals(currentelement)) {
0735: accountForIncludedFile(currentelement,
0736: myfile);
0737: } else if (!_isCaseSensitive
0738: && originalpattern
0739: .equalsIgnoreCase(currentelement)) {
0740: accountForIncludedFile(currentelement,
0741: myfile);
0742: }
0743: }
0744: }
0745: }
0746: }
0747: }
0748:
0749: /**
0750: * Top level invocation for a slow scan. A slow scan builds up a full
0751: * list of excluded/included files/directories, whereas a fast scan
0752: * will only have full results for included files, as it ignores
0753: * directories which can't possibly hold any included files/directories.
0754: * <p>
0755: * Returns immediately if a slow scan has already been completed.
0756: * @throws FileNotFoundException
0757: */
0758: protected void slowScan() throws FileNotFoundException {
0759: if (haveSlowResults) {
0760: return;
0761: }
0762:
0763: String[] excl = new String[dirsExcluded.size()];
0764: dirsExcluded.copyInto(excl);
0765:
0766: String[] notIncl = new String[dirsNotIncluded.size()];
0767: dirsNotIncluded.copyInto(notIncl);
0768:
0769: for (int i = 0; i < excl.length; i++) {
0770: if (!couldHoldIncluded(excl[i])) {
0771: scandir(_basedir.lookupFile(excl[i]), excl[i] + "/",
0772: false);
0773: }
0774: }
0775:
0776: for (int i = 0; i < notIncl.length; i++) {
0777: if (!couldHoldIncluded(notIncl[i])) {
0778: scandir(_basedir.lookupFile(notIncl[i]), notIncl[i]
0779: + "/", false);
0780: }
0781: }
0782:
0783: haveSlowResults = true;
0784: }
0785:
0786: /**
0787: * Scans the given directory for files and directories. Found files and
0788: * directories are placed in their respective collections, based on the
0789: * matching of includes, excludes, and the selectors. When a directory
0790: * is found, it is scanned recursively.
0791: *
0792: * @param dir The directory to scan. Must not be <code>null</code>.
0793: * @param vpath The path relative to the base directory (needed to
0794: * prevent problems with an absolute path when using
0795: * dir). Must not be <code>null</code>.
0796: * @param fast Whether or not this call is part of a fast scan.
0797: * @throws FileNotFoundException
0798: *
0799: * @see #filesIncluded
0800: * @see #filesNotIncluded
0801: * @see #filesExcluded
0802: * @see #dirsIncluded
0803: * @see #dirsNotIncluded
0804: * @see #dirsExcluded
0805: * @see #slowScan
0806: */
0807: protected void scandir(LinkedRemoteFileInterface dir, String vpath,
0808: boolean fast) {
0809: if (dir == null) {
0810: throw new RuntimeException("dir must not be null.");
0811: /*
0812: } else if (!dir.exists()) {
0813: throw new BuildException(dir + " doesn't exists.");
0814: */
0815: } else if (!dir.isDirectory()) {
0816: throw new RuntimeException(dir + " is not a directory.");
0817: }
0818:
0819: // avoid double scanning of directories, can only happen in fast mode
0820: if (fast && hasBeenScanned(vpath)) {
0821: return;
0822: }
0823: LinkedRemoteFileInterface[] newfiles = dir.getFiles().toArray(
0824: new LinkedRemoteFileInterface[0]);
0825:
0826: if (!_followSymlinks) {
0827: Vector<LinkedRemoteFileInterface> noLinks = new Vector<LinkedRemoteFileInterface>();
0828: for (int i = 0; i < newfiles.length; i++) {
0829: //try {
0830: if (newfiles[i].isLink()) {
0831: String name = vpath + newfiles[i];
0832: LinkedRemoteFileInterface file = newfiles[i];
0833: if (file.isDirectory()) {
0834: dirsExcluded.addElement(name);
0835: } else {
0836: filesExcluded.addElement(name);
0837: }
0838: } else {
0839: noLinks.addElement(newfiles[i]);
0840: }
0841: /*} catch (IOException ioe) {
0842: String msg = "IOException caught while checking "
0843: + "for links, couldn't get canonical path!";
0844: // will be caught and redirected to Ant's logging system
0845: System.err.println(msg);
0846: noLinks.addElement(newfiles[i]);
0847: }*/
0848: }
0849: newfiles = new LinkedRemoteFileInterface[noLinks.size()];
0850: noLinks.copyInto(newfiles);
0851: }
0852:
0853: for (int i = 0; i < newfiles.length; i++) {
0854: String name = vpath + newfiles[i].getName();
0855: LinkedRemoteFileInterface file = newfiles[i];
0856: if (file.isDirectory()) {
0857: if (isIncluded(name)) {
0858: accountForIncludedDir(name, file, fast);
0859: } else {
0860: everythingIncluded = false;
0861: dirsNotIncluded.addElement(name);
0862: if (fast && couldHoldIncluded(name)) {
0863: scandir(file, name + "/", fast);
0864: }
0865: }
0866: if (!fast) {
0867: scandir(file, name + "/", fast);
0868: }
0869: } else if (file.isFile()) {
0870: if (isIncluded(name)) {
0871: accountForIncludedFile(name, file);
0872: } else {
0873: everythingIncluded = false;
0874: filesNotIncluded.addElement(name);
0875: }
0876: }
0877: }
0878: }
0879:
0880: /**
0881: * process included file
0882: * @param name path of the file relative to the directory of the fileset
0883: * @param file included file
0884: */
0885: private void accountForIncludedFile(String name,
0886: LinkedRemoteFileInterface file) {
0887: if (!filesIncluded.contains(name)
0888: && !filesExcluded.contains(name)
0889: && !filesDeselected.contains(name)) {
0890:
0891: if (!isExcluded(name)) {
0892: if (isSelected(name, file)) {
0893: filesIncluded.addElement(name);
0894: } else {
0895: everythingIncluded = false;
0896: filesDeselected.addElement(name);
0897: }
0898: } else {
0899: everythingIncluded = false;
0900: filesExcluded.addElement(name);
0901: }
0902: }
0903: }
0904:
0905: /**
0906: *
0907: * @param name path of the directory relative to the directory of
0908: * the fileset
0909: * @param file directory as file
0910: * @param fast
0911: * @throws FileNotFoundException
0912: */
0913: private void accountForIncludedDir(String name,
0914: LinkedRemoteFileInterface file, boolean fast) {
0915: if (!dirsIncluded.contains(name)
0916: && !dirsExcluded.contains(name)
0917: && !dirsDeselected.contains(name)) {
0918:
0919: if (!isExcluded(name)) {
0920: if (isSelected(name, file)) {
0921: dirsIncluded.addElement(name);
0922: if (fast) {
0923: scandir(file, name + "/", fast);
0924: }
0925: } else {
0926: everythingIncluded = false;
0927: dirsDeselected.addElement(name);
0928: if (fast && couldHoldIncluded(name)) {
0929: scandir(file, name + "/", fast);
0930: }
0931: }
0932:
0933: } else {
0934: everythingIncluded = false;
0935: dirsExcluded.addElement(name);
0936: if (fast && couldHoldIncluded(name)) {
0937: scandir(file, name + "/", fast);
0938: }
0939: }
0940: }
0941: }
0942:
0943: /**
0944: * Tests whether or not a name matches against at least one include
0945: * pattern.
0946: *
0947: * @param name The name to match. Must not be <code>null</code>.
0948: * @return <code>true</code> when the name matches against at least one
0949: * include pattern, or <code>false</code> otherwise.
0950: */
0951: protected boolean isIncluded(String name) {
0952: for (int i = 0; i < _includes.length; i++) {
0953: if (matchPath(_includes[i], name, _isCaseSensitive)) {
0954: return true;
0955: }
0956: }
0957: return false;
0958: }
0959:
0960: /**
0961: * Tests whether or not a name matches the start of at least one include
0962: * pattern.
0963: *
0964: * @param name The name to match. Must not be <code>null</code>.
0965: * @return <code>true</code> when the name matches against the start of at
0966: * least one include pattern, or <code>false</code> otherwise.
0967: */
0968: protected boolean couldHoldIncluded(String name) {
0969: for (int i = 0; i < _includes.length; i++) {
0970: if (matchPatternStart(_includes[i], name, _isCaseSensitive)) {
0971: if (isMorePowerfulThanExcludes(name, _includes[i])) {
0972: return true;
0973: }
0974: }
0975: }
0976: return false;
0977: }
0978:
0979: /**
0980: * find out whether one particular include pattern is more powerful
0981: * than all the excludes
0982: * note : the power comparison is based on the length of the include pattern
0983: * and of the exclude patterns without the wildcards
0984: * ideally the comparison should be done based on the depth
0985: * of the match, that is to say how many file separators have been matched
0986: * before the first ** or the end of the pattern
0987: *
0988: * IMPORTANT : this function should return false "with care"
0989: *
0990: * @param name the relative path that one want to test
0991: * @param includepattern one include pattern
0992: * @return true if there is no exclude pattern more powerful than this include pattern
0993: * @since ant1.6
0994: */
0995: private boolean isMorePowerfulThanExcludes(String name,
0996: String includepattern) {
0997: String soughtexclude = name + "/" + "**";
0998: for (int counter = 0; counter < _excludes.length; counter++) {
0999: if (_excludes[counter].equals(soughtexclude)) {
1000: return false;
1001: }
1002: }
1003: return true;
1004: }
1005:
1006: /**
1007: * Tests whether or not a name matches against at least one exclude
1008: * pattern.
1009: *
1010: * @param name The name to match. Must not be <code>null</code>.
1011: * @return <code>true</code> when the name matches against at least one
1012: * exclude pattern, or <code>false</code> otherwise.
1013: */
1014: protected boolean isExcluded(String name) {
1015: for (int i = 0; i < _excludes.length; i++) {
1016: if (matchPath(_excludes[i], name, _isCaseSensitive)) {
1017: return true;
1018: }
1019: }
1020: return false;
1021: }
1022:
1023: /**
1024: * Tests whether a name should be selected.
1025: *
1026: * @param name the filename to check for selecting
1027: * @param file the java.io.File object for this filename
1028: * @return <code>false</code> when the selectors says that the file
1029: * should not be selected, <code>true</code> otherwise.
1030: */
1031: protected boolean isSelected(String name,
1032: LinkedRemoteFileInterface file) {
1033: if (_selectors != null) {
1034: for (int i = 0; i < _selectors.length; i++) {
1035: if (!_selectors[i].isSelected(_basedir, name, file)) {
1036: return false;
1037: }
1038: }
1039: }
1040: return true;
1041: }
1042:
1043: /**
1044: * Returns the names of the files which matched at least one of the
1045: * include patterns and none of the exclude patterns.
1046: * The names are relative to the base directory.
1047: *
1048: * @return the names of the files which matched at least one of the
1049: * include patterns and none of the exclude patterns.
1050: */
1051: public String[] getIncludedFiles() {
1052: String[] files = new String[filesIncluded.size()];
1053: filesIncluded.copyInto(files);
1054: Arrays.sort(files);
1055: return files;
1056: }
1057:
1058: /**
1059: * Returns the names of the files which matched none of the include
1060: * patterns. The names are relative to the base directory. This involves
1061: * performing a slow scan if one has not already been completed.
1062: *
1063: * @return the names of the files which matched none of the include
1064: * patterns.
1065: * @throws FileNotFoundException
1066: *
1067: * @see #slowScan
1068: */
1069: public String[] getNotIncludedFiles() throws FileNotFoundException {
1070: slowScan();
1071: String[] files = new String[filesNotIncluded.size()];
1072: filesNotIncluded.copyInto(files);
1073: return files;
1074: }
1075:
1076: /**
1077: * Returns the names of the files which matched at least one of the
1078: * include patterns and at least one of the exclude patterns.
1079: * The names are relative to the base directory. This involves
1080: * performing a slow scan if one has not already been completed.
1081: *
1082: * @return the names of the files which matched at least one of the
1083: * include patterns and at least one of the exclude patterns.
1084: * @throws FileNotFoundException
1085: *
1086: * @see #slowScan
1087: */
1088: public String[] getExcludedFiles() throws FileNotFoundException {
1089: slowScan();
1090: String[] files = new String[filesExcluded.size()];
1091: filesExcluded.copyInto(files);
1092: return files;
1093: }
1094:
1095: /**
1096: * <p>Returns the names of the files which were selected out and
1097: * therefore not ultimately included.</p>
1098: *
1099: * <p>The names are relative to the base directory. This involves
1100: * performing a slow scan if one has not already been completed.</p>
1101: *
1102: * @return the names of the files which were deselected.
1103: * @throws FileNotFoundException
1104: *
1105: * @see #slowScan
1106: */
1107: public String[] getDeselectedFiles() throws FileNotFoundException {
1108: slowScan();
1109: String[] files = new String[filesDeselected.size()];
1110: filesDeselected.copyInto(files);
1111: return files;
1112: }
1113:
1114: /**
1115: * Returns the names of the directories which matched at least one of the
1116: * include patterns and none of the exclude patterns.
1117: * The names are relative to the base directory.
1118: *
1119: * @return the names of the directories which matched at least one of the
1120: * include patterns and none of the exclude patterns.
1121: */
1122: public String[] getIncludedDirectories() {
1123: String[] directories = new String[dirsIncluded.size()];
1124: dirsIncluded.copyInto(directories);
1125: Arrays.sort(directories);
1126: return directories;
1127: }
1128:
1129: /**
1130: * Returns the names of the directories which matched none of the include
1131: * patterns. The names are relative to the base directory. This involves
1132: * performing a slow scan if one has not already been completed.
1133: *
1134: * @return the names of the directories which matched none of the include
1135: * patterns.
1136: * @throws FileNotFoundException
1137: *
1138: * @see #slowScan
1139: */
1140: public String[] getNotIncludedDirectories()
1141: throws FileNotFoundException {
1142: slowScan();
1143: String[] directories = new String[dirsNotIncluded.size()];
1144: dirsNotIncluded.copyInto(directories);
1145: return directories;
1146: }
1147:
1148: /**
1149: * Returns the names of the directories which matched at least one of the
1150: * include patterns and at least one of the exclude patterns.
1151: * The names are relative to the base directory. This involves
1152: * performing a slow scan if one has not already been completed.
1153: *
1154: * @return the names of the directories which matched at least one of the
1155: * include patterns and at least one of the exclude patterns.
1156: * @throws FileNotFoundException
1157: *
1158: * @see #slowScan
1159: */
1160: public String[] getExcludedDirectories()
1161: throws FileNotFoundException {
1162: slowScan();
1163: String[] directories = new String[dirsExcluded.size()];
1164: dirsExcluded.copyInto(directories);
1165: return directories;
1166: }
1167:
1168: /**
1169: * <p>Returns the names of the directories which were selected out and
1170: * therefore not ultimately included.</p>
1171: *
1172: * <p>The names are relative to the base directory. This involves
1173: * performing a slow scan if one has not already been completed.</p>
1174: *
1175: * @return the names of the directories which were deselected.
1176: * @throws FileNotFoundException
1177: *
1178: * @see #slowScan
1179: */
1180: public String[] getDeselectedDirectories()
1181: throws FileNotFoundException {
1182: slowScan();
1183: String[] directories = new String[dirsDeselected.size()];
1184: dirsDeselected.copyInto(directories);
1185: return directories;
1186: }
1187:
1188: /**
1189: * Adds default exclusions to the current exclusions set.
1190: */
1191: // public void addDefaultExcludes() {
1192: // int excludesLength = excludes == null ? 0 : excludes.length;
1193: // String[] newExcludes;
1194: // newExcludes = new String[excludesLength + defaultExcludes.size()];
1195: // if (excludesLength > 0) {
1196: // System.arraycopy(excludes, 0, newExcludes, 0, excludesLength);
1197: // }
1198: // String[] defaultExcludesTemp = getDefaultExcludes();
1199: // for (int i = 0; i < defaultExcludesTemp.length; i++) {
1200: // newExcludes[i + excludesLength] =
1201: // defaultExcludesTemp[i]
1202: // ;
1203: // }
1204: // excludes = newExcludes;
1205: // }
1206: /**
1207: * Get the named resource
1208: * @param name path name of the file relative to the dir attribute.
1209: *
1210: * @return the resource with the given name.
1211: * @since Ant 1.5.2
1212: */
1213: /*
1214: public Resource getResource(String name) {
1215: LinkedRemoteFileInterface f = FILE_UTILS.resolveFile(basedir, name);
1216: return new Resource(name, f.exists(), f.lastModified(),
1217: f.isDirectory());
1218: }
1219: */
1220:
1221: /**
1222: * temporary table to speed up the various scanning methods below
1223: *
1224: * @since Ant 1.6
1225: */
1226: private Map fileListMap = new HashMap();
1227:
1228: /**
1229: * List of all scanned directories.
1230: *
1231: * @since Ant 1.6
1232: */
1233: private Set<String> scannedDirs = new HashSet<String>();
1234:
1235: /**
1236: * Has the directory with the given path relative to the base
1237: * directory already been scanned?
1238: *
1239: * <p>Registers the given directory as scanned as a side effect.</p>
1240: *
1241: * @since Ant 1.6
1242: */
1243: private boolean hasBeenScanned(String vpath) {
1244: return !scannedDirs.add(vpath);
1245: }
1246:
1247: /**
1248: * Clear internal caches.
1249: *
1250: * @since Ant 1.6
1251: */
1252: private void clearCaches() {
1253: fileListMap.clear();
1254: scannedDirs.clear();
1255: }
1256: }
|