MSHARED-109 : Add ranges support in filters
Submitted by: Benoit Lafontaine

git-svn-id: https://svn.apache.org/repos/asf/maven/shared/trunk@803321 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/maven/shared/artifact/filter/AbstractStrictPatternArtifactFilter.java b/src/main/java/org/apache/maven/shared/artifact/filter/AbstractStrictPatternArtifactFilter.java
index bd476e4..05a214e 100644
--- a/src/main/java/org/apache/maven/shared/artifact/filter/AbstractStrictPatternArtifactFilter.java
+++ b/src/main/java/org/apache/maven/shared/artifact/filter/AbstractStrictPatternArtifactFilter.java
@@ -21,6 +21,9 @@
 
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
 
 import java.util.Iterator;
 import java.util.List;
@@ -171,6 +174,11 @@
 
             matches = token.startsWith( prefix );
         }
+        // support versions range 
+        else if ( pattern.startsWith( "[" ) || pattern.startsWith( "(" ))
+        {
+        	matches = isVersionIncludedInRange(token, pattern);
+        }
         // support exact match
         else
         {
@@ -179,4 +187,13 @@
 
         return matches;
     }
+    
+    private boolean isVersionIncludedInRange(final String version, final String range) {
+    	try {
+			return VersionRange.createFromVersionSpec(range).containsVersion(new DefaultArtifactVersion(version));
+		} catch (InvalidVersionSpecificationException e) {
+			return false;
+		}
+	}
+
 }
diff --git a/src/main/java/org/apache/maven/shared/artifact/filter/PatternIncludesArtifactFilter.java b/src/main/java/org/apache/maven/shared/artifact/filter/PatternIncludesArtifactFilter.java
index ce9d9e5..c81c262 100644
--- a/src/main/java/org/apache/maven/shared/artifact/filter/PatternIncludesArtifactFilter.java
+++ b/src/main/java/org/apache/maven/shared/artifact/filter/PatternIncludesArtifactFilter.java
@@ -27,6 +27,9 @@
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.ArtifactUtils;
 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
 import org.codehaus.plexus.logging.Logger;
 import org.codehaus.plexus.util.StringUtils;
 
@@ -162,67 +165,107 @@
         return false;
     }
 
-    private boolean matchAgainst( String value, List patterns, boolean regionMatch )
+    private boolean matchAgainst( String value, List patterns, boolean regionMatch ) {
+    	for (Iterator iterator = patterns.iterator(); iterator.hasNext();) {
+			String pattern = (String) iterator.next();
+			
+			String[] patternTokens = pattern.split( ":" );
+			String[] tokens = value.split( ":" );
+			
+			// fail immediately if pattern tokens outnumber tokens to match
+	        boolean matched = ( patternTokens.length <= tokens.length );
+
+	        for ( int i = 0; matched && i < patternTokens.length; i++ )
+	        {
+	            matched = matches( tokens[i], patternTokens[i] );
+	        }
+	        
+//	        // case of starting '*' like '*:jar:*'
+	        if (!matched && patternTokens.length < tokens.length && patternTokens.length>0 && "*".equals(patternTokens[0])) 
+	        {
+	        	matched=true;
+		        for ( int i = 0; matched && i < patternTokens.length; i++ )
+		        {
+		            matched = matches( tokens[i+(tokens.length-patternTokens.length)], patternTokens[i] );
+		        }
+	        }
+
+	        if (matched) {
+	        	patternsTriggered.add( pattern );
+                return true;
+	        }
+	        
+	        if ( regionMatch && value.indexOf( pattern ) > -1 )
+            {
+                patternsTriggered.add( pattern );
+                return true;
+            }
+			
+		}
+    	return false;
+    	
+    }
+
+    /**
+     * Gets whether the specified token matches the specified pattern segment.
+     * 
+     * @param token
+     *            the token to check
+     * @param pattern
+     *            the pattern segment to match, as defined above
+     * @return <code>true</code> if the specified token is matched by the specified pattern segment
+     */
+    private boolean matches( String token, final String pattern )
     {
-        patternLoop:
-        for ( Iterator i = patterns.iterator(); i.hasNext(); )
+    	boolean matches;
+
+        // support full wildcard and implied wildcard
+        if ( "*".equals( pattern ) || pattern.length() == 0 )
         {
-            // TODO: what about wildcards? Just specifying groups? versions?
-            String pattern = (String) i.next();
+            matches = true;
+        }
+        // support contains wildcard
+        else if ( pattern.startsWith( "*" ) && pattern.endsWith( "*" ) )
+        {
+            String contains = pattern.substring( 1, pattern.length() - 1 );
 
-            // don't allow wildcards in region-matched searches...i.e. in transitive dependencies.
-//            if ( regionMatch && ( pattern.indexOf( '*' ) > -1 ) )
-//            {
-//                continue;
-//            }
+            matches = ( token.indexOf( contains ) != -1 );
+        }
+        // support leading wildcard
+        else if ( pattern.startsWith( "*" ) )
+        {
+            String suffix = pattern.substring( 1, pattern.length() );
 
-            if ( value.equals( pattern ) )
-            {
-                patternsTriggered.add( pattern );
-                return true;
-            }
-            else if ( pattern.indexOf( '*' ) > -1 )
-            {
-                String[] subPatterns = pattern.split( "\\*" );
-                int[] idxes = new int[subPatterns.length];
+            matches = token.endsWith( suffix );
+        }
+        // support trailing wildcard
+        else if ( pattern.endsWith( "*" ) )
+        {
+            String prefix = pattern.substring( 0, pattern.length() - 1 );
 
-                for ( int j = 0; j < subPatterns.length; j++ )
-                {
-                    String subPattern = subPatterns[j];
-                    if ( subPattern.endsWith( "*" ) )
-                    {
-                        subPattern = subPattern.substring( 0, subPattern.length() - 1 );
-                    }
-
-                    if ( ( subPattern == null ) || ( subPattern.length() < 1 ) )
-                    {
-                        idxes[j] = j == 0 ? 0 : idxes[j - 1];
-
-                        continue;
-                    }
-
-                    int lastIdx = j == 0 ? 0 : idxes[j - 1];
-
-                    idxes[j] = value.indexOf( subPattern, lastIdx );
-
-                    if ( idxes[j] < 0 )
-                    {
-                    	patternsTriggered.add( pattern );
-                        continue patternLoop;
-                    }
-                }
-                
-                return true;
-            }
-            else if ( regionMatch && value.indexOf( pattern ) > -1 )
-            {
-                patternsTriggered.add( pattern );
-                return true;
-            }
+            matches = token.startsWith( prefix );
+        }
+        // support versions range 
+        else if ( pattern.startsWith( "[" ) || pattern.startsWith( "(" ))
+        {
+        	matches = isVersionIncludedInRange(token, pattern);
+        }
+        // support exact match
+        else
+        {
+            matches = token.equals( pattern );
         }
 
-        return false;
+        return matches;
     }
