1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.enforcer.rules.utils;
20
21 import java.util.Collection;
22 import java.util.HashSet;
23 import java.util.Objects;
24 import java.util.function.Function;
25
26 import org.apache.maven.artifact.Artifact;
27 import org.apache.maven.artifact.versioning.ArtifactVersion;
28 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
29 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
30 import org.apache.maven.artifact.versioning.VersionRange;
31 import org.apache.maven.model.Dependency;
32 import org.codehaus.plexus.util.StringUtils;
33
34 import static java.util.Optional.ofNullable;
35
36
37
38
39
40
41 public final class ArtifactMatcher {
42
43
44
45
46 public static class Pattern {
47 private final String pattern;
48
49 private final String[] parts;
50 private final java.util.regex.Pattern[] partsRegex;
51
52 public Pattern(String pattern) {
53 if (pattern == null) {
54 throw new NullPointerException("pattern");
55 }
56
57 this.pattern = pattern;
58
59 parts = pattern.split(":", 7);
60
61 if (parts.length == 7) {
62 throw new IllegalArgumentException("Pattern contains too many delimiters.");
63 }
64
65 for (String part : parts) {
66 if ("".equals(part)) {
67 throw new IllegalArgumentException("Pattern or its part is empty.");
68 }
69 }
70 partsRegex = new java.util.regex.Pattern[parts.length];
71 }
72
73 public boolean match(Artifact artifact) {
74 Objects.requireNonNull(artifact, "artifact must not be null");
75 try {
76 return match(
77 artifact.getGroupId(),
78 artifact.getArtifactId(),
79 artifact.getVersion(),
80 artifact.getType(),
81 artifact.getScope(),
82 artifact.getClassifier());
83 } catch (InvalidVersionSpecificationException e) {
84 throw new IllegalArgumentException(e);
85 }
86 }
87
88 public boolean match(Dependency dependency) {
89 Objects.requireNonNull(dependency, "dependency must not be null");
90 try {
91 return match(
92 dependency.getGroupId(),
93 dependency.getArtifactId(),
94 dependency.getVersion(),
95 dependency.getType(),
96 dependency.getScope(),
97 dependency.getClassifier());
98 } catch (InvalidVersionSpecificationException e) {
99 throw new IllegalArgumentException(e);
100 }
101 }
102
103 private boolean match(
104 String groupId, String artifactId, String version, String type, String scope, String classifier)
105 throws InvalidVersionSpecificationException {
106 switch (parts.length) {
107 case 6:
108 if (!matches(5, classifier)) {
109 return false;
110 }
111 case 5:
112 if (scope == null || scope.isEmpty()) {
113 scope = Artifact.SCOPE_COMPILE;
114 }
115
116 if (!matches(4, scope)) {
117 return false;
118 }
119 case 4:
120 if (type == null || type.isEmpty()) {
121 type = "jar";
122 }
123
124 if (!matches(3, type)) {
125 return false;
126 }
127
128 case 3:
129 if (!matches(2, version)) {
130 if (!containsVersion(
131 VersionRange.createFromVersionSpec(parts[2]), new DefaultArtifactVersion(version))) {
132 return false;
133 }
134 }
135
136 case 2:
137 if (!matches(1, artifactId)) {
138 return false;
139 }
140 case 1:
141 return matches(0, groupId);
142 default:
143 throw new AssertionError();
144 }
145 }
146
147 private boolean matches(int index, String input) {
148
149 if (partsRegex[index] == null) {
150 String regex = parts[index]
151 .replace(".", "\\.")
152 .replace("*", ".*")
153 .replace(":", "\\:")
154 .replace('?', '.')
155 .replace("[", "\\[")
156 .replace("]", "\\]")
157 .replace("(", "\\(")
158 .replace(")", "\\)");
159
160
161 if (input == null) {
162 input = "";
163 }
164 partsRegex[index] = java.util.regex.Pattern.compile(regex);
165 }
166 return partsRegex[index].matcher(input).matches();
167 }
168
169 @Override
170 public String toString() {
171 return pattern;
172 }
173 }
174
175 private final Collection<Pattern> excludePatterns = new HashSet<>();
176
177 private final Collection<Pattern> includePatterns = new HashSet<>();
178
179
180
181
182
183
184
185
186 public ArtifactMatcher(final Collection<String> excludeStrings, final Collection<String> includeStrings) {
187 ofNullable(excludeStrings).ifPresent(excludes -> excludes.stream()
188 .filter(StringUtils::isNotEmpty)
189 .map(Pattern::new)
190 .forEach(excludePatterns::add));
191 ofNullable(includeStrings).ifPresent(includes -> includes.stream()
192 .filter(StringUtils::isNotEmpty)
193 .map(Pattern::new)
194 .forEach(includePatterns::add));
195 }
196
197 private boolean match(Function<Pattern, Boolean> matcher) {
198 return excludePatterns.stream().anyMatch(matcher::apply)
199 && includePatterns.stream().noneMatch(matcher::apply);
200 }
201
202
203
204
205
206
207
208
209 public boolean match(Artifact artifact) {
210 return match(p -> p.match(artifact));
211 }
212
213
214
215
216
217
218
219
220 public boolean match(Dependency dependency) {
221 return match(p -> p.match(dependency));
222 }
223
224
225
226
227
228
229
230
231
232
233 public static boolean containsVersion(VersionRange allowedRange, ArtifactVersion theVersion) {
234 ArtifactVersion recommendedVersion = allowedRange.getRecommendedVersion();
235 if (recommendedVersion == null) {
236 return allowedRange.containsVersion(theVersion);
237 } else {
238
239 int compareTo = recommendedVersion.compareTo(theVersion);
240 return compareTo <= 0;
241 }
242 }
243 }