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.io.File;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import org.apache.maven.artifact.ArtifactUtils;
34  import org.apache.maven.model.Dependency;
35  import org.apache.maven.model.Extension;
36  import org.apache.maven.model.Plugin;
37  import org.apache.maven.model.ReportPlugin;
38  import org.codehaus.plexus.util.dag.CycleDetectedException;
39  import org.codehaus.plexus.util.dag.DAG;
40  import org.codehaus.plexus.util.dag.TopologicalSorter;
41  import org.codehaus.plexus.util.dag.Vertex;
42  
43  
44  
45  
46  
47  
48  
49  public class ProjectSorter
50  {
51      private final DAG dag;
52      
53      private final Map projectMap;
54  
55      private final List<MavenProject> sortedProjects;
56  
57      private MavenProject topLevelProject;
58  
59      
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72      public ProjectSorter( List projects )
73          throws CycleDetectedException, DuplicateProjectException, MissingProjectException
74      {
75          this( projects, null, null, false, false );
76      }
77      
78      public ProjectSorter( List projects, List selectedProjectNames, String resumeFrom, boolean make, boolean makeDependents )
79          throws CycleDetectedException, DuplicateProjectException, MissingProjectException
80      {
81          dag = new DAG();
82  
83          projectMap = new HashMap();
84  
85          for ( Iterator i = projects.iterator(); i.hasNext(); )
86          {
87              MavenProject project = (MavenProject) i.next();
88  
89              String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
90  
91              if ( dag.getVertex( id ) != null )
92              {
93                  throw new DuplicateProjectException( "Project '" + id + "' is duplicated in the reactor" );
94              }
95  
96              dag.addVertex( id );
97  
98              projectMap.put( id, project );
99          }
100 
101         for ( Iterator i = projects.iterator(); i.hasNext(); )
102         {
103             MavenProject project = (MavenProject) i.next();
104 
105             String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
106 
107             for ( Iterator j = project.getDependencies().iterator(); j.hasNext(); )
108             {
109                 Dependency dependency = (Dependency) j.next();
110 
111                 String dependencyId = ArtifactUtils
112                     .versionlessKey( dependency.getGroupId(), dependency.getArtifactId() );
113 
114                 if ( dag.getVertex( dependencyId ) != null )
115                 {
116                     project.addProjectReference( (MavenProject) projectMap.get( dependencyId ) );
117 
118                     dag.addEdge( id, dependencyId );
119                 }
120             }
121 
122             MavenProject parent = project.getParent();
123             if ( parent != null )
124             {
125                 String parentId = ArtifactUtils.versionlessKey( parent.getGroupId(), parent.getArtifactId() );
126                 if ( dag.getVertex( parentId ) != null )
127                 {
128                     
129                     if ( dag.hasEdge( parentId, id ) )
130                     {
131                         dag.removeEdge( parentId, id );
132                     }
133                     dag.addEdge( id, parentId );
134                 }
135             }
136 
137             List buildPlugins = project.getBuildPlugins();
138             if ( buildPlugins != null )
139             {
140                 for ( Iterator j = buildPlugins.iterator(); j.hasNext(); )
141                 {
142                     Plugin plugin = (Plugin) j.next();
143                     String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() );
144                     if ( dag.getVertex( pluginId ) != null && !pluginId.equals( id ) )
145                     {
146                         addEdgeWithParentCheck( projectMap, pluginId, project, id );
147                     }
148 
149                     if ( !pluginId.equals( id ) ) {
150                         for ( Iterator k = plugin.getDependencies().iterator(); k.hasNext(); )
151                         {
152                           Dependency dependency = (Dependency) k.next();
153 
154                           String dependencyId = ArtifactUtils
155                               .versionlessKey( dependency.getGroupId(), dependency.getArtifactId() );
156 
157                           if ( dag.getVertex( dependencyId ) != null )
158                           {
159                               
160                               
161                               
162                               
163                               
164                               
165                               
166                               
167                               if ( !id.equals( dependencyId ) )
168                               {
169                                   project.addProjectReference( (MavenProject) projectMap.get( dependencyId ) );
170 
171                                   addEdgeWithParentCheck( projectMap, dependencyId, project, id );
172                                   
173                                   
174                                   
175                                   
176                                   
177                                   
178                                   
179                               }
180                           }
181                        }
182                     }
183                 }
184             }
185 
186             List reportPlugins = project.getReportPlugins();
187             if ( reportPlugins != null )
188             {
189                 for ( Iterator j = reportPlugins.iterator(); j.hasNext(); )
190                 {
191                     ReportPlugin plugin = (ReportPlugin) j.next();
192                     String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() );
193                     if ( dag.getVertex( pluginId ) != null && !pluginId.equals( id ) )
194                     {
195                         addEdgeWithParentCheck( projectMap, pluginId, project, id );
196                     }
197                 }
198             }
199 
200             for ( Iterator j = project.getBuildExtensions().iterator(); j.hasNext(); )
201             {
202                 Extension extension = (Extension) j.next();
203                 String extensionId = ArtifactUtils.versionlessKey( extension.getGroupId(), extension.getArtifactId() );
204                 if ( dag.getVertex( extensionId ) != null )
205                 {
206                     addEdgeWithParentCheck( projectMap, extensionId, project, id );
207                 }
208             }
209         }
210 
211         List sortedProjects = new ArrayList();
212 
213         for ( Iterator i = TopologicalSorter.sort( dag ).iterator(); i.hasNext(); )
214         {
215             String id = (String) i.next();
216 
217             sortedProjects.add( projectMap.get( id ) );
218         }
219         
220         
221         for ( Iterator i = sortedProjects.iterator(); i.hasNext() && topLevelProject == null; )
222         {
223             MavenProject project = (MavenProject) i.next();
224             if ( project.isExecutionRoot() )
225             {
226                 topLevelProject = project;
227             }
228         }
229         
230         sortedProjects = applyMakeFilter( sortedProjects, dag, projectMap, topLevelProject, selectedProjectNames, make, makeDependents );
231         
232         resumeFrom( resumeFrom, sortedProjects, projectMap, topLevelProject );
233 
234         this.sortedProjects = Collections.unmodifiableList( sortedProjects );
235     }
236 
237     
238     private static List applyMakeFilter( List sortedProjects, DAG dag, Map projectMap, MavenProject topLevelProject, List selectedProjectNames, boolean make, boolean makeDependents ) throws MissingProjectException
239     {
240         if ( selectedProjectNames == null ) return sortedProjects;
241         
242         MavenProject[] selectedProjects = new MavenProject[selectedProjectNames.size()];
243         for ( int i = 0; i < selectedProjects.length; i++ )
244         {
245             selectedProjects[i] = findProject( (String) selectedProjectNames.get( i ), projectMap, topLevelProject );
246         }
247         Set projectsToMake = new HashSet( Arrays.asList( selectedProjects ) );
248         for ( int i = 0; i < selectedProjects.length; i++ )
249         {
250             MavenProject project = selectedProjects[i];
251             String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
252             Vertex v = dag.getVertex( id );
253             if ( make )
254             {
255                 gatherDescendents ( v, projectMap, projectsToMake, new HashSet() );
256             }
257             if ( makeDependents )
258             {
259                 gatherAncestors ( v, projectMap, projectsToMake, new HashSet() );
260             }
261         }
262         for ( Iterator i = sortedProjects.iterator(); i.hasNext(); )
263         {
264             MavenProject project = (MavenProject) i.next();
265             if ( !projectsToMake.contains( project ) )
266             {
267                 i.remove();
268             }
269         }
270         return sortedProjects;
271     }
272     
273     private static void resumeFrom( String resumeFrom, List sortedProjects, Map projectMap, MavenProject topLevelProject ) throws MissingProjectException
274     {
275         if ( resumeFrom == null ) return;
276         MavenProject resumeFromProject = findProject( resumeFrom, projectMap, topLevelProject );
277         for ( Iterator i = sortedProjects.iterator(); i.hasNext(); )
278         {
279             MavenProject project = (MavenProject) i.next();
280             if ( resumeFromProject.equals( project ) ) break;
281             i.remove();
282         }
283         if ( sortedProjects.isEmpty() )
284         {
285             throw new MissingProjectException( "Couldn't resume, project was not scheduled to run: " + resumeFrom );
286         }
287     }
288     
289     private static MavenProject findProject( String projectName, Map projectMap, MavenProject topLevelProject ) throws MissingProjectException
290     {
291         MavenProject project = (MavenProject) projectMap.get( projectName );
292         if ( project != null ) return project;
293         
294         File baseDir;
295         if ( topLevelProject == null ) {
296             baseDir = new File( System.getProperty( "user.dir" ) );
297         } else {
298             baseDir = topLevelProject.getBasedir();
299             
300         }
301         
302         File projectDir = new File( baseDir, projectName );
303         if ( !projectDir.exists() ) {
304             throw new MissingProjectException( "Couldn't find specified project dir: " + projectDir.getAbsolutePath() );
305         }
306         if ( !projectDir.isDirectory() ) {
307             throw new MissingProjectException( "Couldn't find specified project dir (not a directory): " + projectDir.getAbsolutePath() );
308         }
309         
310         for ( Iterator i = projectMap.values().iterator(); i.hasNext(); )
311         {
312             project = (MavenProject) i.next();
313             if ( projectDir.equals( project.getFile().getParentFile() ) ) return project;
314         }
315         
316         throw new MissingProjectException( "Couldn't find specified project in module list: " + projectDir.getAbsolutePath() );
317     }
318     
319     private static void gatherDescendents ( Vertex v, Map projectMap, Set out, Set visited )
320     {
321         if ( visited.contains( v ) ) return;
322         visited.add( v );
323         out.add( projectMap.get( v.getLabel() ) );
324         for ( Iterator i = v.getChildren().iterator(); i.hasNext(); )
325         {
326             Vertex child = (Vertex) i.next();
327             gatherDescendents( child, projectMap, out, visited );
328         }
329     }
330     
331     private static void gatherAncestors ( Vertex v, Map projectMap, Set out, Set visited )
332     {
333         if ( visited.contains( v ) ) return;
334         visited.add( v );
335         out.add( projectMap.get( v.getLabel() ) );
336         for ( Iterator i = v.getParents().iterator(); i.hasNext(); )
337         {
338             Vertex parent = (Vertex) i.next();
339             gatherAncestors( parent, projectMap, out, visited );
340         }
341     }
342     
343     private void addEdgeWithParentCheck( Map projectMap, String projectRefId, MavenProject project, String id )
344         throws CycleDetectedException
345     {
346         MavenProject extProject = (MavenProject) projectMap.get( projectRefId );
347         
348         if ( extProject == null )
349         {
350             return;
351         }
352 
353         project.addProjectReference( extProject );
354 
355         MavenProject extParent = extProject.getParent();
356         if ( extParent != null )
357         {
358             String parentId = ArtifactUtils.versionlessKey( extParent.getGroupId(), extParent.getArtifactId() );
359             
360             if ( !dag.hasEdge( projectRefId, id ) || !parentId.equals( id ) )
361             {
362                 dag.addEdge( id, projectRefId );
363             }
364         }
365     }
366 
367     public MavenProject getTopLevelProject()
368     {
369         return topLevelProject;
370     }
371 
372     public List<MavenProject> getSortedProjects()
373     {
374         return sortedProjects;
375     }
376 
377     public boolean hasMultipleProjects()
378     {
379         return sortedProjects.size() > 1;
380     }
381 
382     public List getDependents( String id )
383     {
384         return dag.getParentLabels( id );
385     }
386     
387     public DAG getDAG()
388     {
389         return dag;
390     }
391     
392     public Map getProjectMap()
393     {
394         return projectMap;
395     }
396 }