1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.project;
20
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.stream.Collectors;
27
28 import org.apache.maven.api.model.Build;
29 import org.apache.maven.api.model.Dependency;
30 import org.apache.maven.api.model.Extension;
31 import org.apache.maven.api.model.Parent;
32 import org.apache.maven.api.model.Plugin;
33 import org.apache.maven.artifact.ArtifactUtils;
34 import org.apache.maven.project.Graph.Vertex;
35
36
37
38
39 public class ProjectSorter {
40 private Graph graph;
41
42 private List<MavenProject> sortedProjects;
43
44 private Map<String, MavenProject> projectMap;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public ProjectSorter(Collection<MavenProject> projects) throws CycleDetectedException, DuplicateProjectException {
70 graph = new Graph();
71
72
73 projectMap = new HashMap<>(projects.size() * 2);
74
75
76 Map<String, Map<String, Vertex>> vertexMap = new HashMap<>(projects.size() * 2);
77
78 for (MavenProject project : projects) {
79 String projectId = getId(project);
80
81 MavenProject conflictingProject = projectMap.put(projectId, project);
82
83 if (conflictingProject != null) {
84 throw new DuplicateProjectException(
85 projectId,
86 conflictingProject.getFile(),
87 project.getFile(),
88 "Project '" + projectId + "' is duplicated in the reactor");
89 }
90
91 String projectKey = ArtifactUtils.versionlessKey(project.getGroupId(), project.getArtifactId());
92
93 Map<String, Vertex> vertices = vertexMap.computeIfAbsent(projectKey, k -> new HashMap<>(2, 1));
94
95 vertices.put(project.getVersion(), graph.addVertex(projectId));
96 }
97
98 for (Vertex projectVertex : graph.getVertices()) {
99 String projectId = projectVertex.getLabel();
100
101 MavenProject project = projectMap.get(projectId);
102
103 for (Dependency dependency : project.getModel().getDelegate().getDependencies()) {
104 addEdge(
105 projectMap,
106 vertexMap,
107 project,
108 projectVertex,
109 dependency.getGroupId(),
110 dependency.getArtifactId(),
111 dependency.getVersion(),
112 false,
113 false);
114 }
115
116 Parent parent = project.getModel().getDelegate().getParent();
117
118 if (parent != null) {
119
120
121 addEdge(
122 projectMap,
123 vertexMap,
124 null,
125 projectVertex,
126 parent.getGroupId(),
127 parent.getArtifactId(),
128 parent.getVersion(),
129 true,
130 false);
131 }
132
133 Build build = project.getModel().getDelegate().getBuild();
134 if (build != null) {
135 for (Plugin plugin : build.getPlugins()) {
136 addEdge(
137 projectMap,
138 vertexMap,
139 project,
140 projectVertex,
141 plugin.getGroupId(),
142 plugin.getArtifactId(),
143 plugin.getVersion(),
144 false,
145 true);
146
147 for (Dependency dependency : plugin.getDependencies()) {
148 addEdge(
149 projectMap,
150 vertexMap,
151 project,
152 projectVertex,
153 dependency.getGroupId(),
154 dependency.getArtifactId(),
155 dependency.getVersion(),
156 false,
157 true);
158 }
159 }
160
161 for (Extension extension : build.getExtensions()) {
162 addEdge(
163 projectMap,
164 vertexMap,
165 project,
166 projectVertex,
167 extension.getGroupId(),
168 extension.getArtifactId(),
169 extension.getVersion(),
170 false,
171 true);
172 }
173 }
174 }
175
176 List<String> sortedProjectLabels = graph.visitAll();
177
178 this.sortedProjects = sortedProjectLabels.stream()
179 .map(id -> projectMap.get(id))
180 .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
181 }
182
183 @SuppressWarnings("checkstyle:parameternumber")
184 private void addEdge(
185 Map<String, MavenProject> projectMap,
186 Map<String, Map<String, Vertex>> vertexMap,
187 MavenProject project,
188 Vertex projectVertex,
189 String groupId,
190 String artifactId,
191 String version,
192 boolean force,
193 boolean safe)
194 throws CycleDetectedException {
195 String projectKey = ArtifactUtils.versionlessKey(groupId, artifactId);
196
197 Map<String, Vertex> vertices = vertexMap.get(projectKey);
198
199 if (vertices != null) {
200 if (isSpecificVersion(version)) {
201 Vertex vertex = vertices.get(version);
202 if (vertex != null) {
203 addEdge(projectVertex, vertex, project, projectMap, force, safe);
204 }
205 } else {
206 for (Vertex vertex : vertices.values()) {
207 addEdge(projectVertex, vertex, project, projectMap, force, safe);
208 }
209 }
210 }
211 }
212
213 private void addEdge(
214 Vertex fromVertex,
215 Vertex toVertex,
216 MavenProject fromProject,
217 Map<String, MavenProject> projectMap,
218 boolean force,
219 boolean safe)
220 throws CycleDetectedException {
221 if (fromVertex.equals(toVertex)) {
222 return;
223 }
224
225 if (fromProject != null) {
226 MavenProject toProject = projectMap.get(toVertex.getLabel());
227 fromProject.addProjectReference(toProject);
228 }
229
230 if (force && toVertex.getChildren().contains(fromVertex)) {
231 graph.removeEdge(toVertex, fromVertex);
232 }
233
234 try {
235 graph.addEdge(fromVertex, toVertex);
236 } catch (CycleDetectedException e) {
237 if (!safe) {
238 throw e;
239 }
240 }
241 }
242
243 private boolean isSpecificVersion(String version) {
244 return !((version == null || version.isEmpty()) || version.startsWith("[") || version.startsWith("("));
245 }
246
247
248
249 public MavenProject getTopLevelProject() {
250 return sortedProjects.stream()
251 .filter(MavenProject::isExecutionRoot)
252 .findFirst()
253 .orElse(null);
254 }
255
256 public List<MavenProject> getSortedProjects() {
257 return sortedProjects;
258 }
259
260 public boolean hasMultipleProjects() {
261 return sortedProjects.size() > 1;
262 }
263
264 public List<String> getDependents(String id) {
265 return graph.getVertex(id).getParents().stream().map(Vertex::getLabel).collect(Collectors.toList());
266 }
267
268 public List<String> getDependencies(String id) {
269 return graph.getVertex(id).getChildren().stream().map(Vertex::getLabel).collect(Collectors.toList());
270 }
271
272 public static String getId(MavenProject project) {
273 return ArtifactUtils.key(project.getGroupId(), project.getArtifactId(), project.getVersion());
274 }
275
276 public Map<String, MavenProject> getProjectMap() {
277 return projectMap;
278 }
279 }