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.eclipse.aether.util.filter;
20  
21  import java.util.Arrays;
22  import java.util.Collection;
23  import java.util.HashSet;
24  import java.util.List;
25  import java.util.Objects;
26  import java.util.Set;
27  
28  import org.eclipse.aether.artifact.Artifact;
29  import org.eclipse.aether.graph.Dependency;
30  import org.eclipse.aether.graph.DependencyFilter;
31  import org.eclipse.aether.graph.DependencyNode;
32  import org.eclipse.aether.version.InvalidVersionSpecificationException;
33  import org.eclipse.aether.version.Version;
34  import org.eclipse.aether.version.VersionRange;
35  import org.eclipse.aether.version.VersionScheme;
36  
37  import static java.util.Objects.requireNonNull;
38  
39  /**
40   */
41  class AbstractPatternDependencyFilter implements DependencyFilter {
42  
43      private final Set<String> patterns = new HashSet<>();
44  
45      private final VersionScheme versionScheme;
46  
47      /**
48       * Creates a new filter using the specified patterns.
49       *
50       * @param patterns The include patterns, may be {@code null} or empty to include no artifacts.
51       */
52      AbstractPatternDependencyFilter(final String... patterns) {
53          this(null, patterns);
54      }
55  
56      /**
57       * Creates a new filter using the specified patterns.
58       *
59       * @param versionScheme To be used for parsing versions/version ranges. If {@code null} and pattern specifies a
60       *            range no artifact will be included.
61       * @param patterns The include patterns, may be {@code null} or empty to include no artifacts.
62       */
63      AbstractPatternDependencyFilter(final VersionScheme versionScheme, final String... patterns) {
64          this(versionScheme, patterns == null ? null : Arrays.asList(patterns));
65      }
66  
67      /**
68       * Creates a new filter using the specified patterns.
69       *
70       * @param patterns The include patterns, may be {@code null} or empty to include no artifacts.
71       */
72      AbstractPatternDependencyFilter(final Collection<String> patterns) {
73          this(null, patterns);
74      }
75  
76      /**
77       * Creates a new filter using the specified patterns and {@link VersionScheme} .
78       *
79       * @param versionScheme To be used for parsing versions/version ranges. If {@code null} and pattern specifies a
80       *            range no artifact will be included.
81       * @param patterns The include patterns, may be {@code null} or empty to include no artifacts.
82       */
83      AbstractPatternDependencyFilter(final VersionScheme versionScheme, final Collection<String> patterns) {
84          if (patterns != null) {
85              this.patterns.addAll(patterns);
86          }
87          this.versionScheme = versionScheme;
88      }
89  
90      public boolean accept(final DependencyNode node, List<DependencyNode> parents) {
91          requireNonNull(node, "node cannot be null");
92          requireNonNull(parents, "parents cannot be null");
93          final Dependency dependency = node.getDependency();
94          if (dependency == null) {
95              return true;
96          }
97          return accept(dependency.getArtifact());
98      }
99  
100     protected boolean accept(final Artifact artifact) {
101         for (final String pattern : patterns) {
102             final boolean matched = accept(artifact, pattern);
103             if (matched) {
104                 return true;
105             }
106         }
107         return false;
108     }
109 
110     private boolean accept(final Artifact artifact, final String pattern) {
111         final String[] tokens = new String[] {
112             artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(), artifact.getBaseVersion()
113         };
114 
115         final String[] patternTokens = pattern.split(":");
116 
117         // fail immediately if pattern tokens outnumber tokens to match
118         boolean matched = (patternTokens.length <= tokens.length);
119 
120         for (int i = 0; matched && i < patternTokens.length; i++) {
121             matched = matches(tokens[i], patternTokens[i]);
122         }
123 
124         return matched;
125     }
126 
127     private boolean matches(final String token, final String pattern) {
128         boolean matches;
129 
130         // support full wildcard and implied wildcard
131         if ("*".equals(pattern) || pattern.length() == 0) {
132             matches = true;
133         }
134         // support contains wildcard
135         else if (pattern.startsWith("*") && pattern.endsWith("*")) {
136             final String contains = pattern.substring(1, pattern.length() - 1);
137 
138             matches = (token.contains(contains));
139         }
140         // support leading wildcard
141         else if (pattern.startsWith("*")) {
142             final String suffix = pattern.substring(1);
143 
144             matches = token.endsWith(suffix);
145         }
146         // support trailing wildcard
147         else if (pattern.endsWith("*")) {
148             final 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         if (versionScheme == null) {
166             return false;
167         } else {
168             try {
169                 final Version parsedVersion = versionScheme.parseVersion(version);
170                 final VersionRange parsedRange = versionScheme.parseVersionRange(range);
171 
172                 return parsedRange.containsVersion(parsedVersion);
173             } catch (final InvalidVersionSpecificationException e) {
174                 return false;
175             }
176         }
177     }
178 
179     @Override
180     public boolean equals(final Object obj) {
181         if (this == obj) {
182             return true;
183         }
184 
185         if (obj == null || !getClass().equals(obj.getClass())) {
186             return false;
187         }
188 
189         final AbstractPatternDependencyFilter that = (AbstractPatternDependencyFilter) obj;
190 
191         return Objects.equals(this.patterns, that.patterns) && Objects.equals(this.versionScheme, that.versionScheme);
192     }
193 
194     @Override
195     public int hashCode() {
196         int hash = 17;
197         hash = hash * 31 + patterns.hashCode();
198         hash = hash * 31 + ((versionScheme == null) ? 0 : versionScheme.hashCode());
199         return hash;
200     }
201 }