View Javadoc

1   package org.apache.maven.plugin.reactor;
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 java.util.ArrayList;
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.Plugin;
33  import org.apache.maven.model.ReportPlugin;
34  import org.apache.maven.project.DuplicateProjectException;
35  import org.apache.maven.project.MavenProject;
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  
40  /**
41   * Sort projects by dependencies.  Just like ProjectSorter from maven-project, but this one exposes
42   * the DAG and the projectMap in getters.
43   *  
44   *
45   * @author <a href="mailto:dfabulich@apache.org">Dan Fabulich</a>
46   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
47   */
48  public class SuperProjectSorter
49  {
50      private final DAG dag;
51  
52      private final Map projectMap;
53      
54      private final List sortedProjects;
55  
56      private MavenProject topLevelProject;
57  
58      /**
59       * Sort a list of projects.
60       * <ul>
61       * <li>collect all the vertices for the projects that we want to build.</li>
62       * <li>iterate through the deps of each project and if that dep is within
63       * the set of projects we want to build then add an edge, otherwise throw
64       * the edge away because that dependency is not within the set of projects
65       * we are trying to build. we assume a closed set.</li>
66       * <li>do a topo sort on the graph that remains.</li>
67       * </ul>
68       * @throws DuplicateProjectException if any projects are duplicated by id
69       */
70      public SuperProjectSorter( List projects )
71          throws CycleDetectedException, DuplicateProjectException
72      {
73          dag = new DAG();
74  
75          projectMap = new HashMap();
76  
77          for ( Iterator i = projects.iterator(); i.hasNext(); )
78          {
79              MavenProject project = (MavenProject) i.next();
80  
81              String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
82  
83              if ( dag.getVertex( id ) != null )
84              {
85                  throw new DuplicateProjectException( "Project '" + id + "' is duplicated in the reactor" );
86              }
87  
88              dag.addVertex( id );
89  
90              projectMap.put( id, project );
91          }
92  
93          for ( Iterator i = projects.iterator(); i.hasNext(); )
94          {
95              MavenProject project = (MavenProject) i.next();
96  
97              String id = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
98  
99              for ( Iterator j = project.getDependencies().iterator(); j.hasNext(); )
100             {
101                 Dependency dependency = (Dependency) j.next();
102 
103                 String dependencyId = ArtifactUtils
104                     .versionlessKey( dependency.getGroupId(), dependency.getArtifactId() );
105 
106                 if ( dag.getVertex( dependencyId ) != null )
107                 {
108                     project.addProjectReference( (MavenProject) projectMap.get( dependencyId ) );
109 
110                     dag.addEdge( id, dependencyId );
111                 }
112             }
113 
114             MavenProject parent = project.getParent();
115             if ( parent != null )
116             {
117                 String parentId = ArtifactUtils.versionlessKey( parent.getGroupId(), parent.getArtifactId() );
118                 if ( dag.getVertex( parentId ) != null )
119                 {
120                     // Parent is added as an edge, but must not cause a cycle - so we remove any other edges it has in conflict
121                     if ( dag.hasEdge( parentId, id ) )
122                     {
123                         dag.removeEdge( parentId, id );
124                     }
125                     dag.addEdge( id, parentId );
126                 }
127             }
128 
129             List buildPlugins = project.getBuildPlugins();
130             if ( buildPlugins != null )
131             {
132                 for ( Iterator j = buildPlugins.iterator(); j.hasNext(); )
133                 {
134                     Plugin plugin = (Plugin) j.next();
135                     String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() );
136                     if ( dag.getVertex( pluginId ) != null && !pluginId.equals( id ) )
137                     {
138                         addEdgeWithParentCheck( projectMap, pluginId, project, id );
139                     }
140                 }
141             }
142 
143             List reportPlugins = project.getReportPlugins();
144             if ( reportPlugins != null )
145             {
146                 for ( Iterator j = reportPlugins.iterator(); j.hasNext(); )
147                 {
148                     ReportPlugin plugin = (ReportPlugin) j.next();
149                     String pluginId = ArtifactUtils.versionlessKey( plugin.getGroupId(), plugin.getArtifactId() );
150                     if ( dag.getVertex( pluginId ) != null && !pluginId.equals( id ) )
151                     {
152                         addEdgeWithParentCheck( projectMap, pluginId, project, id );
153                     }
154                 }
155             }
156 
157             for ( Iterator j = project.getBuildExtensions().iterator(); j.hasNext(); )
158             {
159                 Extension extension = (Extension) j.next();
160                 String extensionId = ArtifactUtils.versionlessKey( extension.getGroupId(), extension.getArtifactId() );
161                 if ( dag.getVertex( extensionId ) != null )
162                 {
163                     addEdgeWithParentCheck( projectMap, extensionId, project, id );
164                 }
165             }
166         }
167 
168         List sortedProjects = new ArrayList();
169 
170         for ( Iterator i = TopologicalSorter.sort( dag ).iterator(); i.hasNext(); )
171         {
172             String id = (String) i.next();
173 
174             sortedProjects.add( projectMap.get( id ) );
175         }
176 
177         this.sortedProjects = Collections.unmodifiableList( sortedProjects );
178     }
179 
180     private void addEdgeWithParentCheck( Map projectMap, String projectRefId, MavenProject project, String id )
181         throws CycleDetectedException
182     {
183         MavenProject extProject = (MavenProject) projectMap.get( projectRefId );
184         
185         if ( extProject == null )
186         {
187             return;
188         }
189 
190         project.addProjectReference( extProject );
191 
192         MavenProject extParent = extProject.getParent();
193         if ( extParent != null )
194         {
195             String parentId = ArtifactUtils.versionlessKey( extParent.getGroupId(), extParent.getArtifactId() );
196             // Don't add edge from parent to extension if a reverse edge already exists
197             if ( !dag.hasEdge( projectRefId, id ) || !parentId.equals( id ) )
198             {
199                 dag.addEdge( id, projectRefId );
200             }
201         }
202     }
203 
204     // TODO: !![jc; 28-jul-2005] check this; if we're using '-r' and there are aggregator tasks, this will result in weirdness.
205     public MavenProject getTopLevelProject()
206     {
207         if ( topLevelProject == null )
208         {
209             for ( Iterator i = sortedProjects.iterator(); i.hasNext() && topLevelProject == null; )
210             {
211                 MavenProject project = (MavenProject) i.next();
212                 if ( project.isExecutionRoot() )
213                 {
214                     topLevelProject = project;
215                 }
216             }
217         }
218 
219         return topLevelProject;
220     }
221 
222     public List getSortedProjects()
223     {
224         return sortedProjects;
225     }
226 
227     public boolean hasMultipleProjects()
228     {
229         return sortedProjects.size() > 1;
230     }
231 
232     public List getDependents( String id )
233     {
234         return dag.getParentLabels( id );
235     }
236     
237     public DAG getDAG()
238     {
239         return dag;
240     }
241     
242     public Map getProjectMap()
243     {
244         return projectMap;
245     }
246     
247 }