Coverage Report - org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter
 
Classes in this File Line Coverage Branch Coverage Complexity
PatternIncludesArtifactFilter
58%
69/118
57%
58/102
3.353
 
 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  
 import org.codehaus.plexus.util.StringUtils;
 35  
 
 36  
 /**
 37  
  * TODO: include in maven-artifact in future
 38  
  *
 39  
  * @author <a href="mailto:brett@apache.org">Brett Porter</a>
 40  
  * @see StrictPatternIncludesArtifactFilter
 41  
  */
 42  
 public class PatternIncludesArtifactFilter
 43  
     implements ArtifactFilter, StatisticsReportingArtifactFilter
 44  
 {
 45  
     private final List positivePatterns;
 46  
 
 47  
     private final List negativePatterns;
 48  
 
 49  
     private final boolean actTransitively;
 50  
 
 51  132
     private Set patternsTriggered = new HashSet();
 52  
 
 53  132
     private List filteredArtifactIds = new ArrayList();
 54  
 
 55  
     public PatternIncludesArtifactFilter( List patterns )
 56  
     {
 57  108
         this( patterns, false );
 58  108
     }
 59  
 
 60  
     public PatternIncludesArtifactFilter( List patterns, boolean actTransitively )
 61  132
     {
 62  132
         this.actTransitively = actTransitively;
 63  132
         List pos = new ArrayList();
 64  132
         List neg = new ArrayList();
 65  132
         if ( ( patterns != null ) && !patterns.isEmpty() )
 66  
         {
 67  132
             for ( Iterator it = patterns.iterator(); it.hasNext(); )
 68  
             {
 69  180
                 String pattern = (String) it.next();
 70  
 
 71  180
                 if ( pattern.startsWith( "!" ) )
 72  
                 {
 73  12
                     neg.add( pattern.substring( 1 ) );
 74  
                 }
 75  
                 else
 76  
                 {
 77  168
                     pos.add( pattern );
 78  
                 }
 79  
             }
 80  
         }
 81  
 
 82  132
         positivePatterns = pos;
 83  132
         negativePatterns = neg;
 84  132
     }
 85  
 
 86  
     public boolean include( Artifact artifact )
 87  
     {
 88  78
         boolean shouldInclude = patternMatches( artifact );
 89  
 
 90  78
         if ( !shouldInclude )
 91  
         {
 92  30
             addFilteredArtifactId( artifact.getId() );
 93  
         }
 94  
 
 95  78
         return shouldInclude;
 96  
     }
 97  
 
 98  
     protected boolean patternMatches( Artifact artifact )
 99  
     {
 100  156
         return ( positiveMatch( artifact ) == Boolean.TRUE ) || ( negativeMatch( artifact ) == Boolean.FALSE );
 101  
     }
 102  
 
 103  
     protected void addFilteredArtifactId( String artifactId )
 104  
     {
 105  78
         filteredArtifactIds.add( artifactId );
 106  78
     }
 107  
 
 108  
     private Boolean negativeMatch( Artifact artifact )
 109  
     {
 110  60
         if ( ( negativePatterns == null ) || negativePatterns.isEmpty() )
 111  
         {
 112  48
             return null;
 113  
         }
 114  
         else
 115  
         {
 116  12
             return Boolean.valueOf( match( artifact, negativePatterns ) );
 117  
         }
 118  
     }
 119  
 
 120  
     protected Boolean positiveMatch( Artifact artifact )
 121  
     {
 122  156
         if ( ( positivePatterns == null ) || positivePatterns.isEmpty() )
 123  
         {
 124  12
             return null;
 125  
         }
 126  
         else
 127  
         {
 128  144
             return Boolean.valueOf( match( artifact, positivePatterns ) );
 129  
         }
 130  
     }
 131  
 
 132  
     private boolean match( Artifact artifact, List patterns )
 133  
     {
 134  156
         String shortId = ArtifactUtils.versionlessKey( artifact );
 135  156
         String id = artifact.getDependencyConflictId();
 136  156
         String wholeId = artifact.getId();
 137  
 
 138  156
         if ( matchAgainst( wholeId, patterns, false ) )
 139  
         {
 140  96
             return true;
 141  
         }
 142  
 
 143  60
         if ( matchAgainst( id, patterns, false ) )
 144  
         {
 145  0
             return true;
 146  
         }
 147  
 
 148  60
         if ( matchAgainst( shortId, patterns, false ) )
 149  
         {
 150  0
             return true;
 151  
         }
 152  
 
 153  60
         if ( actTransitively )
 154  
         {
 155  24
             List depTrail = artifact.getDependencyTrail();
 156  
 
 157  24
             if ( ( depTrail != null ) && !depTrail.isEmpty() )
 158  
             {
 159  12
                 String trailStr = "," + StringUtils.join( depTrail.iterator(), "," );
 160  
 
 161  12
                 return matchAgainst( trailStr, patterns, true );
 162  
             }
 163  
         }
 164  
 
 165  48
         return false;
 166  
     }
 167  
 
 168  
     private boolean matchAgainst( String value, List patterns, boolean regionMatch ) {
 169  288
             for (Iterator iterator = patterns.iterator(); iterator.hasNext();) {
 170  408
                         String pattern = (String) iterator.next();
 171  
                         
 172  408
                         String[] patternTokens = pattern.split( ":" );
 173  408
                         String[] tokens = value.split( ":" );
 174  
                         
 175  
                         // fail immediately if pattern tokens outnumber tokens to match
 176  408
                 boolean matched = ( patternTokens.length <= tokens.length );
 177  
 
 178  972
                 for ( int i = 0; matched && i < patternTokens.length; i++ )
 179  
                 {
 180  564
                     matched = matches( tokens[i], patternTokens[i] );
 181  
                 }
 182  
                 
 183  
 //                // case of starting '*' like '*:jar:*'
 184  408
                 if (!matched && patternTokens.length < tokens.length && patternTokens.length>0 && "*".equals(patternTokens[0])) 
 185  
                 {
 186  24
                         matched=true;
 187  84
                         for ( int i = 0; matched && i < patternTokens.length; i++ )
 188  
                         {
 189  60
                             matched = matches( tokens[i+(tokens.length-patternTokens.length)], patternTokens[i] );
 190  
                         }
 191  
                 }
 192  
 
 193  408
                 if (matched) {
 194  96
                         patternsTriggered.add( pattern );
 195  96
                 return true;
 196  
                 }
 197  
                 
 198  312
                 if ( regionMatch && value.indexOf( pattern ) > -1 )
 199  
             {
 200  12
                 patternsTriggered.add( pattern );
 201  12
                 return true;
 202  
             }
 203  
                         
 204  
                 }
 205  180
             return false;
 206  
             
 207  
     }
 208  
 
 209  
     /**
 210  
      * Gets whether the specified token matches the specified pattern segment.
 211  
      * 
 212  
      * @param token
 213  
      *            the token to check
 214  
      * @param pattern
 215  
      *            the pattern segment to match, as defined above
 216  
      * @return <code>true</code> if the specified token is matched by the specified pattern segment
 217  
      */
 218  
     private boolean matches( String token, final String pattern )
 219  
     {
 220  
             boolean matches;
 221  
 
 222  
         // support full wildcard and implied wildcard
 223  624
         if ( "*".equals( pattern ) || pattern.length() == 0 )
 224  
         {
 225  132
             matches = true;
 226  
         }
 227  
         // support contains wildcard
 228  492
         else if ( pattern.startsWith( "*" ) && pattern.endsWith( "*" ) )
 229  
         {
 230  0
             String contains = pattern.substring( 1, pattern.length() - 1 );
 231  
 
 232  0
             matches = ( token.indexOf( contains ) != -1 );
 233  
         }
 234  
         // support leading wildcard
 235  492
         else if ( pattern.startsWith( "*" ) )
 236  
         {
 237  0
             String suffix = pattern.substring( 1, pattern.length() );
 238  
 
 239  0
             matches = token.endsWith( suffix );
 240  
         }
 241  
         // support trailing wildcard
 242  492
         else if ( pattern.endsWith( "*" ) )
 243  
         {
 244  0
             String prefix = pattern.substring( 0, pattern.length() - 1 );
 245  
 
 246  0
             matches = token.startsWith( prefix );
 247  
         }
 248  
         // support versions range 
 249  492
         else if ( pattern.startsWith( "[" ) || pattern.startsWith( "(" ))
 250  
         {
 251  0
                 matches = isVersionIncludedInRange(token, pattern);
 252  
         }
 253  
         // support exact match
 254  
         else
 255  
         {
 256  492
             matches = token.equals( pattern );
 257  
         }
 258  
 
 259  624
         return matches;
 260  
     }
 261  
     
 262  
     private boolean isVersionIncludedInRange(final String version, final String range) {
 263  
             try {
 264  0
                         return VersionRange.createFromVersionSpec(range).containsVersion(new DefaultArtifactVersion(version));
 265  0
                 } catch (InvalidVersionSpecificationException e) {
 266  0
                         return false;
 267  
                 }
 268  
         }
 269  
 
 270  
     public void reportMissedCriteria( Logger logger )
 271  
     {
 272  
         // if there are no patterns, there is nothing to report.
 273  0
         if ( !positivePatterns.isEmpty() || !negativePatterns.isEmpty() )
 274  
         {
 275  0
             List missed = new ArrayList();
 276  0
             missed.addAll( positivePatterns );
 277  0
             missed.addAll( negativePatterns );
 278  
 
 279  0
             missed.removeAll( patternsTriggered );
 280  
 
 281  0
             if ( !missed.isEmpty() && logger.isWarnEnabled() )
 282  
             {
 283  0
                 StringBuffer buffer = new StringBuffer();
 284  
 
 285  0
                 buffer.append( "The following patterns were never triggered in this " );
 286  0
                 buffer.append( getFilterDescription() );
 287  0
                 buffer.append( ':' );
 288  
 
 289  0
                 for ( Iterator it = missed.iterator(); it.hasNext(); )
 290  
                 {
 291  0
                     String pattern = (String) it.next();
 292  
 
 293  0
                     buffer.append( "\no  \'" ).append( pattern ).append( "\'" );
 294  
                 }
 295  
 
 296  0
                 buffer.append( "\n" );
 297  
 
 298  0
                 logger.warn( buffer.toString() );
 299  
             }
 300  
         }
 301  0
     }
 302  
 
 303  
     public String toString()
 304  
     {
 305  0
         return "Includes filter:" + getPatternsAsString();
 306  
     }
 307  
 
 308  
     protected String getPatternsAsString()
 309  
     {
 310  0
         StringBuffer buffer = new StringBuffer();
 311  0
         for ( Iterator it = positivePatterns.iterator(); it.hasNext(); )
 312  
         {
 313  0
             String pattern = (String) it.next();
 314  
 
 315  0
             buffer.append( "\no \'" ).append( pattern ).append( "\'" );
 316  
         }
 317  
 
 318  0
         return buffer.toString();
 319  
     }
 320  
 
 321  
     protected String getFilterDescription()
 322  
     {
 323  0
         return "artifact inclusion filter";
 324  
     }
 325  
 
 326  
     public void reportFilteredArtifacts( Logger logger )
 327  
     {
 328  0
         if ( !filteredArtifactIds.isEmpty() && logger.isDebugEnabled() )
 329  
         {
 330  0
             StringBuffer buffer = new StringBuffer( "The following artifacts were removed by this "
 331  
                 + getFilterDescription() + ": " );
 332  
 
 333  0
             for ( Iterator it = filteredArtifactIds.iterator(); it.hasNext(); )
 334  
             {
 335  0
                 String artifactId = (String) it.next();
 336  
 
 337  0
                 buffer.append( '\n' ).append( artifactId );
 338  
             }
 339  
 
 340  0
             logger.debug( buffer.toString() );
 341  
         }
 342  0
     }
 343  
 
 344  
     public boolean hasMissedCriteria()
 345  
     {
 346  
         // if there are no patterns, there is nothing to report.
 347  0
         if ( !positivePatterns.isEmpty() || !negativePatterns.isEmpty() )
 348  
         {
 349  0
             List missed = new ArrayList();
 350  0
             missed.addAll( positivePatterns );
 351  0
             missed.addAll( negativePatterns );
 352  
 
 353  0
             missed.removeAll( patternsTriggered );
 354  
 
 355  0
             return !missed.isEmpty();
 356  
         }
 357  
 
 358  0
         return false;
 359  
     }
 360  
 
 361  
 }