Coverage Report - org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter
 
Classes in this File Line Coverage Branch Coverage Complexity
PatternIncludesArtifactFilter
60%
85/141
59%
67/112
4.706
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.maven.shared.artifact.filter;
 20  
 
 21  
 import java.util.ArrayList;
 22  
 import java.util.HashSet;
 23  
 import java.util.Iterator;
 24  
 import java.util.List;
 25  
 import java.util.Set;
 26  
 
 27  
 import org.apache.maven.artifact.Artifact;
 28  
 import org.apache.maven.artifact.ArtifactUtils;
 29  
 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
 30  
 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
 31  
 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
 32  
 import org.apache.maven.artifact.versioning.VersionRange;
 33  
 import org.codehaus.plexus.logging.Logger;
 34  
 
 35  
 /**
 36  
  * TODO: include in maven-artifact in future
 37  
  * 
 38  
  * @author <a href="mailto:brett@apache.org">Brett Porter</a>
 39  
  * @see StrictPatternIncludesArtifactFilter
 40  
  */
 41  
 public class PatternIncludesArtifactFilter
 42  
     implements ArtifactFilter, StatisticsReportingArtifactFilter
 43  
 {
 44  
     private final List positivePatterns;
 45  
 
 46  
     private final List negativePatterns;
 47  
 
 48  
     private final boolean actTransitively;
 49  
 
 50  38
     private final Set patternsTriggered = new HashSet();
 51  
 
 52  38
     private final List filteredArtifactIds = new ArrayList();
 53  
 
 54  
     public PatternIncludesArtifactFilter( final List patterns )
 55  
     {
 56  31
         this( patterns, false );
 57  31
     }
 58  
 
 59  
     public PatternIncludesArtifactFilter( final List patterns, final boolean actTransitively )
 60  38
     {
 61  38
         this.actTransitively = actTransitively;
 62  38
         final List pos = new ArrayList();
 63  38
         final List neg = new ArrayList();
 64  38
         if ( ( patterns != null ) && !patterns.isEmpty() )
 65  
         {
 66  38
             for ( final Iterator it = patterns.iterator(); it.hasNext(); )
 67  
             {
 68  51
                 final String pattern = (String) it.next();
 69  
 
 70  51
                 if ( pattern.startsWith( "!" ) )
 71  
                 {
 72  3
                     neg.add( pattern.substring( 1 ) );
 73  
                 }
 74  
                 else
 75  
                 {
 76  48
                     pos.add( pattern );
 77  
                 }
 78  51
             }
 79  
         }
 80  
 
 81  38
         positivePatterns = pos;
 82  38
         negativePatterns = neg;
 83  38
     }
 84  
 
 85  
     public boolean include( final Artifact artifact )
 86  
     {
 87  18
         final boolean shouldInclude = patternMatches( artifact );
 88  
 
 89  18
         if ( !shouldInclude )
 90  
         {
 91  5
             addFilteredArtifactId( artifact.getId() );
 92  
         }
 93  
 
 94  18
         return shouldInclude;
 95  
     }
 96  
 
 97  
     protected boolean patternMatches( final Artifact artifact )
 98  
     {
 99  44
         return ( positiveMatch( artifact ) == Boolean.TRUE ) || ( negativeMatch( artifact ) == Boolean.FALSE );
 100  
     }
 101  
 
 102  
     protected void addFilteredArtifactId( final String artifactId )
 103  
     {
 104  22
         filteredArtifactIds.add( artifactId );
 105  22
     }
 106  
 
 107  
     private Boolean negativeMatch( final Artifact artifact )
 108  
     {
 109  14
         if ( ( negativePatterns == null ) || negativePatterns.isEmpty() )
 110  
         {
 111  11
             return null;
 112  
         }
 113  
         else
 114  
         {
 115  3
             return Boolean.valueOf( match( artifact, negativePatterns ) );
 116  
         }
 117  
     }
 118  
 
 119  
     protected Boolean positiveMatch( final Artifact artifact )
 120  
     {
 121  44
         if ( ( positivePatterns == null ) || positivePatterns.isEmpty() )
 122  
         {
 123  3
             return null;
 124  
         }
 125  
         else
 126  
         {
 127  41
             return Boolean.valueOf( match( artifact, positivePatterns ) );
 128  
         }
 129  
     }
 130  
 
 131  
     private boolean match( final Artifact artifact, final List patterns )
 132  
     {
 133  44
         final String shortId = ArtifactUtils.versionlessKey( artifact );
 134  44
         final String id = artifact.getDependencyConflictId();
 135  44
         final String wholeId = artifact.getId();
 136  
 
 137  44
         if ( matchAgainst( wholeId, patterns, false ) )
 138  
         {
 139  28
             return true;
 140  
         }
 141  
 
 142  16
         if ( matchAgainst( id, patterns, false ) )
 143  
         {
 144  0
             return true;
 145  
         }
 146  
 
 147  16
         if ( matchAgainst( shortId, patterns, false ) )
 148  
         {
 149  0
             return true;
 150  
         }
 151  
 
 152  16
         if ( actTransitively )
 153  
         {
 154  7
             final List depTrail = artifact.getDependencyTrail();
 155  
 
 156  7
             if ( ( depTrail != null ) && depTrail.size() > 1 )
 157  
             {
 158  5
                 for ( final Iterator iterator = depTrail.iterator(); iterator.hasNext(); )
 159  
                 {
 160  10
                     final String trailItem = (String) iterator.next();
 161  10
                     if ( matchAgainst( trailItem, patterns, true ) )
 162  
                     {
 163  5
                         return true;
 164  
                     }
 165  5
                 }
 166  
             }
 167  
         }
 168  
 
 169  11
         return false;
 170  
     }
 171  
 
 172  
     private boolean matchAgainst( final String value, final List patterns, final boolean regionMatch )
 173  
     {
 174  86
         for ( final Iterator iterator = patterns.iterator(); iterator.hasNext(); )
 175  
         {
 176  115
             final String pattern = (String) iterator.next();
 177  
 
 178  115
             final String[] patternTokens = pattern.split( ":" );
 179  115
             final String[] tokens = value.split( ":" );
 180  
 
 181  
             // fail immediately if pattern tokens outnumber tokens to match
 182  115
             boolean matched = ( patternTokens.length <= tokens.length );
 183  
 
 184  266
             for ( int i = 0; matched && i < patternTokens.length; i++ )
 185  
             {
 186  151
                 matched = matches( tokens[i], patternTokens[i] );
 187  
             }
 188  
 
 189  
             // // case of starting '*' like '*:jar:*'
 190  115
             if ( !matched && patternTokens.length < tokens.length && patternTokens.length > 0
 191  
                 && "*".equals( patternTokens[0] ) )
 192  
             {
 193  4
                 matched = true;
 194  14
                 for ( int i = 0; matched && i < patternTokens.length; i++ )
 195  
                 {
 196  10
                     matched = matches( tokens[i + ( tokens.length - patternTokens.length )], patternTokens[i] );
 197  
                 }
 198  
             }
 199  
 
 200  115
             if ( matched )
 201  
             {
 202  33
                 patternsTriggered.add( pattern );
 203  33
                 return true;
 204  
             }
 205  
 
 206  82
             if ( regionMatch && value.indexOf( pattern ) > -1 )
 207  
             {
 208  0
                 patternsTriggered.add( pattern );
 209  0
                 return true;
 210  
             }
 211  
 
 212  82
         }
 213  53
         return false;
 214  
 
 215  
     }
 216  
 
 217  
     /**
 218  
      * Gets whether the specified token matches the specified pattern segment.
 219  
      * 
 220  
      * @param token the token to check
 221  
      * @param pattern the pattern segment to match, as defined above
 222  
      * @return <code>true</code> if the specified token is matched by the specified pattern segment
 223  
      */
 224  
     private boolean matches( final String token, final String pattern )
 225  
     {
 226  
         boolean matches;
 227  
 
 228  
         // support full wildcard and implied wildcard
 229  161
         if ( "*".equals( pattern ) || pattern.length() == 0 )
 230  
         {
 231  23
             matches = true;
 232  
         }
 233  
         // support contains wildcard
 234  138
         else if ( pattern.startsWith( "*" ) && pattern.endsWith( "*" ) )
 235  
         {
 236  0
             final String contains = pattern.substring( 1, pattern.length() - 1 );
 237  
 
 238  0
             matches = ( token.indexOf( contains ) != -1 );
 239  0
         }
 240  
         // support leading wildcard
 241  138
         else if ( pattern.startsWith( "*" ) )
 242  
         {
 243  0
             final String suffix = pattern.substring( 1, pattern.length() );
 244  
 
 245  0
             matches = token.endsWith( suffix );
 246  0
         }
 247  
         // support trailing wildcard
 248  138
         else if ( pattern.endsWith( "*" ) )
 249  
         {
 250  16
             final String prefix = pattern.substring( 0, pattern.length() - 1 );
 251  
 
 252  16
             matches = token.startsWith( prefix );
 253  16
         }
 254  
         // support wildcards in the middle of a pattern segment
 255  122
         else if ( pattern.indexOf( '*' ) > -1 )
 256  
         {
 257  2
             String[] parts = pattern.split( "\\*" );
 258  2
             int lastPartEnd = -1;
 259  2
             boolean match = true;
 260  
 
 261  6
             for ( String part : parts )
 262  
             {
 263  4
                 int idx = token.indexOf( part );
 264  4
                 if ( idx <= lastPartEnd )
 265  
                 {
 266  0
                     match = false;
 267  0
                     break;
 268  
                 }
 269  
 
 270  4
                 lastPartEnd = idx + part.length();
 271  
             }
 272  
 
 273  2
             matches = match;
 274  2
         }
 275  
         // support versions range
 276  120
         else if ( pattern.startsWith( "[" ) || pattern.startsWith( "(" ) )
 277  
         {
 278  0
             matches = isVersionIncludedInRange( token, pattern );
 279  
         }
 280  
         // support exact match
 281  
         else
 282  
         {
 283  120
             matches = token.equals( pattern );
 284  
         }
 285  
 
 286  161
         return matches;
 287  
     }
 288  
 
 289  
     private boolean isVersionIncludedInRange( final String version, final String range )
 290  
     {
 291  
         try
 292  
         {
 293  0
             return VersionRange.createFromVersionSpec( range ).containsVersion( new DefaultArtifactVersion( version ) );
 294  
         }
 295  0
         catch ( final InvalidVersionSpecificationException e )
 296  
         {
 297  0
             return false;
 298  
         }
 299  
     }
 300  
 
 301  
     public void reportMissedCriteria( final Logger logger )
 302  
     {
 303  
         // if there are no patterns, there is nothing to report.
 304  0
         if ( !positivePatterns.isEmpty() || !negativePatterns.isEmpty() )
 305  
         {
 306  0
             final List missed = new ArrayList();
 307  0
             missed.addAll( positivePatterns );
 308  0
             missed.addAll( negativePatterns );
 309  
 
 310  0
             missed.removeAll( patternsTriggered );
 311  
 
 312  0
             if ( !missed.isEmpty() && logger.isWarnEnabled() )
 313  
             {
 314  0
                 final StringBuffer buffer = new StringBuffer();
 315  
 
 316  0
                 buffer.append( "The following patterns were never triggered in this " );
 317  0
                 buffer.append( getFilterDescription() );
 318  0
                 buffer.append( ':' );
 319  
 
 320  0
                 for ( final Iterator it = missed.iterator(); it.hasNext(); )
 321  
                 {
 322  0
                     final String pattern = (String) it.next();
 323  
 
 324  0
                     buffer.append( "\no  \'" ).append( pattern ).append( "\'" );
 325  0
                 }
 326  
 
 327  0
                 buffer.append( "\n" );
 328  
 
 329  0
                 logger.warn( buffer.toString() );
 330  
             }
 331  
         }
 332  0
     }
 333  
 
 334  
     @Override
 335  
     public String toString()
 336  
     {
 337  0
         return "Includes filter:" + getPatternsAsString();
 338  
     }
 339  
 
 340  
     protected String getPatternsAsString()
 341  
     {
 342  0
         final StringBuffer buffer = new StringBuffer();
 343  0
         for ( final Iterator it = positivePatterns.iterator(); it.hasNext(); )
 344  
         {
 345  0
             final String pattern = (String) it.next();
 346  
 
 347  0
             buffer.append( "\no \'" ).append( pattern ).append( "\'" );
 348  0
         }
 349  
 
 350  0
         return buffer.toString();
 351  
     }
 352  
 
 353  
     protected String getFilterDescription()
 354  
     {
 355  0
         return "artifact inclusion filter";
 356  
     }
 357  
 
 358  
     public void reportFilteredArtifacts( final Logger logger )
 359  
     {
 360  0
         if ( !filteredArtifactIds.isEmpty() && logger.isDebugEnabled() )
 361  
         {
 362  0
             final StringBuffer buffer =
 363  
                 new StringBuffer( "The following artifacts were removed by this " + getFilterDescription() + ": " );
 364  
 
 365  0
             for ( final Iterator it = filteredArtifactIds.iterator(); it.hasNext(); )
 366  
             {
 367  0
                 final String artifactId = (String) it.next();
 368  
 
 369  0
                 buffer.append( '\n' ).append( artifactId );
 370  0
             }
 371  
 
 372  0
             logger.debug( buffer.toString() );
 373  
         }
 374  0
     }
 375  
 
 376  
     public boolean hasMissedCriteria()
 377  
     {
 378  
         // if there are no patterns, there is nothing to report.
 379  0
         if ( !positivePatterns.isEmpty() || !negativePatterns.isEmpty() )
 380  
         {
 381  0
             final List missed = new ArrayList();
 382  0
             missed.addAll( positivePatterns );
 383  0
             missed.addAll( negativePatterns );
 384  
 
 385  0
             missed.removeAll( patternsTriggered );
 386  
 
 387  0
             return !missed.isEmpty();
 388  
         }
 389  
 
 390  0
         return false;
 391  
     }
 392  
 
 393  
 }