You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by kr...@apache.org on 2012/10/01 22:01:21 UTC

svn commit: r1392561 - in /maven/shared/trunk/maven-shared-utils/src: main/java/org/apache/maven/shared/utils/io/ test/java/org/apache/maven/shared/utils/io/

Author: krosenvold
Date: Mon Oct  1 20:01:21 2012
New Revision: 1392561

URL: http://svn.apache.org/viewvc?rev=1392561&view=rev
Log:
o Backported performance pathc from plexus.

I am the original author of this patch

Added:
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/MatchPattern.java
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/MatchPatterns.java
    maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/io/MatchPatternTest.java
    maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/io/MatchPatternsTest.java
Modified:
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/SelectorUtils.java

Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java?rev=1392561&r1=1392560&r2=1392561&view=diff
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java (original)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/DirectoryScanner.java Mon Oct  1 20:01:21 2012
@@ -79,7 +79,7 @@ import java.util.Set;
  * Case sensitivity may be turned off if necessary. By default, it is turned on.
  * <p>
  * Example of usage:
- * 
+ *
  * <pre>
  * String[] includes = { &quot;**\\*.class&quot; };
  * String[] excludes = { &quot;modules\\*\\**&quot; };
@@ -88,7 +88,7 @@ import java.util.Set;
  * ds.setBasedir( new File( &quot;test&quot; ) );
  * ds.setCaseSensitive( true );
  * ds.scan();
- * 
+ *
  * System.out.println( &quot;FILES:&quot; );
  * String[] files = ds.getIncludedFiles();
  * for ( int i = 0; i &lt; files.length; i++ )
@@ -96,12 +96,12 @@ import java.util.Set;
  *     System.out.println( files[i] );
  * }
  * </pre>
- * 
+ *
  * This will scan a directory called test for .class files, but excludes all files in all proper subdirectories of a
  * directory called "modules"
  * <p>
  * This class must not be used from multiple Threads concurrently!
- * 
+ *
  * @author Arnout J. Kuiper <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
  * @author Magesh Umasankar
  * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
@@ -111,7 +111,7 @@ public class DirectoryScanner
 {
     /**
      * Patterns which should be excluded by default.
-     * 
+     *
      * @see #addDefaultExcludes()
      */
     public static final String[] DEFAULTEXCLUDES = {
@@ -169,6 +169,11 @@ public class DirectoryScanner
     /** The patterns for the files to be excluded. */
     protected String[] excludes;
 
+    private MatchPatterns excludesPatterns;
+
+    private MatchPatterns includesPatterns;
+
+
     /**
      * The files which matched at least one include and no excludes and were selected.
      */
@@ -215,7 +220,7 @@ public class DirectoryScanner
 
     /**
      * Whether or not symbolic links should be followed.
-     * 
+     *
      * @since Ant 1.5
      */
     private boolean followSymlinks = true;
@@ -245,7 +250,7 @@ public class DirectoryScanner
      * <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 "**".
@@ -260,7 +265,7 @@ public class DirectoryScanner
      * <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.
@@ -273,7 +278,7 @@ public class 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>.
      * @return <code>true</code> if the pattern matches against the string, or <code>false</code> otherwise.
@@ -285,7 +290,7 @@ public class 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.
@@ -300,7 +305,7 @@ public class DirectoryScanner
      * 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.
@@ -314,7 +319,7 @@ public class DirectoryScanner
      * 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.
@@ -329,7 +334,7 @@ public class DirectoryScanner
      * 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( final String basedir )
@@ -340,7 +345,7 @@ public class DirectoryScanner
 
     /**
      * 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( final File basedir )
@@ -350,7 +355,7 @@ public class DirectoryScanner
 
     /**
      * 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()
@@ -360,7 +365,7 @@ public class DirectoryScanner
 
     /**
      * Sets whether or not the file system should be regarded as case sensitive.
-     * 
+     *
      * @param isCaseSensitive whether or not the file system should be regarded as a case sensitive one
      */
     public void setCaseSensitive( final boolean isCaseSensitive )
@@ -370,7 +375,7 @@ public class DirectoryScanner
 
     /**
      * Sets whether or not symbolic links should be followed.
-     * 
+     *
      * @param followSymlinks whether or not symbolic links should be followed
      */
     public void setFollowSymlinks( final boolean followSymlinks )
@@ -383,7 +388,7 @@ public class DirectoryScanner
      * <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>.
      */
@@ -416,7 +421,7 @@ public class DirectoryScanner
      * <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>.
      */
@@ -451,7 +456,7 @@ public class DirectoryScanner
 
     /**
      * Returns whether or not the scanner has included all the files or directories it has come across so far.
-     * 
+     *
      * @return <code>true</code> if all files and directories which have been found so far have been included.
      */
     public boolean isEverythingIncluded()
@@ -462,7 +467,7 @@ public class DirectoryScanner
     /**
      * 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).
      */
@@ -482,16 +487,8 @@ public class DirectoryScanner
             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] = "**";
-        }
-        if ( excludes == null )
-        {
-            excludes = new String[0];
-        }
+        setupDefaultFilters();
+        setupMatchPatterns();
 
         filesIncluded = new ArrayList<String>();
         filesNotIncluded = new ArrayList<String>();
