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