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
       * &quot;/&quot; 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
       * &quot;/&quot; 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
       * &quot;/&quot; 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>&quot;/&quot; will be used as the directory separator.</p>
       */
      public String getName() {
          return name;
      }
  
      /**
       * @param name relative path of the resource.  Expects
       * &quot;/&quot; 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