You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by sl...@apache.org on 2021/01/23 00:47:20 UTC

[maven-common-artifact-filters] 03/04: Broaden the scope of the optimizations

This is an automated email from the ASF dual-hosted git repository.

slachiewicz pushed a commit to branch speed
in repository https://gitbox.apache.org/repos/asf/maven-common-artifact-filters.git

commit 830b27b5a5aca5f8506b653dc987d893390321cf
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Fri Nov 20 00:08:33 2020 +0100

    Broaden the scope of the optimizations
---
 pom.xml                                            |  12 +
 .../filter/PatternExcludesArtifactFilter.java      |   2 +-
 .../filter/PatternIncludesArtifactFilter.java      | 937 +++++++++++++++------
 .../filter/AbstractPatternArtifactFilterTest.java  | 105 ++-
 .../filter/OldPatternIncludesArtifactFilter.java}  | 106 +--
 .../artifact/filter/PatternFilterPerfTest.java     | 122 +++
 6 files changed, 903 insertions(+), 381 deletions(-)

diff --git a/pom.xml b/pom.xml
index b39ffd3..28c3e23 100644
--- a/pom.xml
+++ b/pom.xml
@@ -160,5 +160,17 @@
       </exclusions>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.openjdk.jmh</groupId>
+      <artifactId>jmh-core</artifactId>
+      <version>1.26</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.openjdk.jmh</groupId>
+      <artifactId>jmh-generator-annprocess</artifactId>
+      <version>1.26</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
diff --git a/src/main/java/org/apache/maven/shared/artifact/filter/PatternExcludesArtifactFilter.java b/src/main/java/org/apache/maven/shared/artifact/filter/PatternExcludesArtifactFilter.java
index b0d7a1e..7b7b267 100644
--- a/src/main/java/org/apache/maven/shared/artifact/filter/PatternExcludesArtifactFilter.java
+++ b/src/main/java/org/apache/maven/shared/artifact/filter/PatternExcludesArtifactFilter.java
@@ -56,7 +56,7 @@ public class PatternExcludesArtifactFilter
 
         if ( !shouldInclude )
         {
-            addFilteredArtifactId( artifact.getId() );
+            addFilteredArtifactId( artifact );
         }
 
         return shouldInclude;
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 8d01c71..bf5138a 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
@@ -20,14 +20,18 @@ package org.apache.maven.shared.artifact.filter;
  */
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 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;
@@ -43,19 +47,20 @@ import org.codehaus.plexus.logging.Logger;
 public class PatternIncludesArtifactFilter
     implements ArtifactFilter, StatisticsReportingArtifactFilter
 {
-    private final Set<String> simplePositivePatterns;
+    /** Holds the set of compiled patterns */
+    private final Set<Pattern> patterns;
 
-    private final Set<String> positivePatterns;
-
-    private final Set<String> simpleNegativePatterns;
-
-    private final Set<String> negativePatterns;
+    /** Holds simple patterns: those that can use direct matching */
+    private final Map<Integer, Map<String, Pattern>> simplePatterns;
 
+    /** Wether the dependency trail should be checked */
     private final boolean actTransitively;
 
-    private final Set<String> patternsTriggered = new HashSet<>();
+    /** Set of patterns that have been triggered */
+    private final Set<Pattern> patternsTriggered = new HashSet<>();
 
-    private final List<String> filteredArtifactIds = new ArrayList<>();
+    /** Set of artifacts that have been filtered out */
+    private final List<Artifact> filteredArtifact = new ArrayList<>();
 
     /**
      * @param patterns The pattern to be used.
@@ -72,43 +77,55 @@ public class PatternIncludesArtifactFilter
     public PatternIncludesArtifactFilter( final Collection<String> patterns, final boolean actTransitively )
     {
         this.actTransitively = actTransitively;
-        final Set<String> pos = new LinkedHashSet<>();
-        final Set<String> spos = new LinkedHashSet<>();
-        final Set<String> neg = new LinkedHashSet<>();
-        final Set<String> sneg = new LinkedHashSet<>();
+        final Set<Pattern> pat = new LinkedHashSet<>();
+        Map<Integer, Map<String, Pattern>> simplePat = null;
+        boolean allPos = true;
         if ( patterns != null && !patterns.isEmpty() )
         {
             for ( String pattern : patterns )
             {
-                if ( pattern.startsWith( "!" ) )
+
+                Pattern p = compile( pattern );
+                allPos &= !( p instanceof NegativePattern );
+                pat.add( p );
+            }
+        }
+        // If all patterns are positive, we can check for simple patterns
+        // Simple patterns will match the first tokens and contain no wildcards,
+        // so we can put them in a map and check them using a simple map lookup.
+        if ( allPos )
+        {
+            for ( Iterator<Pattern> it = pat.iterator(); it.hasNext(); )
+            {
+                Pattern p = it.next();
+                String peq = p.translateEquals();
+                if ( peq != null )
                 {
-                    if ( pattern.contains( "*" ) )
+                    int nb = 0;
+                    for ( char ch : peq.toCharArray() )
                     {
-                        neg.add( pattern.substring( 1 ) );
+                        if ( ch == ':' )
+                        {
+                            nb++;
+                        }
                     }
-                    else
+                    if ( simplePat == null )
                     {
-                        sneg.add( pattern.substring( 1 ) );
+                        simplePat = new HashMap<>();
                     }
-                }
-                else
-                {
-                    if ( pattern.contains( "*" ) )
+                    Map<String, Pattern> peqm = simplePat.get( nb );
+                    if ( peqm == null )
                     {
-                        pos.add( pattern );
-                    }
-                    else
-                    {
-                        spos.add( pattern );
+                        peqm = new HashMap<>();
+                        simplePat.put( nb, peqm );
                     }
+                    peqm.put( peq, p );
+                    it.remove();
                 }
             }
         }
-
-        positivePatterns = pos;
-        simplePositivePatterns = spos;
-        negativePatterns = neg;
-        simpleNegativePatterns = sneg;
+        this.simplePatterns = simplePat;
+        this.patterns = pat;
     }
 
     /** {@inheritDoc} */
@@ -118,7 +135,7 @@ public class PatternIncludesArtifactFilter
 
         if ( !shouldInclude )
         {
-            addFilteredArtifactId( artifact.getId() );
+            addFilteredArtifactId( artifact );
         }
 
         return shouldInclude;
@@ -130,369 +147,783 @@ public class PatternIncludesArtifactFilter
      */
     protected boolean patternMatches( final Artifact artifact )
     {
-        return positiveMatch( artifact ) == Boolean.TRUE || negativeMatch( artifact ) == Boolean.FALSE;
+        // Check if the main artifact matches
+        char[][] tokens = new char[][] {
+                            emptyOrChars( artifact.getGroupId() ),
+                            emptyOrChars( artifact.getArtifactId() ),
+                            emptyOrChars( artifact.getType() ),
+                            emptyOrChars( artifact.getBaseVersion() )
+                    };
+        Boolean match = match( tokens );
+        if ( match != null )
+        {
+            return match;
+        }
+
+        if ( actTransitively )
+        {
+            final List<String> depTrail = artifact.getDependencyTrail();
+
+            if ( depTrail != null && depTrail.size() > 1 )
+            {
+                for ( String trailItem : depTrail )
+                {
+                    char[][] depTokens = tokenizeAndSplit( trailItem );
+                    match = match( depTokens );
+                    if ( match != null)
+                    {
+                        return match;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private Boolean match( char[][] tokens )
+    {
+        if ( simplePatterns != null && simplePatterns.size() > 0 )
+        {
+            // We add the parts one by one to the builder
+            StringBuilder sb = new StringBuilder();
+            for ( int i = 0; i < 4; i++ )
+            {
+                if ( i > 0 )
+                {
+                    sb.append( ":" );
+                }
+                sb.append( tokens[i] );
+                Map<String, Pattern> map = simplePatterns.get( i );
+                if ( map != null )
+                {
+                    // Check if one of the pattern matches
+                    Pattern p = map.get( sb.toString() );
+                    if ( p != null )
+                    {
+                        patternsTriggered.add( p );
+                        return true;
+                    }
+                }
+            }
+        }
+        // Check all other patterns
+        for ( Pattern pattern : patterns )
+        {
+            if ( pattern.matches( tokens ) )
+            {
+                patternsTriggered.add( pattern );
+                return !(pattern instanceof NegativePattern);
+            }
+        }
+
+        return null;
     }
 
     /**
-     * @param artifactId add artifact to the filtered artifacts list.
+     * @param artifact add artifact to the filtered artifacts list.
      */
-    protected void addFilteredArtifactId( final String artifactId )
+    protected void addFilteredArtifactId( final Artifact artifact )
     {
-        filteredArtifactIds.add( artifactId );
+        filteredArtifact.add( artifact );
     }
 
-    private Boolean negativeMatch( final Artifact artifact )
+    /** {@inheritDoc} */
+    public void reportMissedCriteria( final Logger logger )
     {
-        if ( simpleNegativePatterns != null && !simpleNegativePatterns.isEmpty() )
+        // if there are no patterns, there is nothing to report.
+        if ( !patterns.isEmpty() )
         {
-            if ( simpleMatch( artifact, simpleNegativePatterns ) )
+            final List<Pattern> missed = new ArrayList<>( patterns );
+            missed.removeAll( patternsTriggered );
+
+            if ( !missed.isEmpty() && logger.isWarnEnabled() )
             {
-                return true;
+                final StringBuilder buffer = new StringBuilder();
+
+                buffer.append( "The following patterns were never triggered in this " );
+                buffer.append( getFilterDescription() );
+                buffer.append( ':' );
+
+                for ( Pattern pattern : missed )
+                {
+                    buffer.append( "\no  '" ).append( pattern ).append( "'" );
+                }
+
+                buffer.append( "\n" );
+
+                logger.warn( buffer.toString() );
             }
         }
-        if ( negativePatterns == null || negativePatterns.isEmpty() )
-        {
-            return null;
-        }
-        else
+    }
+
+    @Override
+    public String toString()
+    {
+        return "Includes filter:" + getPatternsAsString();
+    }
+
+    /**
+     * @return pattern as a string.
+     */
+    protected String getPatternsAsString()
+    {
+        final StringBuilder buffer = new StringBuilder();
+        for ( Pattern pattern : patterns )
         {
-            return match( artifact, negativePatterns );
+            buffer.append( "\no '" ).append( pattern ).append( "'" );
         }
+
+        return buffer.toString();
     }
 
     /**
-     * @param artifact check for positive match.
-     * @return true/false.
+     * @return description.
      */
-    protected Boolean positiveMatch( final Artifact artifact )
+    protected String getFilterDescription()
     {
-        if ( simplePositivePatterns != null && !simplePositivePatterns.isEmpty() )
+        return "artifact inclusion filter";
+    }
+
+    /** {@inheritDoc} */
+    public void reportFilteredArtifacts( final Logger logger )
+    {
+        if ( !filteredArtifact.isEmpty() && logger.isDebugEnabled() )
         {
-            if ( simpleMatch( artifact, simplePositivePatterns ) )
+            final StringBuilder buffer =
+                new StringBuilder( "The following artifacts were removed by this " + getFilterDescription() + ": " );
+
+            for ( Artifact artifactId : filteredArtifact )
             {
-                return true;
+                buffer.append( '\n' ).append( artifactId.getId() );
             }
+
+            logger.debug( buffer.toString() );
         }
-        if ( positivePatterns == null || positivePatterns.isEmpty() )
+    }
+
+    /** {@inheritDoc} */
+    public boolean hasMissedCriteria()
+    {
+        // if there are no patterns, there is nothing to report.
+        if ( !patterns.isEmpty() )
         {
-            return null;
+            final List<Pattern> missed = new ArrayList<>( patterns );
+            missed.removeAll( patternsTriggered );
+
+            return !missed.isEmpty();
         }
-        else
+
+        return false;
+    }
+
+    private static final char[] EMPTY = new char[0];
+
+    private static final char[] ANY = new char[] { '*' };
+
+    static char[] emptyOrChars( String str )
+    {
+        return str != null && str.length() > 0 ? str.toCharArray() : EMPTY;
+    }
+
+    static char[] anyOrChars( char[] str )
+    {
+        return str.length > 1 || ( str.length == 1 && str[0] != '*' ) ? str : ANY;
+    }
+
+    static char[][] tokenizeAndSplit( String pattern )
+    {
+        String[] stokens = pattern.split( ":" );
+        char[][] tokens = new char[ stokens.length ][];
+        for ( int i = 0; i < stokens.length; i++ )
         {
-            return match( artifact, positivePatterns );
+            tokens[i] = emptyOrChars( stokens[i] );
         }
+        return tokens;
     }
 
-    private boolean simpleMatch( final Artifact artifact, final Set<String> patterns )
+    static boolean match( char[] patArr, char[] strArr, boolean isVersion )
     {
-        final String shortId = ArtifactUtils.versionlessKey( artifact );
-        if ( patterns.contains( shortId ) )
+        int patIdxStart = 0;
+        int patIdxEnd = patArr.length - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strArr.length - 1;
+        char ch;
+
+        boolean containsStar = false;
+        for ( char aPatArr : patArr )
         {
-            patternsTriggered.add( shortId );
-            return true;
+            if ( aPatArr == '*' )
+            {
+                containsStar = true;
+                break;
+            }
         }
 
-        final String id = artifact.getDependencyConflictId();
-        if ( patterns.contains( id ) )
+        if ( !containsStar )
         {
-            patternsTriggered.add( id );
-            return true;
+            if ( isVersion && ( patArr[0] == '[' || patArr[0] == '(' ) )
+            {
+                return isVersionIncludedInRange( String.valueOf( strArr ), String.valueOf( patArr ) );
+            }
+            // 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 != '?' && ch != strArr[i] )
+                {
+                    return false; // Character mismatch
+                }
+            }
+            return true; // String matches against pattern
         }
 
-        final String wholeId = artifact.getId();
-        if ( patterns.contains( wholeId ) )
+        if ( patIdxEnd == 0 )
         {
-            patternsTriggered.add( wholeId );
-            return true;
+            return true; // Pattern contains only '*', which matches anything
         }
 
-        return false;
-    }
-
-    private boolean match( final Artifact artifact, final Iterable<String> patterns )
-    {
-        final String shortId = ArtifactUtils.versionlessKey( artifact );
-        final String id = artifact.getDependencyConflictId();
-        final String wholeId = artifact.getId();
-
-        if ( matchAgainst( wholeId, patterns, false ) )
+        // Process characters before first star
+        while ( ( ch = patArr[patIdxStart] ) != '*' && strIdxStart <= strIdxEnd )
         {
-            return true;
+            if ( ch != '?' && ch != strArr[strIdxStart] )
+            {
+                return false; // Character mismatch
+            }
+            patIdxStart++;
+            strIdxStart++;
         }
-
-        if ( matchAgainst( id, patterns, false ) )
+        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;
         }
 
-        if ( matchAgainst( shortId, patterns, false ) )
+        // Process characters after last star
+        while ( ( ch = patArr[patIdxEnd] ) != '*' && strIdxStart <= strIdxEnd )
         {
+            if ( ch != '?' && ch != 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;
         }
 
-        if ( actTransitively )
+        // process pattern between stars. padIdxStart and patIdxEnd point
+        // always to a '*'.
+        while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
         {
-            final List<String> depTrail = artifact.getDependencyTrail();
-
-            if ( depTrail != null && depTrail.size() > 1 )
+            int patIdxTmp = -1;
+            for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
             {
-                for ( String trailItem : depTrail )
+                if ( patArr[i] == '*' )
                 {
-                    if ( matchAgainst( trailItem, patterns, true ) )
+                    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 != '?' && ch != strArr[strIdxStart + i + j] )
                     {
-                        return true;
+                        continue strLoop;
                     }
                 }
+
+                foundIdx = strIdxStart + i;
+                break;
+            }
+
+            if ( foundIdx == -1 )
+            {
+                return false;
             }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx + patLength;
         }
 
-        return false;
+        // 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;
+    }
+
+    static boolean isVersionIncludedInRange( final String version, final String range )
+    {
+        try
+        {
+            return VersionRange.createFromVersionSpec( range ).containsVersion( new DefaultArtifactVersion( version ) );
+        }
+        catch ( final InvalidVersionSpecificationException e )
+        {
+            return false;
+        }
     }
 
-    private boolean matchAgainst( final String value, final Iterable<String> patterns, final boolean regionMatch )
+    static Pattern compile( String pattern )
     {
-        final String[] tokens = value.split( ":" );
-        for ( String pattern : patterns )
+        if ( pattern.startsWith( "!" ) )
+        {
+            return new NegativePattern( pattern, compile( pattern.substring( 1 ) ) );
+        }
+        else
         {
-            String[] patternTokens = pattern.split( ":" );
-            
-            if ( patternTokens.length == 5 && tokens.length < 5 )
+            char[][] stokens = tokenizeAndSplit( pattern );
+            char[][] tokens = new char[ stokens.length ][];
+            for ( int i = 0; i < stokens.length; i++ )
             {
-                // 4th element is the classifier
-                if ( !"*".equals( patternTokens[3] ) )
+                tokens[i] = anyOrChars( stokens[i] );
+            }
+            if ( tokens.length > 5 )
+            {
+                throw new IllegalArgumentException( "Invalid pattern: " + pattern );
+            }
+            // we only accept 5 tokens if the classifier = '*'
+            if ( tokens.length == 5 )
+            {
+                if ( tokens[3] != ANY )
                 {
-                    // classifier required, cannot be a match
-                    return false;
+                    throw new IllegalArgumentException( "Invalid pattern: " + pattern );
                 }
-                patternTokens = new String[] { patternTokens[0], patternTokens[1], patternTokens[2], patternTokens[4] };
+                tokens = new char[][] { tokens[0], tokens[1], tokens[2], tokens[4] };
             }
-
-            // fail immediately if pattern tokens outnumber tokens to match
-            boolean matched = patternTokens.length <= tokens.length;
-
-            for ( int i = 0; matched && i < patternTokens.length; i++ )
+            //
+            // Check the 4 tokens and build an appropriate Pattern
+            // Special care needs to be taken if the first or the last part is '*'
+            // because this allows the '*' to match multiple tokens
+            //
+            if ( tokens.length == 1 )
             {
-                matched = matches( tokens[i], patternTokens[i] );
+                if ( tokens[0] == ANY )
+                {
+                    // *
+                    return all( pattern );
+                }
+                else
+                {
+                    // [pat0]
+                    return match( pattern, tokens[0], 0 );
+                }
             }
-
-            // case of starting '*' like '*:jar:*'
-            // This really only matches from the end instead.....
-            if ( !matched && patternTokens.length < tokens.length && isFirstPatternWildcard( patternTokens ) )
+            if ( tokens.length == 2 )
             {
-                matched = true;
-                int tokenOffset = tokens.length - patternTokens.length;
-                for ( int i = 0; matched && i < patternTokens.length; i++ )
+                if ( tokens[0] == ANY )
+                {
+                    if ( tokens[1] == ANY )
+                    {
+                        // *:*
+                        return all( pattern );
+                    }
+                    else
+                    {
+                        // *:[pat1]
+                        return match( pattern, tokens[1], 0, 3 );
+                    }
+                }
+                else
                 {
-                    matched = matches( tokens[i + tokenOffset], patternTokens[i] );
+                    if ( tokens[1] == ANY )
+                    {
+                        // [pat0]:*
+                        return match( pattern, tokens[0], 0 );
+                    }
+                    else
+                    {
+                        // [pat0]:[pat1]
+                        Pattern m00 = match( tokens[0], 0 );
+                        Pattern m11 = match( tokens[1], 1 );
+                        return and( pattern, m00, m11 );
+                    }
                 }
             }
-
-            if ( matched )
+            if ( tokens.length == 3 )
             {
-                patternsTriggered.add( pattern );
-                return true;
+                if ( tokens[0] == ANY )
+                {
+                    if ( tokens[1] == ANY )
+                    {
+                        if ( tokens[2] == ANY )
+                        {
+                            // *:*:*
+                            return all( pattern );
+                        }
+                        else
+                        {
+                            // *:*:[pat2]
+                            return match( pattern, tokens[2], 2, 3 );
+                        }
+                    }
+                    else
+                    {
+                        if ( tokens[2] == ANY )
+                        {
+                            // *:[pat1]:*
+                            return match( pattern, tokens[1], 1, 2 );
+                        }
+                        else
+                        {
+                            // *:[pat1]:[pat2]
+                            Pattern m11 = match( tokens[1], 1 );
+                            Pattern m12 = match( tokens[1], 2 );
+                            Pattern m22 = match( tokens[2], 2 );
+                            Pattern m23 = match( tokens[2], 3 );
+                            return or( pattern, and( m11, m22 ), and( m12, m23 ) );
+                        }
+                    }
+                }
+                else
+                {
+                    if ( tokens[1] == ANY )
+                    {
+                        if ( tokens[2] == ANY )
+                        {
+                            // [pat0]:*:*
+                            return match( pattern, tokens[0], 0, 1 );
+                        }
+                        else
+                        {
+                            // [pat0]:*:[pat2]
+                            Pattern m00 = match( tokens[0], 0 );
+                            Pattern m223 = match( tokens[2], 2, 3 );
+                            return and( pattern, m00, m223 );
+                        }
+                    }
+                    else
+                    {
+                        if ( tokens[2] == ANY )
+                        {
+                            // [pat0]:[pat1]:*
+                            Pattern m00 = match( tokens[0], 0 );
+                            Pattern m11 = match( tokens[1], 1 );
+                            return and( pattern, m00, m11 );
+                        }
+                        else
+                        {
+                            // [pat0]:[pat1]:[pat2]
+                            Pattern m00 = match( tokens[0], 0 );
+                            Pattern m11 = match( tokens[1], 1 );
+                            Pattern m22 = match( tokens[2], 2 );
+                            return and( pattern, m00, m11, m22 );
+                        }
+                    }
+                }
             }
-
-            if ( regionMatch && value.contains( pattern ) )
+            if ( tokens.length == 4 )
             {
-                patternsTriggered.add( pattern );
-                return true;
+                List<Pattern> patterns = new ArrayList<>();
+                for ( int i = 0; i < 4; i++ )
+                {
+                    if ( tokens[i] != ANY )
+                    {
+                        patterns.add( match( tokens[i], i ) );
+                    }
+                }
+                return and( pattern, patterns.toArray( new Pattern[0] ) );
             }
-
+            throw new IllegalStateException();
         }
-        return false;
-
     }
 
-    private boolean isFirstPatternWildcard( String[] patternTokens )
+    private static Pattern match( String pattern, char[] token, int posVal )
     {
-        return patternTokens.length > 0 && "*".equals( patternTokens[0] );
+        return match( pattern, token, posVal, posVal );
     }
 
-    /**
-     * 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( final String token, final String pattern )
+    private static Pattern match( char[] token, int posVal )
     {
-        boolean matches;
+        return match( "", token, posVal, posVal );
+    }
 
-        // support full wildcard and implied wildcard
-        if ( "*".equals( pattern ) || pattern.length() == 0 )
+    private static Pattern match( String pattern, char[] token, int posMin, int posMax )
+    {
+        boolean hasWildcard = false;
+        for ( char ch : token )
         {
-            matches = true;
+            if ( ch == '*' || ch == '?' )
+            {
+                hasWildcard = true;
+                break;
+            }
         }
-        // support contains wildcard
-        else if ( pattern.startsWith( "*" ) && pattern.endsWith( "*" ) )
+        if ( hasWildcard || posMax == 3 )
         {
-            final String contains = pattern.substring( 1, pattern.length() - 1 );
-
-            matches = token.contains( contains );
+            return new PosPattern( pattern, token, posMin, posMax );
         }
-        // support leading wildcard
-        else if ( pattern.startsWith( "*" ) )
+        else
         {
-            final String suffix = pattern.substring( 1 );
-
-            matches = token.endsWith( suffix );
+            return new EqPattern( pattern, token, posMin, posMax );
         }
-        // support trailing wildcard
-        else if ( pattern.endsWith( "*" ) )
-        {
-            final String prefix = pattern.substring( 0, pattern.length() - 1 );
+    }
 
-            matches = token.startsWith( prefix );
-        }
-        // support wildcards in the middle of a pattern segment
-        else if ( pattern.indexOf( '*' ) > -1 )
-        {
-            String[] parts = pattern.split( "\\*" );
-            int lastPartEnd = -1;
-            boolean match = true;
+    private static Pattern match( char[] token, int posMin, int posMax )
+    {
+        return new PosPattern( "", token, posMin, posMax );
+    }
 
-            for ( String part : parts )
-            {
-                int idx = token.indexOf( part );
-                if ( idx <= lastPartEnd )
-                {
-                    match = false;
-                    break;
-                }
+    private static Pattern and( String pattern, Pattern... patterns )
+    {
+        return new AndPattern( pattern, patterns );
+    }
 
-                lastPartEnd = idx + part.length();
-            }
+    private static Pattern and( Pattern... patterns )
+    {
+        return and( "", patterns );
+    }
 
-            matches = match;
+    private static Pattern or( String pattern, Pattern... patterns )
+    {
+        return new OrPattern( pattern, patterns );
+    }
+
+    private static Pattern or( Pattern... patterns )
+    {
+        return or( "", patterns );
+    }
+
+    private static Pattern all( String pattern )
+    {
+        return new MatchAllPattern( pattern );
+    }
+
+    static abstract class Pattern
+    {
+        private final String pattern;
+
+        public Pattern( String pattern )
+        {
+            this.pattern = Objects.requireNonNull( pattern );
         }
-        // support versions range
-        else if ( pattern.startsWith( "[" ) || pattern.startsWith( "(" ) )
+
+        public abstract boolean matches( char[][] parts );
+
+        public String translateEquals()
         {
-            matches = isVersionIncludedInRange( token, pattern );
+            return null;
         }
-        // support exact match
-        else
+
+        public String translateEquals( int pos )
         {
-            matches = token.equals( pattern );
+            return null;
         }
 
-        return matches;
+        @Override
+        public String toString() {
+            return pattern;
+        }
     }
 
-    private boolean isVersionIncludedInRange( final String version, final String range )
+    static class AndPattern extends Pattern
     {
-        try
+        private final Pattern[] patterns;
+
+        public AndPattern( String pattern, Pattern[] patterns )
         {
-            return VersionRange.createFromVersionSpec( range ).containsVersion( new DefaultArtifactVersion( version ) );
+            super( pattern );
+            this.patterns = patterns;
         }
-        catch ( final InvalidVersionSpecificationException e )
+
+        @Override
+        public boolean matches( char[][] parts )
         {
-            return false;
+            for ( Pattern pattern : patterns )
+            {
+                if ( !pattern.matches( parts ) )
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        public String translateEquals()
+        {
+            String[] strings = new String[ patterns.length ];
+            for ( int i = 0; i < patterns.length; i++ )
+            {
+                strings[i] = patterns[i].translateEquals( i );
+                if ( strings[i] == null )
+                {
+                    return null;
+                }
+            }
+            StringBuilder sb = new StringBuilder();
+            for ( int i = 0; i < strings.length; i++ )
+            {
+                if ( i > 0 )
+                {
+                    sb.append( ":" );
+                }
+                sb.append( strings[i] );
+            }
+            return sb.toString();
         }
     }
 
-    /** {@inheritDoc} */
-    public void reportMissedCriteria( final Logger logger )
+    static class OrPattern extends Pattern
     {
-        // if there are no patterns, there is nothing to report.
-        if ( !positivePatterns.isEmpty() || !negativePatterns.isEmpty()
-                || !simplePositivePatterns.isEmpty() || !simpleNegativePatterns.isEmpty() )
-        {
-            final List<String> missed = new ArrayList<>();
-            missed.addAll( positivePatterns );
-            missed.addAll( simplePositivePatterns );
-            missed.addAll( negativePatterns );
-            missed.addAll( simpleNegativePatterns );
+        private final Pattern[] patterns;
 
-            missed.removeAll( patternsTriggered );
+        public OrPattern( String pattern, Pattern[] patterns )
+        {
+            super( pattern );
+            this.patterns = patterns;
+        }
 
-            if ( !missed.isEmpty() && logger.isWarnEnabled() )
+        @Override
+        public boolean matches( char[][] parts )
+        {
+            for ( Pattern pattern : patterns )
             {
-                final StringBuilder buffer = new StringBuilder();
-
-                buffer.append( "The following patterns were never triggered in this " );
-                buffer.append( getFilterDescription() );
-                buffer.append( ':' );
-
-                for ( String pattern : missed )
+                if ( pattern.matches( parts ) )
                 {
-                    buffer.append( "\no  '" ).append( pattern ).append( "'" );
+                    return true;
                 }
-
-                buffer.append( "\n" );
-
-                logger.warn( buffer.toString() );
             }
+            return false;
         }
     }
 
-    @Override
-    public String toString()
+    static class PosPattern extends Pattern
     {
-        return "Includes filter:" + getPatternsAsString();
-    }
+        private final char[] token;
+        private final int posMin;
+        private final int posMax;
 
-    /**
-     * @return pattern as a string.
-     */
-    protected String getPatternsAsString()
-    {
-        final StringBuilder buffer = new StringBuilder();
-        for ( String pattern : simplePositivePatterns )
+        public PosPattern( String pattern, char[] token, int posMin, int posMax )
         {
-            buffer.append( "\no '" ).append( pattern ).append( "'" );
+            super( pattern );
+            this.token = token;
+            this.posMin = posMin;
+            this.posMax = posMax;
         }
-        for ( String pattern : positivePatterns )
+
+        @Override
+        public boolean matches( char[][] parts )
         {
-            buffer.append( "\no '" ).append( pattern ).append( "'" );
+            for ( int i = posMin; i <= posMax; i++ )
+            {
+                if ( match( token, parts[i], i == 3 ) )
+                {
+                    return true;
+                }
+            }
+            return false;
         }
-
-        return buffer.toString();
     }
 
-    /**
-     * @return description.
-     */
-    protected String getFilterDescription()
+    static class EqPattern extends Pattern
     {
-        return "artifact inclusion filter";
-    }
+        private final char[] token;
+        private final int posMin;
+        private final int posMax;
 
-    /** {@inheritDoc} */
-    public void reportFilteredArtifacts( final Logger logger )
-    {
-        if ( !filteredArtifactIds.isEmpty() && logger.isDebugEnabled() )
+        public EqPattern( String pattern, char[] token, int posMin, int posMax )
         {
-            final StringBuilder buffer =
-                new StringBuilder( "The following artifacts were removed by this " + getFilterDescription() + ": " );
+            super( pattern );
+            this.token = token;
+            this.posMin = posMin;
+            this.posMax = posMax;
+        }
 
-            for ( String artifactId : filteredArtifactIds )
+        @Override
+        public boolean matches( char[][] parts )
+        {
+            for ( int i = posMin; i <= posMax; i++ )
             {
-                buffer.append( '\n' ).append( artifactId );
+                if (Arrays.equals( token, parts[i] ) )
+                {
+                    return true;
+                }
             }
+            return false;
+        }
 
-            logger.debug( buffer.toString() );
+        public String translateEquals( int pos )
+        {
+            return posMin == pos && posMax == pos
+                && ( pos < 3 || ( token[0] != '[' && token[0] != '(' ) )
+                    ? String.valueOf( token ) : null;
         }
+
     }
 
-    /** {@inheritDoc} */
-    public boolean hasMissedCriteria()
+    static class MatchAllPattern extends Pattern
     {
-        // if there are no patterns, there is nothing to report.
-        if ( !positivePatterns.isEmpty() || !negativePatterns.isEmpty() )
+        public MatchAllPattern( String pattern )
+        {
+            super( pattern );
+        }
+
+        @Override
+        public boolean matches( char[][] parts )
         {
-            final List<String> missed = new ArrayList<>();
-            missed.addAll( positivePatterns );
-            missed.addAll( negativePatterns );
+            return true;
+        }
+    }
 
-            missed.removeAll( patternsTriggered );
+    static class NegativePattern extends Pattern
+    {
+        private final Pattern inner;
 
-            return !missed.isEmpty();
+        NegativePattern( String pattern, Pattern inner )
+        {
+            super( pattern );
+            this.inner = inner;
         }
 
-        return false;
+        @Override
+        public boolean matches( char[][] parts )
+        {
+            return inner.matches( parts );
+        }
     }
 
 }
diff --git a/src/test/java/org/apache/maven/shared/artifact/filter/AbstractPatternArtifactFilterTest.java b/src/test/java/org/apache/maven/shared/artifact/filter/AbstractPatternArtifactFilterTest.java
index bbd46f0..cb4ad8d 100644
--- a/src/test/java/org/apache/maven/shared/artifact/filter/AbstractPatternArtifactFilterTest.java
+++ b/src/test/java/org/apache/maven/shared/artifact/filter/AbstractPatternArtifactFilterTest.java
@@ -52,16 +52,16 @@ public abstract class AbstractPatternArtifactFilterTest
         final String artifactId2 = "artifact2";
 
         Artifact artifact1 = mock( Artifact.class );
-        when( artifact1.getDependencyConflictId() ).thenReturn( groupId1 + ":" + artifactId1 + ":jar" );
         when( artifact1.getGroupId() ).thenReturn( groupId1 );
         when( artifact1.getArtifactId() ).thenReturn( artifactId1 );
-        when( artifact1.getId() ).thenReturn( groupId1 + ":" + artifactId1 + ":jar:version" );
+        when( artifact1.getType() ).thenReturn( "jar" );
+        when( artifact1.getBaseVersion() ).thenReturn( "version" );
         
         Artifact artifact2 = mock( Artifact.class );
-        when( artifact2.getDependencyConflictId() ).thenReturn( groupId2 + ":" + artifactId2 + ":jar" );
         when( artifact2.getGroupId() ).thenReturn( groupId2 );
         when( artifact2.getArtifactId() ).thenReturn( artifactId2 );
-        when( artifact2.getId() ).thenReturn( groupId2 + ":" + artifactId2 + ":jar:version" );
+        when( artifact2.getType() ).thenReturn( "jar" );
+        when( artifact2.getBaseVersion() ).thenReturn( "version" );
 
         final List<String> patterns = new ArrayList<>();
         patterns.add( groupId1 + ":" + artifactId1 + ":*" );
@@ -91,16 +91,16 @@ public abstract class AbstractPatternArtifactFilterTest
         final String artifactId2 = "artifact2";
 
         Artifact artifact1 = mock( Artifact.class );
-        when( artifact1.getDependencyConflictId() ).thenReturn( groupId1 + ":" + artifactId1 + ":jar" );
         when( artifact1.getGroupId() ).thenReturn( groupId1 );
         when( artifact1.getArtifactId() ).thenReturn( artifactId1 );
-        when( artifact1.getId() ).thenReturn( groupId1 + ":" + artifactId1 + ":jar:version" );
+        when( artifact1.getType() ).thenReturn( "jar" );
+        when( artifact1.getBaseVersion() ).thenReturn( "version" );
 
         Artifact artifact2 = mock( Artifact.class );
-        when( artifact2.getDependencyConflictId() ).thenReturn( groupId2 + ":" + artifactId2 + ":jar" );
         when( artifact2.getGroupId() ).thenReturn( groupId2 );
         when( artifact2.getArtifactId() ).thenReturn( artifactId2 );
-        when( artifact2.getId() ).thenReturn( groupId2 + ":" + artifactId2 + ":jar:version" );
+        when( artifact2.getType() ).thenReturn( "jar" );
+        when( artifact2.getBaseVersion() ).thenReturn( "version" );
 
         final List<String> patterns = new ArrayList<>();
         patterns.add( groupId1 + "*" );
@@ -127,10 +127,10 @@ public abstract class AbstractPatternArtifactFilterTest
         final String artifactId = "artifact";
 
         Artifact artifact = mock( Artifact.class );
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
 
         final ArtifactFilter filter = createFilter( Collections.singletonList( groupId + ":" + artifactId ) );
 
@@ -151,10 +151,10 @@ public abstract class AbstractPatternArtifactFilterTest
         final String artifactId = "artifact";
 
         Artifact artifact = mock( Artifact.class );
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
 
         final ArtifactFilter filter = createFilter( Collections.singletonList( groupId + ":" + artifactId + ":jar" ) );
 
@@ -175,10 +175,10 @@ public abstract class AbstractPatternArtifactFilterTest
         final String artifactId = "artifact";
 
         Artifact artifact = mock( Artifact.class );
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
 
         final List<String> patterns = new ArrayList<>();
         patterns.add( "otherGroup:" + artifactId + ":jar" );
@@ -203,10 +203,10 @@ public abstract class AbstractPatternArtifactFilterTest
         final String artifactId = "artifact";
 
         Artifact artifact = mock( Artifact.class );
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
 
         final List<String> patterns = new ArrayList<>();
         patterns.add( groupId + "otherArtifact:jar" );
@@ -231,10 +231,10 @@ public abstract class AbstractPatternArtifactFilterTest
         final String artifactId = "artifact";
 
         Artifact artifact = mock( Artifact.class );
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
 
         final List<String> patterns = new ArrayList<>();
         patterns.add( "otherGroup:otherArtifact:jar" );
@@ -265,10 +265,10 @@ public abstract class AbstractPatternArtifactFilterTest
         final List<String> patterns = Collections.singletonList( depTrailItem );
 
         Artifact artifact = mock( Artifact.class );
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
         when( artifact.getDependencyTrail() ).thenReturn( depTrail );
 
         final ArtifactFilter filter = createFilter( patterns, true );
@@ -296,10 +296,10 @@ public abstract class AbstractPatternArtifactFilterTest
         final List<String> patterns = Collections.singletonList( "otherGroup*" );
 
         Artifact artifact = mock( Artifact.class );
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
         when( artifact.getDependencyTrail() ).thenReturn( depTrail );
 
         final ArtifactFilter filter = createFilter( patterns, true );
@@ -321,10 +321,10 @@ public abstract class AbstractPatternArtifactFilterTest
         final String artifactId = "artifact";
 
         Artifact artifact = mock( Artifact.class );
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
 
         final List<String> patterns = new ArrayList<>();
         patterns.add( "!group:artifact:jar" );
@@ -348,10 +348,10 @@ public abstract class AbstractPatternArtifactFilterTest
         final String artifactId = "artifact";
 
         Artifact artifact = mock( Artifact.class );
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
 
         final List<String> patterns = new ArrayList<>();
         patterns.add( "group:*:jar" );
@@ -376,10 +376,10 @@ public abstract class AbstractPatternArtifactFilterTest
 
         Artifact artifact = mock( Artifact.class );
 
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
 
         final List<String> patterns = new ArrayList<>();
         patterns.add( "*:artifact:*" );
@@ -404,10 +404,10 @@ public abstract class AbstractPatternArtifactFilterTest
 
         Artifact artifact = mock( Artifact.class );
 
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
 
         final List<String> patterns = new ArrayList<>();
         patterns.add( "group:some-*-id" );
@@ -432,10 +432,10 @@ public abstract class AbstractPatternArtifactFilterTest
 
         Artifact artifact = mock( Artifact.class );
 
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
 
         final List<String> patterns = new ArrayList<>();
         patterns.add( "some.group*" );
@@ -465,16 +465,16 @@ public abstract class AbstractPatternArtifactFilterTest
         final List<String> patterns = Collections.singletonList( "*:jar:*" );
 
         Artifact artifact1 = mock( Artifact.class );
-        when( artifact1.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact1.getGroupId() ).thenReturn( groupId );
         when( artifact1.getArtifactId() ).thenReturn( artifactId );
-        when( artifact1.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
-        
+        when( artifact1.getType() ).thenReturn( "jar" );
+        when( artifact1.getBaseVersion() ).thenReturn( "version" );
+
         Artifact artifact2 = mock( Artifact.class );
-        when( artifact2.getDependencyConflictId() ).thenReturn( otherGroup + ":" + otherArtifact + ":" + otherType );
         when( artifact2.getGroupId() ).thenReturn( otherGroup );
         when( artifact2.getArtifactId() ).thenReturn( otherArtifact );
-        when( artifact2.getId() ).thenReturn( otherGroup + ":" + otherArtifact + ":" + otherType + ":version" );
+        when( artifact2.getType() ).thenReturn( otherType );
+        when( artifact2.getBaseVersion() ).thenReturn( "version" );
         when( artifact2.getDependencyTrail() ).thenReturn( Collections.<String> emptyList() );
 
         final ArtifactFilter filter = createFilter( patterns, true );
@@ -498,10 +498,10 @@ public abstract class AbstractPatternArtifactFilterTest
         final String artifactId = "some-artifact-id";
 
         Artifact artifact = mock( Artifact.class );
-        when( artifact.getDependencyConflictId() ).thenReturn( groupId + ":" + artifactId + ":jar" );
         when( artifact.getGroupId() ).thenReturn( groupId );
         when( artifact.getArtifactId() ).thenReturn( artifactId );
-        when( artifact.getId() ).thenReturn( groupId + ":" + artifactId + ":jar:version" );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "version" );
 
         final List<String> patterns = new ArrayList<>();
         patterns.add( "com.mycompany.*:*:jar:*:*" );
@@ -517,4 +517,31 @@ public abstract class AbstractPatternArtifactFilterTest
             assertTrue( filter.include( artifact ) );
         }
     }
+
+    @Test
+    public void testWithVersionRange()
+    {
+        final String groupId = "com.mycompany.myproject";
+        final String artifactId = "some-artifact-id";
+
+        Artifact artifact = mock( Artifact.class );
+        when( artifact.getGroupId() ).thenReturn( groupId );
+        when( artifact.getArtifactId() ).thenReturn( artifactId );
+        when( artifact.getType() ).thenReturn( "jar" );
+        when( artifact.getBaseVersion() ).thenReturn( "1.1" );
+
+        final List<String> patterns = new ArrayList<>();
+        patterns.add( "com.mycompany.myproject:some-artifact-id:jar:*:[1.0,2.0)" );
+
+        final ArtifactFilter filter = createFilter( patterns );
+
+        if ( isInclusionNotExpected() )
+        {
+            assertFalse( filter.include( artifact ) );
+        }
+        else
+        {
+            assertTrue( filter.include( artifact ) );
+        }
+    }
 }
diff --git a/src/main/java/org/apache/maven/shared/artifact/filter/PatternIncludesArtifactFilter.java b/src/test/java/org/apache/maven/shared/artifact/filter/OldPatternIncludesArtifactFilter.java
similarity index 78%
copy from src/main/java/org/apache/maven/shared/artifact/filter/PatternIncludesArtifactFilter.java
copy to src/test/java/org/apache/maven/shared/artifact/filter/OldPatternIncludesArtifactFilter.java
index 8d01c71..df492bb 100644
--- a/src/main/java/org/apache/maven/shared/artifact/filter/PatternIncludesArtifactFilter.java
+++ b/src/test/java/org/apache/maven/shared/artifact/filter/OldPatternIncludesArtifactFilter.java
@@ -22,7 +22,6 @@ package org.apache.maven.shared.artifact.filter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -36,20 +35,16 @@ import org.codehaus.plexus.logging.Logger;
 
 /**
  * TODO: include in maven-artifact in future
- * 
+ *
  * @author <a href="mailto:brett@apache.org">Brett Porter</a>
  * @see StrictPatternIncludesArtifactFilter
  */
-public class PatternIncludesArtifactFilter
-    implements ArtifactFilter, StatisticsReportingArtifactFilter
+public class OldPatternIncludesArtifactFilter
+        implements ArtifactFilter, StatisticsReportingArtifactFilter
 {
-    private final Set<String> simplePositivePatterns;
-
-    private final Set<String> positivePatterns;
-
-    private final Set<String> simpleNegativePatterns;
+    private final List<String> positivePatterns;
 
-    private final Set<String> negativePatterns;
+    private final List<String> negativePatterns;
 
     private final boolean actTransitively;
 
@@ -60,7 +55,7 @@ public class PatternIncludesArtifactFilter
     /**
      * @param patterns The pattern to be used.
      */
-    public PatternIncludesArtifactFilter( final Collection<String> patterns )
+    public OldPatternIncludesArtifactFilter( final Collection<String> patterns )
     {
         this( patterns, false );
     }
@@ -69,46 +64,28 @@ public class PatternIncludesArtifactFilter
      * @param patterns The pattern to be used.
      * @param actTransitively transitive yes/no.
      */
-    public PatternIncludesArtifactFilter( final Collection<String> patterns, final boolean actTransitively )
+    public OldPatternIncludesArtifactFilter( final Collection<String> patterns, final boolean actTransitively )
     {
         this.actTransitively = actTransitively;
-        final Set<String> pos = new LinkedHashSet<>();
-        final Set<String> spos = new LinkedHashSet<>();
-        final Set<String> neg = new LinkedHashSet<>();
-        final Set<String> sneg = new LinkedHashSet<>();
+        final List<String> pos = new ArrayList<>();
+        final List<String> neg = new ArrayList<>();
         if ( patterns != null && !patterns.isEmpty() )
         {
             for ( String pattern : patterns )
             {
                 if ( pattern.startsWith( "!" ) )
                 {
-                    if ( pattern.contains( "*" ) )
-                    {
-                        neg.add( pattern.substring( 1 ) );
-                    }
-                    else
-                    {
-                        sneg.add( pattern.substring( 1 ) );
-                    }
+                    neg.add( pattern.substring( 1 ) );
                 }
                 else
                 {
-                    if ( pattern.contains( "*" ) )
-                    {
-                        pos.add( pattern );
-                    }
-                    else
-                    {
-                        spos.add( pattern );
-                    }
+                    pos.add( pattern );
                 }
             }
         }
 
         positivePatterns = pos;
-        simplePositivePatterns = spos;
         negativePatterns = neg;
-        simpleNegativePatterns = sneg;
     }
 
     /** {@inheritDoc} */
@@ -143,13 +120,6 @@ public class PatternIncludesArtifactFilter
 
     private Boolean negativeMatch( final Artifact artifact )
     {
-        if ( simpleNegativePatterns != null && !simpleNegativePatterns.isEmpty() )
-        {
-            if ( simpleMatch( artifact, simpleNegativePatterns ) )
-            {
-                return true;
-            }
-        }
         if ( negativePatterns == null || negativePatterns.isEmpty() )
         {
             return null;
@@ -166,13 +136,6 @@ public class PatternIncludesArtifactFilter
      */
     protected Boolean positiveMatch( final Artifact artifact )
     {
-        if ( simplePositivePatterns != null && !simplePositivePatterns.isEmpty() )
-        {
-            if ( simpleMatch( artifact, simplePositivePatterns ) )
-            {
-                return true;
-            }
-        }
         if ( positivePatterns == null || positivePatterns.isEmpty() )
         {
             return null;
@@ -183,33 +146,7 @@ public class PatternIncludesArtifactFilter
         }
     }
 
-    private boolean simpleMatch( final Artifact artifact, final Set<String> patterns )
-    {
-        final String shortId = ArtifactUtils.versionlessKey( artifact );
-        if ( patterns.contains( shortId ) )
-        {
-            patternsTriggered.add( shortId );
-            return true;
-        }
-
-        final String id = artifact.getDependencyConflictId();
-        if ( patterns.contains( id ) )
-        {
-            patternsTriggered.add( id );
-            return true;
-        }
-
-        final String wholeId = artifact.getId();
-        if ( patterns.contains( wholeId ) )
-        {
-            patternsTriggered.add( wholeId );
-            return true;
-        }
-
-        return false;
-    }
-
-    private boolean match( final Artifact artifact, final Iterable<String> patterns )
+    private boolean match( final Artifact artifact, final List<String> patterns )
     {
         final String shortId = ArtifactUtils.versionlessKey( artifact );
         final String id = artifact.getDependencyConflictId();
@@ -249,13 +186,13 @@ public class PatternIncludesArtifactFilter
         return false;
     }
 
-    private boolean matchAgainst( final String value, final Iterable<String> patterns, final boolean regionMatch )
+    private boolean matchAgainst( final String value, final List<String> patterns, final boolean regionMatch )
     {
         final String[] tokens = value.split( ":" );
         for ( String pattern : patterns )
         {
             String[] patternTokens = pattern.split( ":" );
-            
+
             if ( patternTokens.length == 5 && tokens.length < 5 )
             {
                 // 4th element is the classifier
@@ -311,7 +248,7 @@ public class PatternIncludesArtifactFilter
 
     /**
      * 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
@@ -397,14 +334,11 @@ public class PatternIncludesArtifactFilter
     public void reportMissedCriteria( final Logger logger )
     {
         // if there are no patterns, there is nothing to report.
-        if ( !positivePatterns.isEmpty() || !negativePatterns.isEmpty()
-                || !simplePositivePatterns.isEmpty() || !simpleNegativePatterns.isEmpty() )
+        if ( !positivePatterns.isEmpty() || !negativePatterns.isEmpty() )
         {
             final List<String> missed = new ArrayList<>();
             missed.addAll( positivePatterns );
-            missed.addAll( simplePositivePatterns );
             missed.addAll( negativePatterns );
-            missed.addAll( simpleNegativePatterns );
 
             missed.removeAll( patternsTriggered );
 
@@ -440,10 +374,6 @@ public class PatternIncludesArtifactFilter
     protected String getPatternsAsString()
     {
         final StringBuilder buffer = new StringBuilder();
-        for ( String pattern : simplePositivePatterns )
-        {
-            buffer.append( "\no '" ).append( pattern ).append( "'" );
-        }
         for ( String pattern : positivePatterns )
         {
             buffer.append( "\no '" ).append( pattern ).append( "'" );
@@ -466,7 +396,7 @@ public class PatternIncludesArtifactFilter
         if ( !filteredArtifactIds.isEmpty() && logger.isDebugEnabled() )
         {
             final StringBuilder buffer =
-                new StringBuilder( "The following artifacts were removed by this " + getFilterDescription() + ": " );
+                    new StringBuilder( "The following artifacts were removed by this " + getFilterDescription() + ": " );
 
             for ( String artifactId : filteredArtifactIds )
             {
@@ -495,4 +425,4 @@ public class PatternIncludesArtifactFilter
         return false;
     }
 
-}
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/maven/shared/artifact/filter/PatternFilterPerfTest.java b/src/test/java/org/apache/maven/shared/artifact/filter/PatternFilterPerfTest.java
new file mode 100644
index 0000000..159d979
--- /dev/null
+++ b/src/test/java/org/apache/maven/shared/artifact/filter/PatternFilterPerfTest.java
@@ -0,0 +1,122 @@
+package org.apache.maven.shared.artifact.filter;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.junit.Test;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.openjdk.jmh.runner.options.TimeValue;
+
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS)
+public class PatternFilterPerfTest {
+
+    @State(Scope.Benchmark)
+    static public class OldPatternState {
+
+        @Param({
+                "groupId:artifact-00,groupId:artifact-01,groupId:artifact-02,groupId:artifact-03,groupId:artifact-04,groupId:artifact-05,groupId:artifact-06,groupId:artifact-07,groupId:artifact-08,groupId:artifact-09",
+                "groupId:artifact-99",
+                "groupId:artifact-*",
+                "*:artifact-99",
+                "*:artifact-*",
+                "*:artifact-*:*",
+                "*:artifact-99:*",
+        })
+        public String patterns;
+
+        ArtifactFilter filter;
+        Artifact artifact;
+
+        @Setup(Level.Invocation)
+        public void setup()
+        {
+            filter = new OldPatternIncludesArtifactFilter( Arrays.asList( patterns.split( "," ) ) );
+            artifact = new DefaultArtifact(
+                    "groupId", "artifact-99", "1.0", "runtime",
+                    "jar", "", null
+            );
+        }
+
+    }
+
+    @State(Scope.Benchmark)
+    static public class NewPatternState {
+
+        @Param({
+                "groupId:artifact-00,groupId:artifact-01,groupId:artifact-02,groupId:artifact-03,groupId:artifact-04,groupId:artifact-05,groupId:artifact-06,groupId:artifact-07,groupId:artifact-08,groupId:artifact-09",
+                "groupId:artifact-99",
+                "groupId:artifact-*",
+                "*:artifact-99",
+                "*:artifact-*",
+                "*:artifact-*:*",
+                "*:artifact-99:*",
+        })
+        public String patterns;
+
+        ArtifactFilter filter;
+        Artifact artifact;
+
+        @Setup(Level.Invocation)
+        public void setup()
+        {
+            filter = new PatternIncludesArtifactFilter( Arrays.asList( patterns.split( "," ) ) );
+            artifact = new DefaultArtifact(
+                    "groupId", "artifact-99", "1.0", "runtime",
+                    "jar", "", null
+            );
+        }
+
+    }
+
+
+    @Benchmark
+    public boolean newPatternTest(NewPatternState state )
+    {
+        return state.filter.include( state.artifact );
+    }
+
+    @Benchmark
+    public boolean oldPatternTest(OldPatternState state )
+    {
+        return state.filter.include( state.artifact );
+    }
+
+    public static void main( String... args )
+            throws RunnerException
+    {
+        Options opts = new OptionsBuilder()
+                .measurementIterations( 3 )
+                .measurementTime( TimeValue.milliseconds( 3000 ) )
+                .forks( 1 )
+                .include( "org.apache.maven.shared.artifact.filter.PatternFilterPerfTest" )
+                .build();
+        new Runner( opts ).run();
+    }
+}