You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@avalon.apache.org by tb...@apache.org on 2004/04/17 21:16:01 UTC
cvs commit: avalon/merlin/composition/impl/src/java/org/apache/avalon/composition/model/impl DirectoryScanner.java Resource.java ScannerUtils.java
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 {@link #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 {@link 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 {@link #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: cvs-unsubscribe@avalon.apache.org
For additional commands, e-mail: cvs-help@avalon.apache.org