1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
37
38
39
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 private final Set patternsTriggered = new HashSet();
51
52 private final List filteredArtifactIds = new ArrayList();
53
54 public PatternIncludesArtifactFilter( final List patterns )
55 {
56 this( patterns, false );
57 }
58
59 public PatternIncludesArtifactFilter( final List patterns, final boolean actTransitively )
60 {
61 this.actTransitively = actTransitively;
62 final List pos = new ArrayList();
63 final List neg = new ArrayList();
64 if ( ( patterns != null ) && !patterns.isEmpty() )
65 {
66 for ( final Iterator it = patterns.iterator(); it.hasNext(); )
67 {
68 final String pattern = (String) it.next();
69
70 if ( pattern.startsWith( "!" ) )
71 {
72 neg.add( pattern.substring( 1 ) );
73 }
74 else
75 {
76 pos.add( pattern );
77 }
78 }
79 }
80
81 positivePatterns = pos;
82 negativePatterns = neg;
83 }
84
85 public boolean include( final Artifact artifact )
86 {
87 final boolean shouldInclude = patternMatches( artifact );
88
89 if ( !shouldInclude )
90 {
91 addFilteredArtifactId( artifact.getId() );
92 }
93
94 return shouldInclude;
95 }
96
97 protected boolean patternMatches( final Artifact artifact )
98 {
99 return ( positiveMatch( artifact ) == Boolean.TRUE ) || ( negativeMatch( artifact ) == Boolean.FALSE );
100 }
101
102 protected void addFilteredArtifactId( final String artifactId )
103 {
104 filteredArtifactIds.add( artifactId );
105 }
106
107 private Boolean negativeMatch( final Artifact artifact )
108 {
109 if ( ( negativePatterns == null ) || negativePatterns.isEmpty() )
110 {
111 return null;
112 }
113 else
114 {
115 return Boolean.valueOf( match( artifact, negativePatterns ) );
116 }
117 }
118
119 protected Boolean positiveMatch( final Artifact artifact )
120 {
121 if ( ( positivePatterns == null ) || positivePatterns.isEmpty() )
122 {
123 return null;
124 }
125 else
126 {
127 return Boolean.valueOf( match( artifact, positivePatterns ) );
128 }
129 }
130
131 private boolean match( final Artifact artifact, final List patterns )
132 {
133 final String shortId = ArtifactUtils.versionlessKey( artifact );
134 final String id = artifact.getDependencyConflictId();
135 final String wholeId = artifact.getId();
136
137 if ( matchAgainst( wholeId, patterns, false ) )
138 {
139 return true;
140 }
141
142 if ( matchAgainst( id, patterns, false ) )
143 {
144 return true;
145 }
146
147 if ( matchAgainst( shortId, patterns, false ) )
148 {
149 return true;
150 }
151
152 if ( actTransitively )
153 {
154 final List depTrail = artifact.getDependencyTrail();
155
156 if ( ( depTrail != null ) && depTrail.size() > 1 )
157 {
158 for ( final Iterator iterator = depTrail.iterator(); iterator.hasNext(); )
159 {
160 final String trailItem = (String) iterator.next();
161 if ( matchAgainst( trailItem, patterns, true ) )
162 {
163 return true;
164 }
165 }
166 }
167 }
168
169 return false;
170 }
171
172 private boolean matchAgainst( final String value, final List patterns, final boolean regionMatch )
173 {
174 for ( final Iterator iterator = patterns.iterator(); iterator.hasNext(); )
175 {
176 final String pattern = (String) iterator.next();
177
178 final String[] patternTokens = pattern.split( ":" );
179 final String[] tokens = value.split( ":" );
180
181
182 boolean matched = ( patternTokens.length <= tokens.length );
183
184 for ( int i = 0; matched && i < patternTokens.length; i++ )
185 {
186 matched = matches( tokens[i], patternTokens[i] );
187 }
188
189
190 if ( !matched && patternTokens.length < tokens.length && patternTokens.length > 0
191 && "*".equals( patternTokens[0] ) )
192 {
193 matched = true;
194 for ( int i = 0; matched && i < patternTokens.length; i++ )
195 {
196 matched = matches( tokens[i + ( tokens.length - patternTokens.length )], patternTokens[i] );
197 }
198 }
199
200 if ( matched )
201 {
202 patternsTriggered.add( pattern );
203 return true;
204 }
205
206 if ( regionMatch && value.indexOf( pattern ) > -1 )
207 {
208 patternsTriggered.add( pattern );
209 return true;
210 }
211
212 }
213 return false;
214
215 }
216
217
218
219
220
221
222
223
224 private boolean matches( final String token, final String pattern )
225 {
226 boolean matches;
227
228
229 if ( "*".equals( pattern ) || pattern.length() == 0 )
230 {
231 matches = true;
232 }
233
234 else if ( pattern.startsWith( "*" ) && pattern.endsWith( "*" ) )
235 {
236 final String contains = pattern.substring( 1, pattern.length() - 1 );
237
238 matches = ( token.indexOf( contains ) != -1 );
239 }
240
241 else if ( pattern.startsWith( "*" ) )
242 {
243 final String suffix = pattern.substring( 1, pattern.length() );
244
245 matches = token.endsWith( suffix );
246 }
247
248 else if ( pattern.endsWith( "*" ) )
249 {
250 final String prefix = pattern.substring( 0, pattern.length() - 1 );
251
252 matches = token.startsWith( prefix );
253 }
254
255 else if ( pattern.indexOf( '*' ) > -1 )
256 {
257 String[] parts = pattern.split( "\\*" );
258 int lastPartEnd = -1;
259 boolean match = true;
260
261 for ( String part : parts )
262 {
263 int idx = token.indexOf( part );
264 if ( idx <= lastPartEnd )
265 {
266 match = false;
267 break;
268 }
269
270 lastPartEnd = idx + part.length();
271 }
272
273 matches = match;
274 }
275
276 else if ( pattern.startsWith( "[" ) || pattern.startsWith( "(" ) )
277 {
278 matches = isVersionIncludedInRange( token, pattern );
279 }
280
281 else
282 {
283 matches = token.equals( pattern );
284 }
285
286 return matches;
287 }
288
289 private boolean isVersionIncludedInRange( final String version, final String range )
290 {
291 try
292 {
293 return VersionRange.createFromVersionSpec( range ).containsVersion( new DefaultArtifactVersion( version ) );
294 }
295 catch ( final InvalidVersionSpecificationException e )
296 {
297 return false;
298 }
299 }
300
301 public void reportMissedCriteria( final Logger logger )
302 {
303
304 if ( !positivePatterns.isEmpty() || !negativePatterns.isEmpty() )
305 {
306 final List missed = new ArrayList();
307 missed.addAll( positivePatterns );
308 missed.addAll( negativePatterns );
309
310 missed.removeAll( patternsTriggered );
311
312 if ( !missed.isEmpty() && logger.isWarnEnabled() )
313 {
314 final StringBuffer buffer = new StringBuffer();
315
316 buffer.append( "The following patterns were never triggered in this " );
317 buffer.append( getFilterDescription() );
318 buffer.append( ':' );
319
320 for ( final Iterator it = missed.iterator(); it.hasNext(); )
321 {
322 final String pattern = (String) it.next();
323
324 buffer.append( "\no \'" ).append( pattern ).append( "\'" );
325 }
326
327 buffer.append( "\n" );
328
329 logger.warn( buffer.toString() );
330 }
331 }
332 }
333
334 @Override
335 public String toString()
336 {
337 return "Includes filter:" + getPatternsAsString();
338 }
339
340 protected String getPatternsAsString()
341 {
342 final StringBuffer buffer = new StringBuffer();
343 for ( final Iterator it = positivePatterns.iterator(); it.hasNext(); )
344 {
345 final String pattern = (String) it.next();
346
347 buffer.append( "\no \'" ).append( pattern ).append( "\'" );
348 }
349
350 return buffer.toString();
351 }
352
353 protected String getFilterDescription()
354 {
355 return "artifact inclusion filter";
356 }
357
358 public void reportFilteredArtifacts( final Logger logger )
359 {
360 if ( !filteredArtifactIds.isEmpty() && logger.isDebugEnabled() )
361 {
362 final StringBuffer buffer =
363 new StringBuffer( "The following artifacts were removed by this " + getFilterDescription() + ": " );
364
365 for ( final Iterator it = filteredArtifactIds.iterator(); it.hasNext(); )
366 {
367 final String artifactId = (String) it.next();
368
369 buffer.append( '\n' ).append( artifactId );
370 }
371
372 logger.debug( buffer.toString() );
373 }
374 }
375
376 public boolean hasMissedCriteria()
377 {
378
379 if ( !positivePatterns.isEmpty() || !negativePatterns.isEmpty() )
380 {
381 final List missed = new ArrayList();
382 missed.addAll( positivePatterns );
383 missed.addAll( negativePatterns );
384
385 missed.removeAll( patternsTriggered );
386
387 return !missed.isEmpty();
388 }
389
390 return false;
391 }
392
393 }