1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.basepom.mojo.duplicatefinder.artifact;
15
16 import java.io.File;
17 import java.io.IOException;
18 import java.util.Collection;
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.Objects;
22 import java.util.Set;
23
24 import com.google.common.annotations.VisibleForTesting;
25 import com.google.common.base.MoreObjects;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.collect.ImmutableMultimap;
28 import com.google.common.collect.ImmutableSet;
29 import com.google.common.collect.ImmutableSortedSet;
30 import com.google.common.collect.Multimap;
31 import com.google.common.collect.MultimapBuilder;
32 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
33 import org.apache.maven.artifact.Artifact;
34 import org.apache.maven.artifact.DefaultArtifact;
35 import org.apache.maven.artifact.DependencyResolutionRequiredException;
36 import org.apache.maven.artifact.versioning.VersionRange;
37 import org.apache.maven.project.MavenProject;
38 import org.basepom.mojo.duplicatefinder.ClasspathElement;
39 import org.basepom.mojo.duplicatefinder.ClasspathElement.ClasspathArtifact;
40 import org.basepom.mojo.duplicatefinder.ClasspathElement.ClasspathLocalFolder;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import static java.lang.String.format;
45
46 import static com.google.common.base.Preconditions.checkNotNull;
47 import static com.google.common.base.Preconditions.checkState;
48 import static org.basepom.mojo.duplicatefinder.artifact.ArtifactHelper.getOutputDirectory;
49 import static org.basepom.mojo.duplicatefinder.artifact.ArtifactHelper.getTestOutputDirectory;
50 import static org.basepom.mojo.duplicatefinder.artifact.ArtifactHelper.isTestArtifact;
51
52
53
54
55
56
57
58 @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
59 public class ArtifactFileResolver {
60
61 private static final Logger LOG = LoggerFactory.getLogger(ArtifactFileResolver.class);
62
63
64 private final Multimap<File, Artifact> localFileArtifactCache;
65 private final Map<Artifact, File> localArtifactFileCache;
66
67 private final Map<Artifact, File> repoArtifactCache;
68
69
70
71 private final Multimap<File, Artifact> repoFileCache = MultimapBuilder
72 .hashKeys()
73 .hashSetValues()
74 .build();
75 private final boolean preferLocal;
76
77 public ArtifactFileResolver(final MavenProject project,
78 final boolean preferLocal) throws DependencyResolutionRequiredException, IOException {
79 checkNotNull(project, "project is null");
80 this.preferLocal = preferLocal;
81
82
83
84
85 ImmutableMultimap.Builder<File, Artifact> localFileArtifactCacheBuilder = ImmutableMultimap.builder();
86
87
88 this.repoArtifactCache = new HashMap<>(project.getArtifacts().size());
89
90 for (final Artifact artifact : project.getArtifacts()) {
91 final File repoPath = artifact.getFile().getCanonicalFile();
92 final Artifact canonicalizedArtifact = ArtifactFileResolver.canonicalizeArtifact(artifact);
93
94 checkState(repoPath.exists(), "Repository Path '%s' does not exist.", repoPath);
95 final File oldFile = repoArtifactCache.put(canonicalizedArtifact, repoPath);
96 checkState(oldFile == null || oldFile.equals(repoPath), "Already encountered a file for %s: %s", canonicalizedArtifact, oldFile);
97 repoFileCache.put(repoPath, canonicalizedArtifact);
98 }
99
100 for (final MavenProject referencedProject : project.getProjectReferences().values()) {
101
102 final Set<Artifact> repoArtifacts = findRepoArtifacts(referencedProject, repoArtifactCache);
103
104
105
106
107 if (repoArtifacts.isEmpty()) {
108 LOG.debug(format("Found project reference to %s but no repo reference, probably used in a plugin dependency.", referencedProject.getArtifact()));
109 }
110
111 for (final Artifact artifact : repoArtifacts) {
112
113 final File outputDir = isTestArtifact(artifact) ? getTestOutputDirectory(referencedProject) : getOutputDirectory(referencedProject);
114
115 if (outputDir.exists()) {
116 localFileArtifactCacheBuilder.put(outputDir, artifact);
117 }
118 }
119 }
120
121 this.localFileArtifactCache = localFileArtifactCacheBuilder.build();
122
123
124
125
126 ImmutableMap.Builder<Artifact, File> localArtifactFileCacheBuilder = ImmutableMap.builder();
127 for (Map.Entry<File, Artifact> entry : localFileArtifactCache.entries()) {
128 localArtifactFileCacheBuilder.put(entry.getValue(), entry.getKey());
129 }
130
131 this.localArtifactFileCache = localArtifactFileCacheBuilder.build();
132 }
133
134 public ImmutableMultimap<File, Artifact> resolveArtifactsForScopes(final Set<String> scopes) {
135 checkNotNull(scopes, "scopes is null");
136
137 final ImmutableMultimap.Builder<File, Artifact> inScopeBuilder = ImmutableMultimap.builder();
138 for (final Artifact artifact : listArtifacts()) {
139 if (artifact.getArtifactHandler().isAddedToClasspath()) {
140 if (scopes.isEmpty() || scopes.contains(artifact.getScope())) {
141 final File file = resolveFileForArtifact(artifact);
142 checkState(file != null, "No file for artifact '%s' found!", artifact);
143 inScopeBuilder.put(file, artifact);
144 }
145 }
146 }
147
148 return inScopeBuilder.build();
149 }
150
151 public ImmutableSortedSet<ClasspathElement> getClasspathElementsForElements(final Collection<File> elements) {
152 final ImmutableSortedSet.Builder<ClasspathElement> builder = ImmutableSortedSet.naturalOrder();
153
154 for (final File element : elements) {
155 resolveClasspathElementsForFile(element, builder);
156 }
157 return builder.build();
158 }
159
160 private void resolveClasspathElementsForFile(final File file, ImmutableSet.Builder<ClasspathElement> builder) {
161 checkNotNull(file, "file is null");
162
163 if (preferLocal && localFileArtifactCache.containsKey(file)) {
164 for (Artifact artifact : localFileArtifactCache.get(file)) {
165 builder.add(new ClasspathArtifact(artifact));
166 }
167 return;
168 }
169
170 if (repoFileCache.containsKey(file)) {
171 for (Artifact artifact : repoFileCache.get(file)) {
172 builder.add(new ClasspathArtifact(artifact));
173 }
174 return;
175 }
176
177 if (localFileArtifactCache.containsKey(file)) {
178 for (Artifact artifact : localFileArtifactCache.get(file)) {
179 builder.add(new ClasspathArtifact(artifact));
180 }
181 return;
182 }
183
184 builder.add(new ClasspathLocalFolder(file));
185 }
186
187 private File resolveFileForArtifact(final Artifact artifact) {
188 checkNotNull(artifact, "artifact is null");
189
190 if (preferLocal && localArtifactFileCache.containsKey(artifact)) {
191 return localArtifactFileCache.get(artifact);
192 }
193
194 if (repoArtifactCache.containsKey(artifact)) {
195 return repoArtifactCache.get(artifact);
196 }
197
198 return localArtifactFileCache.get(artifact);
199 }
200
201 @VisibleForTesting
202 static DefaultArtifact canonicalizeArtifact(final Artifact artifact) {
203 final VersionRange versionRange =
204 artifact.getVersionRange() == null ? VersionRange.createFromVersion(artifact.getVersion()) : artifact.getVersionRange();
205 String type = MoreObjects.firstNonNull(artifact.getType(), "jar");
206 String classifier = artifact.getClassifier();
207
208 if ("test-jar".equals(type) && (classifier == null || "tests".equals(classifier))) {
209 type = "jar";
210 classifier = "tests";
211 }
212
213 return new DefaultArtifact(artifact.getGroupId(),
214 artifact.getArtifactId(),
215 versionRange,
216 artifact.getScope(),
217 type,
218 classifier,
219 artifact.getArtifactHandler(),
220 artifact.isOptional());
221 }
222
223 private Set<Artifact> listArtifacts() {
224 return ImmutableSet.<Artifact>builder().addAll(localArtifactFileCache.keySet()).addAll(repoArtifactCache.keySet()).build();
225 }
226
227 private static Set<Artifact> findRepoArtifacts(final MavenProject project, final Map<Artifact, File> repoArtifactCache) {
228 final ImmutableSet.Builder<Artifact> builder = ImmutableSet.builder();
229
230 for (final Artifact artifact : repoArtifactCache.keySet()) {
231 if (Objects.equals(project.getArtifact().getGroupId(), artifact.getGroupId())
232 && Objects.equals(project.getArtifact().getArtifactId(), artifact.getArtifactId())
233 && Objects.equals(project.getArtifact().getBaseVersion(), artifact.getBaseVersion())) {
234 builder.add(artifact);
235 }
236 }
237 return builder.build();
238 }
239 }