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.dependency;
20
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.apache.commons.lang3.StringUtils;
27 import org.apache.maven.enforcer.rules.utils.ArtifactUtils;
28 import org.apache.maven.enforcer.rules.utils.ParentNodeProvider;
29 import org.apache.maven.enforcer.rules.utils.ParentsVisitor;
30 import org.eclipse.aether.artifact.Artifact;
31 import org.eclipse.aether.graph.DependencyNode;
32 import org.eclipse.aether.graph.DependencyVisitor;
33
34
35
36
37 class DependencyVersionMap implements DependencyVisitor, ParentNodeProvider {
38 private ParentsVisitor parentsVisitor;
39 private boolean uniqueVersions;
40 private final Map<String, List<DependencyNode>> idsToNode = new HashMap<>();
41
42 DependencyVersionMap() {
43 this.parentsVisitor = new ParentsVisitor();
44 }
45
46 public DependencyVersionMap setUniqueVersions(boolean uniqueVersions) {
47 this.uniqueVersions = uniqueVersions;
48 return this;
49 }
50
51 @Override
52 public boolean visitEnter(DependencyNode node) {
53 addDependency(node);
54 return parentsVisitor.visitEnter(node) && !containsConflicts(node);
55 }
56
57 @Override
58 public boolean visitLeave(DependencyNode node) {
59 return parentsVisitor.visitLeave(node);
60 }
61
62 @Override
63 public DependencyNode getParent(DependencyNode node) {
64 return parentsVisitor.getParent(node);
65 }
66
67 private String constructKey(DependencyNode node) {
68 return constructKey(node.getArtifact());
69 }
70
71 private String constructKey(Artifact artifact) {
72 return artifact.getGroupId() + ":" + artifact.getArtifactId();
73 }
74
75 public void addDependency(DependencyNode node) {
76 String key = constructKey(node);
77 idsToNode.computeIfAbsent(key, k -> new ArrayList<>()).add(node);
78 }
79
80 private String getVersion(Artifact artifact) {
81 return uniqueVersions ? artifact.getVersion() : artifact.getBaseVersion();
82 }
83
84 private boolean containsConflicts(DependencyNode node) {
85 return containsConflicts(node.getArtifact());
86 }
87
88 private boolean containsConflicts(Artifact artifact) {
89 return containsConflicts(idsToNode.get(constructKey(artifact)));
90 }
91
92 private boolean containsConflicts(List<DependencyNode> nodes) {
93 String version = null;
94 for (DependencyNode node : nodes) {
95 if (version == null) {
96 version = getVersion(node.getArtifact());
97 } else {
98 if (version.compareTo(getVersion(node.getArtifact())) != 0) {
99 return true;
100 }
101 }
102 }
103 return false;
104 }
105
106 public List<List<DependencyNode>> getConflictedVersionNumbers(List<String> includes, List<String> excludes) {
107 List<String> formattedIncludes = formatPatterns(includes);
108 List<String> formattedExcludes = formatPatterns(excludes);
109 List<List<DependencyNode>> output = new ArrayList<>();
110 for (List<DependencyNode> nodes : idsToNode.values()) {
111 List<DependencyNode> filteredNodes = nodes;
112 if (formattedIncludes != null || formattedExcludes != null) {
113 filteredNodes = new ArrayList<>();
114 for (DependencyNode node : nodes) {
115 if (includeArtifact(node, formattedIncludes, formattedExcludes)) {
116 filteredNodes.add(node);
117 }
118 }
119 }
120 if (containsConflicts(filteredNodes)) {
121 output.add(filteredNodes);
122 }
123 }
124 return output;
125 }
126
127 private static boolean includeArtifact(DependencyNode node, List<String> includes, List<String> excludes) {
128 boolean included = includes == null || includes.isEmpty();
129 if (!included) {
130 for (String pattern : includes) {
131 if (ArtifactUtils.compareDependency(pattern, ArtifactUtils.toArtifact(node))) {
132 included = true;
133 break;
134 }
135 }
136 }
137 if (!included) {
138 return false;
139 }
140 boolean excluded = false;
141 if (excludes != null) {
142 for (String pattern : excludes) {
143 if (ArtifactUtils.compareDependency(pattern, ArtifactUtils.toArtifact(node))) {
144 excluded = true;
145 break;
146 }
147 }
148 }
149 return !excluded;
150 }
151
152 private static List<String> formatPatterns(List<String> patterns) {
153 if (patterns == null) {
154 return null;
155 }
156 List<String> formattedPatterns = new ArrayList<>();
157 for (String pattern : patterns) {
158 String[] subStrings = pattern.split(":");
159 subStrings = StringUtils.stripAll(subStrings);
160 String formattedPattern = StringUtils.join(subStrings, ":");
161 formattedPatterns.add(formattedPattern);
162 }
163 return formattedPatterns;
164 }
165 }