@@ -614,19 +611,16 @@ public class DirectoryScanner
 
         final String[] notIncl = dirsNotIncluded.toArray( new String[] {} );
 
-        for ( int i = 0; i < excl.length; i++ )
-        {
-            if ( !couldHoldIncluded( excl[i] ) )
-            {
-                scandir( new File( basedir, excl[i] ), excl[i] + File.separator, false );
+        for (String anExcl : excl) {
+            if (!couldHoldIncluded(anExcl)) {
+                scandir(new File(basedir, anExcl), anExcl + File.separator, false);
             }
         }
 
-        for ( int i = 0; i < notIncl.length; i++ )
+        for (String aNotIncl : notIncl)
         {
-            if ( !couldHoldIncluded( notIncl[i] ) )
-            {
-                scandir( new File( basedir, notIncl[i] ), notIncl[i] + File.separator, false );
+            if (!couldHoldIncluded(aNotIncl)) {
+                scandir(new File(basedir, aNotIncl), aNotIncl + File.separator, false);
             }
         }
 
@@ -637,7 +631,7 @@ public class DirectoryScanner
      * Scans the given directory for files and directories. Found files and directories are placed in their respective
      * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is
      * scanned recursively.
-     * 
+     *
      * @param dir The directory to scan. Must not be <code>null</code>.
      * @param vpath The path relative to the base directory (needed to prevent problems with an absolute path when using
      *            dir). Must not be <code>null</code>.
@@ -860,64 +854,43 @@ public class DirectoryScanner
 
     /**
      * 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( final String name )
     {
-        for ( final String include : includes )
-        {
-            if ( matchPath( include, name, isCaseSensitive ) )
-            {
-                return true;
-            }
-        }
-        return false;
+        return includesPatterns.matches( name, isCaseSensitive );
     }
 
     /**
      * Tests whether or not a name matches the start of 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 the start of at least one include pattern, or
      *         <code>false</code> otherwise.
      */
     protected boolean couldHoldIncluded( final String name )
     {
-        for ( final String include : includes )
-        {
-            if ( matchPatternStart( include, name, isCaseSensitive ) )
-            {
-                return true;
-            }
-        }
-        return false;
+        return includesPatterns.matchesPatternStart(name, isCaseSensitive);
     }
 
     /**
      * 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( final String name )
     {
-        for ( final String exclude : excludes )
-        {
-            if ( matchPath( exclude, name, isCaseSensitive ) )
-            {
-                return true;
-            }
-        }
-        return false;
+        return excludesPatterns.matches(name, isCaseSensitive);
     }
 
     /**
      * 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>
@@ -931,7 +904,7 @@ public class DirectoryScanner
     /**
      * Returns the names of the files which matched at least one of the include patterns and none of the exclude
      * patterns. The names are relative to the base directory.
-     * 
+     *
      * @return the names of the files which matched at least one of the include patterns and none of the exclude
      *         patterns.
      */
@@ -948,7 +921,7 @@ public class DirectoryScanner
     /**
      * Returns the names of the files which matched none of the include patterns. The names are relative to the base
      * directory. This involves performing a slow scan if one has not already been completed.
-     * 
+     *
      * @return the names of the files which matched none of the include patterns.
      * @see #slowScan
      */
@@ -963,7 +936,7 @@ public class DirectoryScanner
      * Returns the names of the files which matched at least one of the include patterns and at least one of the exclude
      * patterns. The names are relative to the base directory. This involves performing a slow scan if one has not
      * already been completed.
-     * 
+     *
      * @return the names of the files which matched at least one of the include patterns and at at least one of the
      *         exclude patterns.
      * @see #slowScan
@@ -983,7 +956,7 @@ public class DirectoryScanner
      * The names are relative to the base directory. This involves performing a slow scan if one has not already been
      * completed.
      * </p>
-     * 
+     *
      * @return the names of the files which were deselected.
      * @see #slowScan
      */
@@ -997,7 +970,7 @@ public class DirectoryScanner
     /**
      * Returns the names of the directories which matched at least one of the include patterns and none of the exclude
      * patterns. The names are relative to the base directory.
-     * 
+     *
      * @return the names of the directories which matched at least one of the include patterns and none of the exclude
      *         patterns.
      */
@@ -1010,7 +983,7 @@ public class DirectoryScanner
     /**
      * Returns the names of the directories which matched none of the include patterns. The names are relative to the
      * base directory. This involves performing a slow scan if one has not already been completed.
-     * 
+     *
      * @return the names of the directories which matched none of the include patterns.
      * @see #slowScan
      */
@@ -1025,7 +998,7 @@ public class DirectoryScanner
      * Returns the names of the directories which matched at least one of the include patterns and at least one of the
      * exclude patterns. The names are relative to the base directory. This involves performing a slow scan if one has
      * not already been completed.
-     * 
+     *
      * @return the names of the directories which matched at least one of the include patterns and at least one of the
      *         exclude patterns.
      * @see #slowScan
@@ -1045,7 +1018,7 @@ public class DirectoryScanner
      * The names are relative to the base directory. This involves performing a slow scan if one has not already been
      * completed.
      * </p>
-     * 
+     *
      * @return the names of the directories which were deselected.
      * @see #slowScan
      */
@@ -1082,7 +1055,7 @@ public class DirectoryScanner
      * It doesn't really test for symbolic links but whether the canonical and absolute paths of the file are identical
      * - this may lead to false positives on some platforms.
      * </p>
-     * 
+     *
      * @param parent the parent directory of the file to test
      * @param name the name of the file to test.
      * @since Ant 1.5
@@ -1096,4 +1069,24 @@ public class DirectoryScanner
                       .equals( toTest.getCanonicalPath() );
     }
 
+    private void setupDefaultFilters() {
+        if ( includes == null )
+        {
+            // No includes supplied, so set it to 'matches all'
+            includes = new String[1];
+            includes[0] = "**";
+        }
+        if ( excludes == null )
+        {
+            excludes = new String[0];
+        }
+    }
+
+
+    private void setupMatchPatterns()
+    {
+        includesPatterns = MatchPatterns.from( includes );
+        excludesPatterns = MatchPatterns.from( excludes );
+    }
+
 }

Added: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/MatchPattern.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/MatchPattern.java?rev=1392561&view=auto
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/MatchPattern.java (added)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/MatchPattern.java Mon Oct  1 20:01:21 2012
@@ -0,0 +1,128 @@
+package org.apache.maven.shared.utils.io;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * Describes a match target for SelectorUtils.
+ * <p/>
+ * Significantly more efficient than using strings, since re-evaluation and re-tokenizing is avoided.
+ *
+ * @author Kristian Rosenvold
+ */
+public class MatchPattern
+{
+    private final String source;
+
+    private final String regexPattern;
+
+    private final String separator;
+
+    private final String[] tokenized;
+
+    private MatchPattern(String source, String separator)
+    {
+        regexPattern = SelectorUtils.isRegexPrefixedPattern( source ) ? source.substring(
+            SelectorUtils.REGEX_HANDLER_PREFIX.length(),
+            source.length() - SelectorUtils.PATTERN_HANDLER_SUFFIX.length() ) : null;
+        this.source =
+            SelectorUtils.isAntPrefixedPattern( source )
+                ? source.substring( SelectorUtils.ANT_HANDLER_PREFIX.length(), source.length()
+                - SelectorUtils.PATTERN_HANDLER_SUFFIX.length() )
+                : source;
+        this.separator = separator;
+        tokenized = tokenizePathToString( this.source, separator );
+    }
+
+
+
+    public boolean matchPath( String str, boolean isCaseSensitive )
+    {
+        if ( regexPattern != null )
+        {
+            return str.matches( regexPattern );
+        }
+        else
+        {
+            return SelectorUtils.matchAntPathPattern( this, str, separator, isCaseSensitive );
+        }
+    }
+
+    boolean matchPath( String str, String[] strDirs, boolean isCaseSensitive )
+    {
+        if ( regexPattern != null )
+        {
+            return str.matches( regexPattern );
+        }
+        else
+        {
+            return SelectorUtils.matchAntPathPattern( getTokenizedPathString(), strDirs, isCaseSensitive );
+        }
+    }
+
+    public boolean matchPatternStart( String str, boolean isCaseSensitive )
+    {
+        if ( regexPattern != null )
+        {
+            // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have
+            // a file to deal with, or we can definitely say this is an exclusion...
+            return true;
+        }
+        else
+        {
+            String altStr = source.replace( '\\', '/' );
+
+            return SelectorUtils.matchAntPathPatternStart( this, str, File.separator, isCaseSensitive )
+                || SelectorUtils.matchAntPathPatternStart( this, altStr, "/", isCaseSensitive );
+        }
+    }
+
+    public String[] getTokenizedPathString()
+    {
+        return tokenized;
+    }
+
+
+    public boolean startsWith( String string )
+    {
+        return source.startsWith( string );
+    }
+
+
+    static String[] tokenizePathToString( String path, String separator )
+    {
+        List<String> ret = new ArrayList<String>();
+        StringTokenizer st = new StringTokenizer( path, separator );
+        while ( st.hasMoreTokens() )
+        {
+            ret.add( st.nextToken() );
+        }
+        return ret.toArray( new String[ret.size()] );
+    }
+
+    public static MatchPattern fromString( String source )
+    {
+        return new MatchPattern( source, File.separator );
+    }
+
+}

Added: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/MatchPatterns.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/MatchPatterns.java?rev=1392561&view=auto
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/MatchPatterns.java (added)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/MatchPatterns.java Mon Oct  1 20:01:21 2012
@@ -0,0 +1,98 @@
+package org.apache.maven.shared.utils.io;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A list of patterns to be matched
+ *
+ * @author Kristian Rosenvold
+ */
+public class MatchPatterns
+{
+    private final MatchPattern[] patterns;
+
+    private MatchPatterns(MatchPattern[] patterns)
+    {
+        this.patterns = patterns;
+    }
+
+    /**
+     * Checks these MatchPatterns against a specified string.
+     * <p/>
+     * Uses far less string tokenization than any of the alternatives.
+     *
+     * @param name            The name to look for
+     * @param isCaseSensitive If the comparison is case sensitive
+     * @return true if any of the supplied patterns match
+     */
+    public boolean matches( String name, boolean isCaseSensitive )
+    {
+        String[] tokenized = MatchPattern.tokenizePathToString( name, File.separator );
+        for ( MatchPattern pattern : patterns )
+        {
+            if ( pattern.matchPath( name, tokenized, isCaseSensitive ) )
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean matchesPatternStart( String name, boolean isCaseSensitive )
+    {
+        for ( MatchPattern includesPattern : patterns )
+        {
+            if ( includesPattern.matchPatternStart( name, isCaseSensitive ) )
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static MatchPatterns from( String... sources )
+    {
+        final int length = sources.length;
+        MatchPattern[] result = new MatchPattern[length];
+        for ( int i = 0; i < length; i++ )
+        {
+            result[i] = MatchPattern.fromString( sources[i] );
+        }
+        return new MatchPatterns( result );
+    }
+
+    public static MatchPatterns from( Iterable<String> strings )
+    {
+        return new MatchPatterns( getMatchPatterns( strings ) );
+    }
+
+    private static MatchPattern[] getMatchPatterns( Iterable<String> items )
+    {
+        List<MatchPattern> result = new ArrayList<MatchPattern>();
+        for ( String string : items )
+        {
+            result.add( MatchPattern.fromString( string ) );
+        }
+        return result.toArray( new MatchPattern[result.size()] );
+    }
+
+}

Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/SelectorUtils.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/SelectorUtils.java?rev=1392561&r1=1392560&r2=1392561&view=diff
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/SelectorUtils.java (original)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/io/SelectorUtils.java Mon Oct  1 20:01:21 2012
@@ -21,6 +21,8 @@ package org.apache.maven.shared.utils.io
 
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
@@ -108,8 +110,7 @@ public final class SelectorUtils
     public static boolean matchPatternStart( String pattern, String str,
                                              boolean isCaseSensitive )
     {
-        if ( pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
-                && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) )
+        if ( isRegexPrefixedPattern( pattern )  )
         {
             // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have
             // a file to deal with, or we can definitely say this is an exclusion...
@@ -117,8 +118,7 @@ public final class SelectorUtils
         }
         else
         {
-            if ( pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
-                    && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) )
+            if ( isAntPrefixedPattern( pattern ) )
             {
                 pattern =
                         pattern.substring( ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() );
@@ -137,8 +137,7 @@ public final class SelectorUtils
         // File.separator.
         // When pattern starts with a File.separator, str has to start with a
         // File.separator.
-        if ( str.startsWith( separator ) !=
-                pattern.startsWith( separator ) )
+        if ( separatorPatternStartSlashMismatch( pattern, str, separator ) )
         {
             return false;
         }
@@ -685,4 +684,218 @@ public final class SelectorUtils
         }
         return result.toString();
     }
+
+    static boolean matchAntPathPatternStart( MatchPattern pattern, String str, String separator,
+                                             boolean isCaseSensitive )
+    {
+        if ( separatorPatternStartSlashMismatch( pattern, str, separator ) )
+        {
+            return false;
+        }
+
+        return matchAntPathPatternStart( pattern.getTokenizedPathString(), str, separator, isCaseSensitive );
+    }
+
+    private static String[] tokenizePathToString( String path, String separator )
+    {
+        List<String> ret = new ArrayList<String>();
+        StringTokenizer st = new StringTokenizer( path, separator );
+        while ( st.hasMoreTokens() )
+        {
+            ret.add( st.nextToken() );
+        }
+        return ret.toArray(new String[ret.size()]);
+    }
+
+    static boolean matchAntPathPatternStart( String[] patDirs, String str, String separator, boolean isCaseSensitive )
+    {
+        String[] strDirs = tokenizePathToString( str, separator );
+
+        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++;
+        }
+
+        return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd;
+    }
+
+    private static boolean separatorPatternStartSlashMismatch( MatchPattern matchPattern, String str, String separator )
+    {
+        return str.startsWith( separator ) != matchPattern.startsWith( separator );
+    }
+
+    private static boolean separatorPatternStartSlashMismatch( String pattern, String str, String separator )
+    {
+        return str.startsWith( separator ) != pattern.startsWith( separator );
+    }
+
+
+    static boolean matchAntPathPattern( String[] patDirs, String[] strDirs, boolean isCaseSensitive )
+    {
+        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
+            for ( int i = patIdxStart; i <= patIdxEnd; i++ )
+            {
+                if ( !patDirs[i].equals( "**" ) )
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+        else
+        {
+            if ( patIdxStart > patIdxEnd )
+            {
+                // String not exhausted, but pattern is. Failure.
+                return false;
+            }
+        }
+
+        // up to last '**'
+        while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
+        {
+            String patDir = patDirs[patIdxEnd];
+            if ( patDir.equals( "**" ) )
+            {
+                break;
+            }
+            if ( !match( patDir, strDirs[strIdxEnd], isCaseSensitive ) )
+            {
+                return false;
+            }
+            patIdxEnd--;
+            strIdxEnd--;
+        }
+        if ( strIdxStart > strIdxEnd )
+        {
+            // String is exhausted
+            for ( int i = patIdxStart; i <= patIdxEnd; i++ )
+            {
+                if ( !patDirs[i].equals( "**" ) )
+                {
+                    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 )
+            {
+                return false;
+            }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx + patLength;
+        }
+
+        for ( int i = patIdxStart; i <= patIdxEnd; i++ )
+        {
+            if ( !patDirs[i].equals( "**" ) )
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    static boolean isRegexPrefixedPattern( String pattern )
+    {
+        return pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
+                && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX );
+    }
+    static boolean isAntPrefixedPattern( String pattern )
+    {
+        return pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 )
+                && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX );
+    }
+
+    static boolean matchAntPathPattern( MatchPattern matchPattern, String str, String separator,
+                                        boolean isCaseSensitive )
+    {
+        if ( separatorPatternStartSlashMismatch( matchPattern, str, separator ) )
+        {
+            return false;
+        }
+        String[] patDirs = matchPattern.getTokenizedPathString();
+        String[] strDirs = tokenizePathToString( str, separator );
+        return matchAntPathPattern( patDirs, strDirs, isCaseSensitive );
+    }
 }

Added: maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/io/MatchPatternTest.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/io/MatchPatternTest.java?rev=1392561&view=auto
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/io/MatchPatternTest.java (added)
+++ maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/io/MatchPatternTest.java Mon Oct  1 20:01:21 2012
@@ -0,0 +1,37 @@
+package org.apache.maven.shared.utils.io;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+
+/**
+ * @author Kristian Rosenvold
+ */
+public class MatchPatternTest {
+    @Test
+    public void matchPath()
+            throws Exception
+    {
+        MatchPattern mp = MatchPattern.fromString( "ABC*" );
+        assertTrue(mp.matchPath( "ABCD", true ));
+    }
+
+}

Added: maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/io/MatchPatternsTest.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/io/MatchPatternsTest.java?rev=1392561&view=auto
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/io/MatchPatternsTest.java (added)
+++ maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/io/MatchPatternsTest.java Mon Oct  1 20:01:21 2012
@@ -0,0 +1,38 @@
+package org.apache.maven.shared.utils.io;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Kristian Rosenvold
+ */
+public class MatchPatternsTest {
+    @Test
+    public void matches()
+            throws Exception
+    {
+        MatchPatterns from = MatchPatterns.from( "ABC**", "CDE**" );
+        assertTrue( from.matches( "ABCDE", true ) );
+        assertTrue( from.matches( "CDEF", true ) );
+        assertFalse( from.matches( "XYZ", true ) );
+
+    }
+}