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