View Javadoc

1   package org.apache.maven.project;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.artifact.ArtifactUtils;
23  import org.apache.maven.model.Dependency;
24  import org.apache.maven.model.Extension;
25  import org.apache.maven.model.Plugin;
26  import org.apache.maven.model.ReportPlugin;
27  import org.codehaus.plexus.util.dag.CycleDetectedException;
28  import org.codehaus.plexus.util.dag.DAG;
29  import org.codehaus.plexus.util.dag.TopologicalSorter;
30  
31  import java.util.ArrayList;
32  import java.util.Collections;
33  import java.util.HashMap;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
37  
38  /**
39   * Sort projects by dependencies.
40   *
41   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
42   * @version $Id: ProjectSorter.java 495147 2007-01-11 07:47:53Z jvanzyl $
43   */
44  public class ProjectSorter
45  {
46      private final DAG dag;
47  
48      private final List sortedProjects;
49  
50      private MavenProject topLevelProject;
51  
52      /**
53       * Sort a list of projects.
54       * <ul>
55       * <li>collect all the vertices for the projects that we want to build.</li>
56       * <li>iterate through the deps of each project and if that dep is within
57       * the set of projects we want to build then add an edge, otherwise throw
58       * the edge away because that dependency is not within the set of projects
59       * we are trying to build. we assume a closed set.</li>
60       * <li>do a topo sort on the graph that remains.</li>
61       * </ul>
62       * @throws DuplicateProjectException if any projects are duplicated by id
63       */
64      public ProjectSorter( List projects )
65          throws CycleDetectedException, DuplicateProjectException
66      {
67          dag = new DAG();
68  
69          Map projectMap = new HashMap();
70  
71          for ( Iterator i = projects.iterator(); i.hasNext(); )
72          {
73              MavenProject project = (MavenProject) i.next();
74  
75              String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
76  
77              if ( dag.getVertex( id ) != null )
78              {
79                  throw new DuplicateProjectException( "Project '" + id + "' is duplicated in the reactor" );
80              }
81  
82              dag.addVertex( id );
83  
84              projectMap.put( id, project );
85          }
86  
87          for ( Iterator i = projects.iterator(); i.hasNext(); )
88          {
89              MavenProject project = (MavenProject) i.next();
90  
91              String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
92  
93              for ( Iterator j = project.getDependencies().iterator(); j.hasNext(); )
94              {
95                  Dependency dependency = (Dependency) j.next();
96  
97                  String dependencyId = ArtifactUtils
98                      .versionlessKey( dependency.getGroupId(), dependency.getArtifactId() );
99  
100                 if ( dag.getVertex( dependencyId ) != null )
101                 {
102                     project.addProjectReference( (MavenProject) projectMap.get( dependencyId ) );
103 
104                     dag.addEdge( id, dependencyId );
105                 }
106             }
107 
108             MavenProject parent = project.getParent();
109             if ( parent != null )
110             {
111                 String parentId = ArtifactUtils.versionlessKey( parent.getGroupId(), parent.getArtifactId() );
112                 if ( dag.getVertex( parentId ) != null )
113                 {
114                     // Parent is added as an edge, but must not cause a cycle - so we remove any other edges it has in conflict
115                     if ( dag.hasEdge( parentId, id ) )
116                     {
117                         dag.removeEdge( parentId, id );
118                     }
119                     dag.addEdge( id, parentId );
120                 }
121             }
122 
123             List buildPlugins = project.getBuildPlugins();
124             if ( buildPlugins != null )
125             {
126                 for ( Iterator j = buildPlugins.iterator(); j.hasNext(); )
127                 {
128                     Plugin plugin = (Plugin) j.next();
129                     String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() );
130                     if ( dag.getVertex( pluginId ) != null && !pluginId.equals( id ) )
131                     {
132                         addEdgeWithParentCheck( projectMap, pluginId, project, id );
133                     }
134                 }
135             }
136 
137             List reportPlugins = project.getReportPlugins();
138             if ( reportPlugins != null )
139             {
140                 for ( Iterator j = reportPlugins.iterator(); j.hasNext(); )
141                 {
142                     ReportPlugin plugin = (ReportPlugin) 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             }
150 
151             for ( Iterator j = project.getBuildExtensions().iterator(); j.hasNext(); )
152             {
153                 Extension extension = (Extension) j.next();
154                 String extensionId = ArtifactUtils.versionlessKey( extension.getGroupId(), extension.getArtifactId() );
155                 if ( dag.getVertex( extensionId ) != null )
156                 {
157                     addEdgeWithParentCheck( projectMap, extensionId, project, id );
158                 }
159             }
160         }
161 
162         List sortedProjects = new ArrayList();
163 
164         for ( Iterator i = TopologicalSorter.sort( dag ).iterator(); i.hasNext(); )
165         {
166             String id = (String) i.next();
167 
168             sortedProjects.add( projectMap.get( id ) );
169         }
170 
171         this.sortedProjects = Collections.unmodifiableList( sortedProjects );
172     }
173 
174     private void addEdgeWithParentCheck( Map projectMap, String projectRefId, MavenProject project, String id )
175         throws CycleDetectedException
176     {
177         MavenProject extProject = (MavenProject) projectMap.get( projectRefId );
178         
179         if ( extProject == null )
180         {
181             return;
182         }
183 
184         project.addProjectReference( extProject );
185 
186         MavenProject extParent = extProject.getParent();
187         if ( extParent != null )
188         {
189             String parentId = ArtifactUtils.versionlessKey( extParent.getGroupId(), extParent.getArtifactId() );
190             // Don't add edge from parent to extension if a reverse edge already exists
191             if ( !dag.hasEdge( projectRefId, id ) || !parentId.equals( id ) )
192             {
193                 dag.addEdge( id, projectRefId );
194             }
195         }
196     }
197 
198     // TODO: !![jc; 28-jul-2005] check this; if we're using '-r' and there are aggregator tasks, this will result in weirdness.
199     public MavenProject getTopLevelProject()
200     {
201         if ( topLevelProject == null )
202         {
203             for ( Iterator i = sortedProjects.iterator(); i.hasNext() && topLevelProject == null; )
204             {
205                 MavenProject project = (MavenProject) i.next();
206                 if ( project.isExecutionRoot() )
207                 {
208                     topLevelProject = project;
209                 }
210             }
211         }
212 
213         return topLevelProject;
214     }
215 
216     public List getSortedProjects()
217     {
218         return sortedProjects;
219     }
220 
221     public boolean hasMultipleProjects()
222     {
223         return sortedProjects.size() > 1;
224     }
225 
226     public List getDependents( String id )
227     {
228         return dag.getParentLabels( id );
229     }
230 }