View Javadoc
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.List;
22  
23  import org.apache.maven.artifact.Artifact;
24  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
25  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
26  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
27  import org.apache.maven.artifact.versioning.VersionRange;
28  
29  /**
30   * Filter to include or exclude artifacts from a list of patterns. The artifact pattern syntax is of the form:
31   *
32   * <pre>[groupId]:[artifactId]:[type]:[version]</pre>
33   *
34   * <p>
35   * Where each pattern segment is optional and supports full and partial <code>*</code> wildcards. An empty pattern
36   * segment is treated as an implicit wildcard.
37   * </p>
38   *
39   * <p>
40   * For example, <code>org.apache.*</code> would match all artifacts whose group id started with
41   * <code>org.apache.</code>, and <code>:::*-SNAPSHOT</code> would match all snapshot artifacts.
42   * </p>
43   *
44   * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
45   */
46  public abstract class AbstractStrictPatternArtifactFilter implements ArtifactFilter {
47      // fields -----------------------------------------------------------------
48  
49      /**
50       * The list of artifact patterns to match, as described above.
51       */
52      private final List<String> patterns;
53  
54      /**
55       * Whether this filter should include or exclude artifacts that match the patterns.
56       */
57      private final boolean include;
58  
59      // constructors -----------------------------------------------------------
60  
61      /**
62       * Creates a new filter that matches the specified artifact patterns and includes or excludes them according to the
63       * specified flag.
64       *
65       * @param patterns
66       *            the list of artifact patterns to match, as described above
67       * @param include
68       *            <code>true</code> to include artifacts that match the patterns, or <code>false</code> to exclude
69       *            them
70       */
71      public AbstractStrictPatternArtifactFilter(List<String> patterns, boolean include) {
72          this.patterns = patterns;
73          this.include = include;
74      }
75  
76      // ArtifactFilter methods -------------------------------------------------
77  
78      /** {@inheritDoc} */
79      public boolean include(Artifact artifact) {
80          boolean matched = false;
81  
82          for (String pattern : patterns) {
83              if (include(artifact, pattern)) {
84                  matched = true;
85                  break;
86              }
87          }
88  
89          return include ? matched : !matched;
90      }
91  
92      // private methods --------------------------------------------------------
93  
94      /**
95       * Gets whether the specified artifact matches the specified pattern.
96       *
97       * @param artifact
98       *            the artifact to check
99       * @param pattern
100      *            the pattern to match, as defined above
101      * @return <code>true</code> if the specified artifact is matched by the specified pattern
102      */
103     private boolean include(Artifact artifact, String pattern) {
104         String[] tokens = new String[] {
105             artifact.getGroupId(), artifact.getArtifactId(), artifact.getType(), artifact.getBaseVersion()
106         };
107 
108         String[] patternTokens = pattern.split(":");
109 
110         // fail immediately if pattern tokens outnumber tokens to match
111         boolean matched = patternTokens.length <= tokens.length;
112 
113         for (int i = 0; matched && i < patternTokens.length; i++) {
114             matched = matches(tokens[i], patternTokens[i]);
115         }
116 
117         return matched;
118     }
119 
120     /**
121      * Gets whether the specified token matches the specified pattern segment.
122      *
123      * @param token
124      *            the token to check
125      * @param pattern
126      *            the pattern segment to match, as defined above
127      * @return <code>true</code> if the specified token is matched by the specified pattern segment
128      */
129     private boolean matches(String token, String pattern) {
130         boolean matches;
131 
132         // support full wildcard and implied wildcard
133         if ("*".equals(pattern) || pattern.length() == 0) {
134             matches = true;
135         }
136         // support contains wildcard
137         else if (pattern.startsWith("*") && pattern.endsWith("*")) {
138             String contains = pattern.substring(1, pattern.length() - 1);
139 
140             matches = token.contains(contains);
141         }
142         // support leading wildcard
143         else if (pattern.startsWith("*")) {
144             matches = token.endsWith(pattern.substring(1));
145         }
146         // support trailing wildcard
147         else if (pattern.endsWith("*")) {
148             String prefix = pattern.substring(0, pattern.length() - 1);
149 
150             matches = token.startsWith(prefix);
151         }
152         // support versions range
153         else if (pattern.startsWith("[") || pattern.startsWith("(")) {
154             matches = isVersionIncludedInRange(token, pattern);
155         }
156         // support exact match
157         else {
158             matches = token.equals(pattern);
159         }
160 
161         return matches;
162     }
163 
164     private boolean isVersionIncludedInRange(final String version, final String range) {
165         try {
166             return VersionRange.createFromVersionSpec(range).containsVersion(new DefaultArtifactVersion(version));
167         } catch (InvalidVersionSpecificationException e) {
168             return false;
169         }
170     }
171 }