+    
+    private boolean isVersionIncludedInRange(final String version, final String range) {
+    	try {
+			return VersionRange.createFromVersionSpec(range).containsVersion(new DefaultArtifactVersion(version));
+		} catch (InvalidVersionSpecificationException e) {
+			return false;
+		}
+	}
 
     public void reportMissedCriteria( Logger logger )
     {
diff --git a/src/test/java/org/apache/maven/shared/artifact/filter/AbstractStrictPatternArtifactFilterTest.java b/src/test/java/org/apache/maven/shared/artifact/filter/AbstractStrictPatternArtifactFilterTest.java
index 26f23a8..39f8739 100644
--- a/src/test/java/org/apache/maven/shared/artifact/filter/AbstractStrictPatternArtifactFilterTest.java
+++ b/src/test/java/org/apache/maven/shared/artifact/filter/AbstractStrictPatternArtifactFilterTest.java
@@ -42,7 +42,7 @@
 {
     // fields -----------------------------------------------------------------
 
-    private Artifact artifact;
+    protected Artifact artifact;
 
     // TestCase methods -------------------------------------------------------
 
@@ -317,7 +317,31 @@
 
         assertIncluded( ":::*-SNAPSHOT" );
     }
+    
+    public void testRangeVersion()
+    {
+        artifact = createArtifact( "groupId", "artifactId", "type", "1.0.1" );
+        assertIncluded( "groupId:artifactId:type:[1.0.1]");
+        assertIncluded( "groupId:artifactId:type:[1.0,1.1)");
+        
+        assertExcluded( "groupId:artifactId:type:[1.5,)");
+        assertExcluded( "groupId:artifactId:type:(,1.0],[1.2,)");
+        assertExcluded( "groupId:artifactId:type:(,1.0],[1.2,)");
+    }
 
+    public void testWildcardsWithRangeVersion()
+    {
+        artifact = createArtifact( "groupId", "artifactId", "type", "1.0.1" );
+        assertIncluded( ":::[1.0.1]");
+        assertIncluded( ":artifact*:*:[1.0,1.1)");
+        
+        assertExcluded( "*group*:*:t*e:[1.5,)");
+
+        artifact = createArtifact( "test", "uf", "jar", "0.2.0" );
+        assertIncluded( "test:*:*:[0.0.2,)" );
+    	
+    }
+    
     // protected methods ------------------------------------------------------
 
     /**
diff --git a/src/test/java/org/apache/maven/shared/artifact/filter/PatternArtifactFilterTCK.java b/src/test/java/org/apache/maven/shared/artifact/filter/PatternArtifactFilterTCK.java
index 9ff3c21..36e18cb 100644
--- a/src/test/java/org/apache/maven/shared/artifact/filter/PatternArtifactFilterTCK.java
+++ b/src/test/java/org/apache/maven/shared/artifact/filter/PatternArtifactFilterTCK.java
@@ -390,6 +390,8 @@
 
         String artifactId;
 
+        String version;
+        
         List dependencyTrail;
 
         String type;
@@ -412,7 +414,7 @@
             artifact = (Artifact) control.getMock();
 
             enableGetDependencyConflictId();
-            enableGetGroupIdAndArtifactId();
+            enableGetGroupIdArtifactIdAndVersion();
             enableGetId();
 
             if ( dependencyTrail != null )
@@ -449,13 +451,17 @@
             control.setReturnValue( groupId + ":" + artifactId + ":" + type, MockControl.ONE_OR_MORE );
         }
 
-        void enableGetGroupIdAndArtifactId()
+        void enableGetGroupIdArtifactIdAndVersion() 
         {
             artifact.getGroupId();
             control.setReturnValue( groupId, MockControl.ONE_OR_MORE );
 
             artifact.getArtifactId();
             control.setReturnValue( artifactId, MockControl.ONE_OR_MORE );
+
+            artifact.getVersion();
+            control.setReturnValue( version , MockControl.ZERO_OR_MORE );
+            
         }
     }