tbennett 2004/04/17 12:16:01 Added: merlin/composition/impl/src/java/org/apache/avalon/composition/model/impl DirectoryScanner.java Resource.java ScannerUtils.java Log: Implementation classes for full featured fileset resolution Revision Changes Path 1.1 avalon/merlin/composition/impl/src/java/org/apache/avalon/composition/model/impl/DirectoryScanner.java Index: DirectoryScanner.java =================================================================== /* * Copyright 2000-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.avalon.composition.model.impl; import java.io.File; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.Vector; import org.apache.avalon.composition.model.FileSelector; /** * TODO Write class description. * * @author Apache Ant Development Team (Kuiper, Umasankar, Atherton, and Levy-Lamber) * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a> * @version $Revision: 1.1 $ $Date: 2004/04/17 19:16:01 $ */ public class DirectoryScanner { /** * Patterns which should be excluded by default. * * <p>Note that you can now add patterns to the list of default * excludes. Added patterns will not become part of this array * that has only been kept around for backwards compatibility * reasons.</p> * * @deprecated use the [EMAIL PROTECTED] #getDefaultExcludes * getDefaultExcludes} method instead. */ protected static final String[] DEFAULTEXCLUDES = { // Miscellaneous typical temporary files "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*", // CVS "**/CVS", "**/CVS/**", "**/.cvsignore", // SCCS "**/SCCS", "**/SCCS/**", // Visual SourceSafe "**/vssver.scc", // Subversion "**/.svn", "**/.svn/**", // Mac "**/.DS_Store" }; /** * Patterns which should be excluded by default. * * @see #addDefaultExcludes() */ private static Vector defaultExcludes = new Vector(); static { resetDefaultExcludes(); } /** The base directory to be scanned. */ private File basedir; /** The patterns for the files to be included. */ protected String[] includes; /** The patterns for the files to be excluded. */ protected String[] excludes; /** Selectors that will filter which files are in our candidate list. */ protected FileSelector[] selectors = null; /** The files which matched at least one include and no excludes * and were selected. */ protected Vector filesIncluded; /** The files which did not match any includes or selectors. */ protected Vector filesNotIncluded; /** * The files which matched at least one include and at least * one exclude. */ protected Vector filesExcluded; /** The directories which matched at least one include and no excludes * and were selected. */ protected Vector dirsIncluded; /** The directories which were found and did not match any includes. */ protected Vector dirsNotIncluded; /** * The directories which matched at least one include and at least one * exclude. */ protected Vector dirsExcluded; /** The files which matched at least one include and no excludes and * which a selector discarded. */ protected Vector filesDeselected; /** The directories which matched at least one include and no excludes * but which a selector discarded. */ protected Vector dirsDeselected; /** * Whether or not the file system should be treated as a case sensitive * one. */ protected boolean isCaseSensitive = true; /** * Sole constructor. */ public DirectoryScanner() { } /** * Tests whether or not a given path matches a given pattern. * * @param pattern The pattern to match against. Must not be * <code>null</code>. * @param str The path to match, as a String. Must not be * <code>null</code>. * @param isCaseSensitive Whether or not matching should be performed * case sensitively. * * @return <code>true</code> if the pattern matches against the string, * or <code>false</code> otherwise. */ protected static boolean matchPath(String pattern, String str, boolean isCaseSensitive) { return ScannerUtils.matchPath(pattern, str, isCaseSensitive); } /** * Tests whether or not a string matches against a pattern. * The pattern may contain two special characters:<br> * '*' means zero or more characters<br> * '?' means one and only one character * * @param pattern The pattern to match against. * Must not be <code>null</code>. * @param str The string which must be matched against the pattern. * Must not be <code>null</code>. * * @return <code>true</code> if the string matches against the pattern, * or <code>false</code> otherwise. */ public static boolean match(String pattern, String str) { return ScannerUtils.match(pattern, str); } /** * Go back to the hard wired default exclude patterns */ public static void resetDefaultExcludes() { defaultExcludes = new Vector(); for (int i = 0; i < DEFAULTEXCLUDES.length; i++) { defaultExcludes.add(DEFAULTEXCLUDES[i]); } } /** * Sets the base directory to be scanned. This is the directory which is * scanned recursively. All '/' and '\' characters are replaced by * <code>File.separatorChar</code>, so the separator used need not match * <code>File.separatorChar</code>. * * @param basedir The base directory to scan. * Must not be <code>null</code>. */ public void setBasedir(String basedir) { setBasedir(new File(basedir.replace('/', File.separatorChar).replace( '\\', File.separatorChar))); } /** * Sets the base directory to be scanned. This is the directory which is * scanned recursively. * * @param basedir The base directory for scanning. * Should not be <code>null</code>. */ public void setBasedir(File basedir) { this.basedir = basedir; } /** * Returns the base directory to be scanned. * This is the directory which is scanned recursively. * * @return the base directory to be scanned */ public File getBasedir() { return basedir; } /** * Find out whether include exclude patterns are matched in a * case sensitive way * @return whether or not the scanning is case sensitive * @since ant 1.6 */ public boolean isCaseSensitive() { return isCaseSensitive; } /** * Sets whether or not include and exclude patterns are matched * in a case sensitive way * * @param isCaseSensitive whether or not the file system should be * regarded as a case sensitive one */ public void setCaseSensitive(boolean isCaseSensitive) { this.isCaseSensitive = isCaseSensitive; } /** * Sets the list of include patterns to use. All '/' and '\' characters * are replaced by <code>File.separatorChar</code>, so the separator used * need not match <code>File.separatorChar</code>. * <p> * When a pattern ends with a '/' or '\', "**" is appended. * * @param includes A list of include patterns. * May be <code>null</code>, indicating that all files * should be included. If a non-<code>null</code> * list is given, all elements must be * non-<code>null</code>. */ public void setIncludes(String[] includes) { if (includes == null) { this.includes = null; } else { this.includes = new String[includes.length]; for (int i = 0; i < includes.length; i++) { String pattern; pattern = includes[i].replace('/', File.separatorChar).replace( '\\', File.separatorChar); if (pattern.endsWith(File.separator)) { pattern += "**"; } this.includes[i] = pattern; } } } /** * Sets the list of exclude patterns to use. All '/' and '\' characters * are replaced by <code>File.separatorChar</code>, so the separator used * need not match <code>File.separatorChar</code>. * <p> * When a pattern ends with a '/' or '\', "**" is appended. * * @param excludes A list of exclude patterns. * May be <code>null</code>, indicating that no files * should be excluded. If a non-<code>null</code> list is * given, all elements must be non-<code>null</code>. */ public void setExcludes(String[] excludes) { if (excludes == null) { this.excludes = null; } else { this.excludes = new String[excludes.length]; for (int i = 0; i < excludes.length; i++) { String pattern; pattern = excludes[i].replace('/', File.separatorChar).replace( '\\', File.separatorChar); if (pattern.endsWith(File.separator)) { pattern += "**"; } this.excludes[i] = pattern; } } } /** * Scans the base directory for files which match at least one include * pattern and don't match any exclude patterns. If there are selectors * then the files must pass muster there, as well. * * @exception IllegalStateException if the base directory was set * incorrectly (i.e. if it is <code>null</code>, doesn't exist, * or isn't a directory). */ public void scan() throws IllegalStateException { if (basedir == null) { throw new IllegalStateException("No basedir set"); } if (!basedir.exists()) { System.out.println("basedir=[" + basedir + "]"); 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 String[1]; includes[0] = "**//*.jar"; } if (excludes == null) { excludes = new String[0]; } if (selectors == null) { selectors = new FileSelector[1]; selectors[0] = new JarFileSelector(); } filesIncluded = new Vector(); filesNotIncluded = new Vector(); filesExcluded = new Vector(); filesDeselected = new Vector(); dirsIncluded = new Vector(); dirsNotIncluded = new Vector(); dirsExcluded = new Vector(); dirsDeselected = new Vector(); if (isIncluded("")) { if (!isExcluded("")) { if (isSelected("", basedir)) { dirsIncluded.addElement(""); } else { dirsDeselected.addElement(""); } } else { dirsExcluded.addElement(""); } } else { dirsNotIncluded.addElement(""); } checkIncludePatterns(); clearCaches(); */ } /** * this routine is actually checking all the include patterns in * order to avoid scanning everything under base dir */ private void checkIncludePatterns() { } /** * Tests whether or not a name matches against at least one include * pattern. * * @param name The name to match. Must not be <code>null</code>. * @return <code>true</code> when the name matches against at least one * include pattern, or <code>false</code> otherwise. */ protected boolean isIncluded(String name) { for (int i = 0; i < includes.length; i++) { if (matchPath(includes[i], name, isCaseSensitive)) { return true; } } return false; } /** * Tests whether or not a name matches against at least one exclude * pattern. * * @param name The name to match. Must not be <code>null</code>. * @return <code>true</code> when the name matches against at least one * exclude pattern, or <code>false</code> otherwise. */ protected boolean isExcluded(String name) { for (int i = 0; i < excludes.length; i++) { if (matchPath(excludes[i], name, isCaseSensitive)) { return true; } } return false; } /** * Tests whether a name should be selected. * * @param name the filename to check for selecting * @param file the java.io.File object for this filename * @return <code>false</code> when the selectors says that the file * should not be selected, <code>true</code> otherwise. */ protected boolean isSelected(String name, File file) { if (selectors != null) { for (int i = 0; i < selectors.length; i++) { if (!selectors[i].isSelected(basedir, name, file)) { return false; } } } return true; } /** * temporary table to speed up the various scanning methods below * * @since Ant 1.6 */ private Map fileListMap = new HashMap(); /** * Returns a cached result of list performed on file, if * available. Invokes the method and caches the result otherwise. * * @since Ant 1.6 */ private String[] list(File file) { String[] files = (String[]) fileListMap.get(file); if (files == null) { files = file.list(); if (files != null) { fileListMap.put(file, files); } } return files; } /** * List of all scanned directories. */ private Set scannedDirs = new HashSet(); /** * Has the directory with the given path relative to the base * directory already been scanned? * * <p>Registers the given directory as scanned as a side effect.</p> */ private boolean hasBeenScanned(String vpath) { return !scannedDirs.add(vpath); } /** * Clear internal caches. */ private void clearCaches() { fileListMap.clear(); scannedDirs.clear(); } public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("{basedir=[" + basedir.getAbsolutePath() + "];"); buffer.append("isCaseSensitive=[" + isCaseSensitive + "];"); buffer.append("includes=("); for (int i = 0; i < includes.length; i++) { if (i != 0) buffer.append(","); buffer.append(includes[i]); } buffer.append(");"); buffer.append("excludes=("); for (int i = 0; i < excludes.length; i++) { if (i != 0) buffer.append(","); buffer.append(excludes[i]); } buffer.append(")}"); return buffer.toString(); } public class JarFileSelector implements FileSelector { /** * Method that each selector will implement to create their * selection behaviour. * * @param basedir A java.io.File object for the base directory * @param filename The name of the file to check * @param file A File object for this filename * @return whether the file should be selected or not */ public boolean isSelected(File basedir, String filename, File file) { if (getExtension(file).equals("jar")) { return true; } return false; } /** * Extract extension from filename. * * @param f File object * @return the file extension stripped from the filename */ private String getExtension(final File f) { String extension = ""; final String name = f.getName(); int i = name.lastIndexOf("."); if (i > 0) { extension = name.substring(i + 1); } return extension; } } } 1.1 avalon/merlin/composition/impl/src/java/org/apache/avalon/composition/model/impl/Resource.java Index: Resource.java =================================================================== /* * Copyright 2000-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.avalon.composition.model.impl; /** * describes a File or a ZipEntry * * this class is meant to be used by classes needing to record path * and date/time information about a file, a zip entry or some similar * resource (URL, archive in a version control repository, ...) * * @author Apache Ant Development Team (Levy-Lamber) * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a> * @version $Revision: 1.1 $ $Date: 2004/04/17 19:16:01 $ */ public class Resource { private String name = null; private boolean exists = true; private long lastmodified = 0; private boolean directory = false; /** * default constructor */ public Resource() { } /** * only sets the name. * * <p>This is a dummy, used for not existing resources.</p> * * @param name relative path of the resource. Expects * "/" to be used as the directory separator. */ public Resource(String name) { this(name, false, 0, false); } /** * sets the name, lastmodified flag, and exists flag * * @param name relative path of the resource. Expects * "/" to be used as the directory separator. */ public Resource(String name, boolean exists, long lastmodified) { this(name, exists, lastmodified, false); } /** * @param name relative path of the resource. Expects * "/" to be used as the directory separator. */ public Resource(String name, boolean exists, long lastmodified, boolean directory) { this.name = name; this.exists = exists; this.lastmodified = lastmodified; this.directory = directory; } /** * name attribute will contain the path of a file relative to the * root directory of its fileset or the recorded path of a zip * entry. * * <p>example for a file with fullpath /var/opt/adm/resource.txt * in a file set with root dir /var/opt it will be * adm/resource.txt.</p> * * <p>"/" will be used as the directory separator.</p> */ public String getName() { return name; } /** * @param name relative path of the resource. Expects * "/" to be used as the directory separator. */ public void setName(String name) { this.name = name; } /** * the exists attribute tells whether a file exists */ public boolean isExists() { return exists; } public void setExists(boolean exists) { this.exists = exists; } /** * tells the modification time in milliseconds since 01.01.1970 of * * @return 0 if the resource does not exist to mirror the behavior * of [EMAIL PROTECTED] java.io.File File}. */ public long getLastModified() { return !exists || lastmodified < 0 ? 0 : lastmodified; } public void setLastModified(long lastmodified) { this.lastmodified = lastmodified; } /** * tells if the resource is a directory * @return boolean flag indicating if the resource is a directory */ public boolean isDirectory() { return directory; } public void setDirectory(boolean directory) { this.directory = directory; } /** * @return copy of this */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new Error("CloneNotSupportedException for a " + "Clonable Resource caught?"); } } /** * delegates to a comparison of names. */ public int compareTo(Object other) { if (!(other instanceof Resource)) { throw new IllegalArgumentException("Can only be compared with " + "Resources"); } Resource r = (Resource) other; return getName().compareTo(r.getName()); } } 1.1 avalon/merlin/composition/impl/src/java/org/apache/avalon/composition/model/impl/ScannerUtils.java Index: ScannerUtils.java =================================================================== /* * Copyright 2000-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.avalon.composition.model.impl; import java.io.File; import java.util.StringTokenizer; import java.util.Vector; /** * <p>This is a utility class used by DirectoryScanner.</p> * <p>This is a Singleton.</p> * * @author Apache Ant Development Team (Kuiper, Umasankar, Atherton) * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a> * @version $Revision: 1.1 $ $Date: 2004/04/17 19:16:01 $ */ public class ScannerUtils { private static ScannerUtils instance = new ScannerUtils(); /** * Private Constructor */ private ScannerUtils() { } /** * Retrieves the instance of the Singleton. * @return singleton instance */ public static ScannerUtils getInstance() { return instance; } /** * Tests whether or not a given path matches the start of a given * pattern up to the first "**". * <p> * This is not a general purpose test and should only be used if you * can live with false positives. For example, <code>pattern=**\a</code> * and <code>str=b</code> will yield <code>true</code>. * * @param pattern The pattern to match against. Must not be * <code>null</code>. * @param str The path to match, as a String. Must not be * <code>null</code>. * * @return whether or not a given path matches the start of a given * pattern up to the first "**". */ public static boolean matchPatternStart(String pattern, String str) { return matchPatternStart(pattern, str, true); } /** * Tests whether or not a given path matches the start of a given * pattern up to the first "**". * <p> * This is not a general purpose test and should only be used if you * can live with false positives. For example, <code>pattern=**\a</code> * and <code>str=b</code> will yield <code>true</code>. * * @param pattern The pattern to match against. Must not be * <code>null</code>. * @param str The path to match, as a String. Must not be * <code>null</code>. * @param isCaseSensitive Whether or not matching should be performed * case sensitively. * * @return whether or not a given path matches the start of a given * pattern up to the first "**". */ public 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; } String[] patDirs = tokenizePathAsArray(pattern); String[] strDirs = tokenizePathAsArray(str); int patIdxStart = 0; int patIdxEnd = patDirs.length - 1; int strIdxStart = 0; int strIdxEnd = strDirs.length - 1; // up to first '**' while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { String patDir = patDirs[patIdxStart]; if (patDir.equals("**")) { break; } if (!match(patDir, strDirs[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; } } /** * Tests whether or not a given path matches a given pattern. * * @param pattern The pattern to match against. Must not be * <code>null</code>. * @param str The path to match, as a String. Must not be * <code>null</code>. * * @return <code>true</code> if the pattern matches against the string, * or <code>false</code> otherwise. */ public static boolean matchPath(String pattern, String str) { return matchPath(pattern, str, true); } /** * Tests whether or not a given path matches a given pattern. * * @param pattern The pattern to match against. Must not be * <code>null</code>. * @param str The path to match, as a String. Must not be * <code>null</code>. * @param isCaseSensitive Whether or not matching should be performed * case sensitively. * * @return <code>true</code> if the pattern matches against the string, * or <code>false</code> otherwise. */ public static boolean matchPath(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; } String[] patDirs = tokenizePathAsArray(pattern); String[] strDirs = tokenizePathAsArray(str); int patIdxStart = 0; int patIdxEnd = patDirs.length - 1; int strIdxStart = 0; int strIdxEnd = strDirs.length - 1; // up to first '**' while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { String patDir = patDirs[patIdxStart]; if (patDir.equals("**")) { break; } if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) { patDirs = null; strDirs = null; return false; } patIdxStart++; strIdxStart++; } if (strIdxStart > strIdxEnd) { // String is exhausted for (int i = patIdxStart; i <= patIdxEnd; i++) { if (!patDirs[i].equals("**")) { patDirs = null; strDirs = null; return false; } } return true; } else { if (patIdxStart > patIdxEnd) { // String not exhausted, but pattern is. Failure. patDirs = null; strDirs = null; return false; } } // up to last '**' while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { String patDir = patDirs[patIdxEnd]; if (patDir.equals("**")) { break; } if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) { patDirs = null; strDirs = null; return false; } patIdxEnd--; strIdxEnd--; } if (strIdxStart > strIdxEnd) { // String is exhausted for (int i = patIdxStart; i <= patIdxEnd; i++) { if (!patDirs[i].equals("**")) { patDirs = null; strDirs = null; return false; } } return true; } while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { int patIdxTmp = -1; for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { if (patDirs[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 = patDirs[patIdxStart + j + 1]; String subStr = strDirs[strIdxStart + i + j]; if (!match(subPat, subStr, isCaseSensitive)) { continue strLoop; } } foundIdx = strIdxStart + i; break; } if (foundIdx == -1) { patDirs = null; strDirs = null; return false; } patIdxStart = patIdxTmp; strIdxStart = foundIdx + patLength; } for (int i = patIdxStart; i <= patIdxEnd; i++) { if (!patDirs[i].equals("**")) { patDirs = null; strDirs = null; return false; } } return true; } /** * Tests whether or not a string matches against a pattern. * The pattern may contain two special characters:<br> * '*' means zero or more characters<br> * '?' means one and only one character * * @param pattern The pattern to match against. * Must not be <code>null</code>. * @param str The string which must be matched against the pattern. * Must not be <code>null</code>. * * @return <code>true</code> if the string matches against the pattern, * or <code>false</code> otherwise. */ public static boolean match(String pattern, String str) { return match(pattern, str, true); } /** * Tests whether or not a string matches against a pattern. * The pattern may contain two special characters:<br> * '*' means zero or more characters<br> * '?' means one and only one character * * @param pattern The pattern to match against. * Must not be <code>null</code>. * @param str The string which must be matched against the pattern. * Must not be <code>null</code>. * @param isCaseSensitive Whether or not matching should be performed * case sensitively. * * * @return <code>true</code> if the string matches against the pattern, * or <code>false</code> otherwise. */ public 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; } /** * Breaks a path up into a Vector of path elements, tokenizing on * <code>File.separator</code>. * * @param path Path to tokenize. Must not be <code>null</code>. * * @return a Vector of path elements from the tokenized path */ public static Vector tokenizePath (String path) { return tokenizePath(path, File.separator); } /** * Breaks a path up into a Vector of path elements, tokenizing on * * @param path Path to tokenize. Must not be <code>null</code>. * @param separator the separator against which to tokenize. * * @return a Vector of path elements from the tokenized path * @since Ant 1.6 */ public static Vector tokenizePath (String path, String separator) { Vector ret = new Vector(); StringTokenizer st = new StringTokenizer(path, separator); while (st.hasMoreTokens()) { ret.addElement(st.nextToken()); } return ret; } /** * Same as [EMAIL PROTECTED] #tokenizePath tokenizePath} but hopefully faster. */ private static String[] tokenizePathAsArray(String path) { char sep = File.separatorChar; int start = 0; int len = path.length(); int count = 0; for (int pos = 0; pos < len; pos++) { if (path.charAt(pos) == sep) { if (pos != start) { count++; } start = pos + 1; } } if (len != start) { count++; } String[] l = new String[count]; count = 0; start = 0; for (int pos = 0; pos < len; pos++) { if (path.charAt(pos) == sep) { if (pos != start) { String tok = path.substring(start, pos); l[count++] = tok; } start = pos + 1; } } if (len != start) { String tok = path.substring(start); l[count/*++*/] = tok; } return l; } /** * Returns dependency information on these two files. If src has been * modified later than target, it returns true. If target doesn't exist, * it likewise returns true. Otherwise, target is newer than src and * is not out of date, thus the method returns false. It also returns * false if the src file doesn't even exist, since how could the * target then be out of date. * * @param src the original file * @param target the file being compared against * @param granularity the amount in seconds of slack we will give in * determining out of dateness * @return whether the target is out of date */ public static boolean isOutOfDate(File src, File target, int granularity) { if (!src.exists()) { return false; } if (!target.exists()) { return true; } if ((src.lastModified() - granularity) > target.lastModified()) { return true; } return false; } /** * Returns dependency information on these two resources. If src has been * modified later than target, it returns true. If target doesn't exist, * it likewise returns true. Otherwise, target is newer than src and * is not out of date, thus the method returns false. It also returns * false if the src file doesn't even exist, since how could the * target then be out of date. * * @param src the original resource * @param target the resource being compared against * @param granularity the amount in seconds of slack we will give in * determining out of dateness * @return whether the target is out of date */ public static boolean isOutOfDate(Resource src, Resource target, int granularity) { if (!src.isExists()) { return false; } if (!target.isExists()) { return true; } if ((src.getLastModified() - granularity) > target.getLastModified()) { return true; } return false; } /** * "Flattens" a string by removing all whitespace (space, tab, linefeed, * carriage return, and formfeed). This uses StringTokenizer and the * default set of tokens as documented in the single arguement constructor. * * @param input a String to remove all whitespace. * @return a String that has had all whitespace removed. */ public static String removeWhitespace(String input) { StringBuffer result = new StringBuffer(); if (input != null) { StringTokenizer st = new StringTokenizer(input); while (st.hasMoreTokens()) { result.append(st.nextToken()); } } return result.toString(); } /** * Tests if a string contains stars or question marks * @param input a String which one wants to test for containing wildcard * @return true if the string contains at least a star or a question mark */ public static boolean hasWildcards(String input) { return (input.indexOf('*') != -1 || input.indexOf('?') != -1); } /** * removes from a pattern all tokens to the right containing wildcards * @param input the input string * @return the leftmost part of the pattern without wildcards */ public static String rtrimWildcardTokens(String input) { Vector v = tokenizePath(input, File.separator); StringBuffer sb = new StringBuffer(); for (int counter = 0; counter < v.size(); counter++) { if (hasWildcards((String) v.elementAt(counter))) { break; } if (counter > 0) { sb.append(File.separator); } sb.append((String) v.elementAt(counter)); } return sb.toString(); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]