1 package org.apache.maven.shared.artifact.filter;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import org.apache.maven.artifact.Artifact;
23 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
24 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
25 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
26 import org.apache.maven.artifact.versioning.VersionRange;
27
28 import java.util.Iterator;
29 import java.util.List;
30
31 /**
32 * Filter to include or exclude artifacts from a list of patterns. The artifact pattern syntax is of the form:
33 *
34 * <pre>
35 * [groupId]:[artifactId]:[type]:[version]
36 * </pre>
37 *
38 * <p>
39 * Where each pattern segment is optional and supports full and partial <code>*</code> wildcards. An empty pattern
40 * segment is treated as an implicit wildcard.
41 * </p>
42 *
43 * <p>
44 * For example, <code>org.apache.*</code> would match all artifacts whose group id started with
45 * <code>org.apache.</code>, and <code>:::*-SNAPSHOT</code> would match all snapshot artifacts.
46 * </p>
47 *
48 * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
49 * @version $Id: AbstractStrictPatternArtifactFilter.java 803321 2009-08-11 23:09:24Z aheritier $
50 */
51 public abstract class AbstractStrictPatternArtifactFilter implements ArtifactFilter
52 {
53 // fields -----------------------------------------------------------------
54
55 /**
56 * The list of artifact patterns to match, as described above.
57 */
58 private final List patterns;
59
60 /**
61 * Whether this filter should include or exclude artifacts that match the patterns.
62 */
63 private final boolean include;
64
65 // constructors -----------------------------------------------------------
66
67 /**
68 * Creates a new filter that matches the specified artifact patterns and includes or excludes them according to the
69 * specified flag.
70 *
71 * @param patterns
72 * the list of artifact patterns to match, as described above
73 * @param include
74 * <code>true</code> to include artifacts that match the patterns, or <code>false</code> to exclude
75 * them
76 */
77 public AbstractStrictPatternArtifactFilter( List patterns, boolean include )
78 {
79 this.patterns = patterns;
80 this.include = include;
81 }
82
83 // ArtifactFilter methods -------------------------------------------------
84
85 /*
86 * @see org.apache.maven.artifact.resolver.filter.ArtifactFilter#include(org.apache.maven.artifact.Artifact)
87 */
88 public boolean include( Artifact artifact )
89 {
90 boolean matched = false;
91
92 for ( Iterator i = patterns.iterator(); i.hasNext() & !matched; )
93 {
94 String pattern = (String) i.next();
95
96 if ( include( artifact, pattern ) )
97 {
98 matched = true;
99 }
100 }
101
102 return include ? matched : !matched;
103 }
104
105 // private methods --------------------------------------------------------
106
107 /**
108 * Gets whether the specified artifact matches the specified pattern.
109 *
110 * @param artifact
111 * the artifact to check
112 * @param pattern
113 * the pattern to match, as defined above
114 * @return <code>true</code> if the specified artifact is matched by the specified pattern
115 */
116 private boolean include( Artifact artifact, String pattern )
117 {
118 String[] tokens = new String[] {
119 artifact.getGroupId(),
120 artifact.getArtifactId(),
121 artifact.getType(),
122 artifact.getBaseVersion()
123 };
124
125 String[] patternTokens = pattern.split( ":" );
126
127 // fail immediately if pattern tokens outnumber tokens to match
128 boolean matched = ( patternTokens.length <= tokens.length );
129
130 for ( int i = 0; matched && i < patternTokens.length; i++ )
131 {
132 matched = matches( tokens[i], patternTokens[i] );
133 }
134
135 return matched;
136 }
137
138 /**
139 * Gets whether the specified token matches the specified pattern segment.
140 *
141 * @param token
142 * the token to check
143 * @param pattern
144 * the pattern segment to match, as defined above
145 * @return <code>true</code> if the specified token is matched by the specified pattern segment
146 */
147 private boolean matches( String token, String pattern )
148 {
149 boolean matches;
150
151 // support full wildcard and implied wildcard
152 if ( "*".equals( pattern ) || pattern.length() == 0 )
153 {
154 matches = true;
155 }
156 // support contains wildcard
157 else if ( pattern.startsWith( "*" ) && pattern.endsWith( "*" ) )
158 {
159 String contains = pattern.substring( 1, pattern.length() - 1 );
160
161 matches = ( token.indexOf( contains ) != -1 );
162 }
163 // support leading wildcard
164 else if ( pattern.startsWith( "*" ) )
165 {
166 String suffix = pattern.substring( 1, pattern.length() );
167
168 matches = token.endsWith( suffix );
169 }
170 // support trailing wildcard
171 else if ( pattern.endsWith( "*" ) )
172 {
173 String prefix = pattern.substring( 0, pattern.length() - 1 );
174
175 matches = token.startsWith( prefix );
176 }
177 // support versions range
178 else if ( pattern.startsWith( "[" ) || pattern.startsWith( "(" ))
179 {
180 matches = isVersionIncludedInRange(token, pattern);
181 }
182 // support exact match
183 else
184 {
185 matches = token.equals( pattern );
186 }
187
188 return matches;
189 }
190
191 private boolean isVersionIncludedInRange(final String version, final String range) {
192 try {
193 return VersionRange.createFromVersionSpec(range).containsVersion(new DefaultArtifactVersion(version));
194 } catch (InvalidVersionSpecificationException e) {
195 return false;
196 }
197 }
198
199 }