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