001package org.apache.maven.lifecycle;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.Iterator;
026import java.util.LinkedHashMap;
027import java.util.LinkedHashSet;
028import java.util.List;
029import java.util.Map;
030import java.util.Set;
031
032import org.apache.maven.lifecycle.internal.ExecutionPlanItem;
033import org.apache.maven.model.Plugin;
034import org.apache.maven.plugin.MojoExecution;
035import org.apache.maven.plugin.descriptor.MojoDescriptor;
036
037//TODO: lifecycles being executed
038//TODO: what runs in each phase
039//TODO: plugins that need downloading
040//TODO: project dependencies that need downloading
041//TODO: unfortunately the plugins need to be downloaded in order to get the plugin.xml file. need to externalize this
042//      from the plugin archive.
043//TODO: this will be the class that people get in IDEs to modify
044
045public class MavenExecutionPlan
046    implements Iterable<ExecutionPlanItem>
047{
048
049    /*
050       At the moment, this class is totally immutable, and this is in line with thoughts about the
051       pre-calculated execution plan that stays the same during the execution.
052
053       If deciding to add mutable state to this class, it should be at least considered to
054       separate this into a separate mutable structure.
055
056     */
057
058    private final List<ExecutionPlanItem> planItem;
059
060    private final Map<String, ExecutionPlanItem> lastMojoExecutionForAllPhases;
061
062    final List<String> phasesInExecutionPlan;
063
064    public MavenExecutionPlan( List<ExecutionPlanItem> planItem, DefaultLifecycles defaultLifecycles )
065    {
066        this.planItem = planItem;
067
068        lastMojoExecutionForAllPhases = new LinkedHashMap<String, ExecutionPlanItem>();
069
070        LinkedHashSet<String> totalPhaseSet = new LinkedHashSet<String>();
071        if ( defaultLifecycles != null )
072        {
073            for ( String phase : getDistinctPhasesInOrderOfExecutionPlanAppearance( planItem ) )
074            {
075                final Lifecycle lifecycle = defaultLifecycles.get( phase );
076                if ( lifecycle != null )
077                {
078                    totalPhaseSet.addAll( lifecycle.getPhases() );
079                }
080            }
081        }
082        this.phasesInExecutionPlan = new ArrayList<String>( totalPhaseSet );
083
084        Map<String, ExecutionPlanItem> lastInExistingPhases = new HashMap<String, ExecutionPlanItem>();
085        for ( ExecutionPlanItem executionPlanItem : getExecutionPlanItems() )
086        {
087            lastInExistingPhases.put( executionPlanItem.getLifecyclePhase(), executionPlanItem );
088        }
089
090        ExecutionPlanItem lastSeenExecutionPlanItem = null;
091
092        for ( String phase : totalPhaseSet )
093        {
094            ExecutionPlanItem forThisPhase = lastInExistingPhases.get( phase );
095            if ( forThisPhase != null )
096            {
097                lastSeenExecutionPlanItem = forThisPhase;
098            }
099
100            lastMojoExecutionForAllPhases.put( phase, lastSeenExecutionPlanItem );
101        }
102    }
103
104    public Iterator<ExecutionPlanItem> iterator()
105    {
106        return getExecutionPlanItems().iterator();
107    }
108
109    /**
110     * Returns the last ExecutionPlanItem in the supplied phase. If no items are in the specified phase,
111     * the closest executionPlanItem from an earlier phase item will be returned.
112     *
113     * @param requestedPhase the requested phase
114     *                       The execution plan item
115     * @return The ExecutionPlanItem or null if none can be found
116     */
117    public ExecutionPlanItem findLastInPhase( String requestedPhase )
118    {
119        return lastMojoExecutionForAllPhases.get( requestedPhase );
120    }
121
122    private List<ExecutionPlanItem> getExecutionPlanItems()
123    {
124        return planItem;
125    }
126
127    private static Iterable<String> getDistinctPhasesInOrderOfExecutionPlanAppearance(
128        List<ExecutionPlanItem> planItems )
129    {
130        LinkedHashSet<String> result = new LinkedHashSet<String>();
131        for ( ExecutionPlanItem executionPlanItem : planItems )
132        {
133            final String phase = executionPlanItem.getLifecyclePhase();
134            if ( !result.contains( phase ) )
135            {
136                result.add( phase );
137            }
138        }
139        return result;
140    }
141
142    public void forceAllComplete()
143    {
144        for ( ExecutionPlanItem executionPlanItem : getExecutionPlanItems() )
145        {
146            executionPlanItem.forceComplete();
147        }
148    }
149
150    public void waitUntilAllDone()
151        throws InterruptedException
152    {
153        for ( ExecutionPlanItem executionPlanItem : getExecutionPlanItems() )
154        {
155            executionPlanItem.waitUntilDone();
156        }
157    }
158
159    public boolean containsPhase( String phase )
160    {
161        return phasesInExecutionPlan.contains( phase );
162    }
163
164    public List<MojoExecution> getMojoExecutions()
165    {
166        List<MojoExecution> result = new ArrayList<MojoExecution>();
167        for ( ExecutionPlanItem executionPlanItem : planItem )
168        {
169            result.add( executionPlanItem.getMojoExecution() );
170        }
171        return result;
172    }
173
174    /**
175     * Get set of plugins having a goal/mojo used but not marked @threadSafe
176     *
177     * @return the set of plugins (without info on which goal is concerned)
178     */
179    public Set<Plugin> getNonThreadSafePlugins()
180    {
181        Set<Plugin> plugins = new HashSet<Plugin>();
182        for ( ExecutionPlanItem executionPlanItem : planItem )
183        {
184            final MojoExecution mojoExecution = executionPlanItem.getMojoExecution();
185            if ( !mojoExecution.getMojoDescriptor().isThreadSafe() )
186            {
187                plugins.add( mojoExecution.getPlugin() );
188            }
189        }
190        return plugins;
191    }
192
193    /**
194     * Get set of mojos used but not marked @threadSafe
195     *
196     * @return the set of mojo descriptors
197     */
198    public Set<MojoDescriptor> getNonThreadSafeMojos()
199    {
200        Set<MojoDescriptor> mojos = new HashSet<MojoDescriptor>();
201        for ( ExecutionPlanItem executionPlanItem : planItem )
202        {
203            final MojoExecution mojoExecution = executionPlanItem.getMojoExecution();
204            if ( !mojoExecution.getMojoDescriptor().isThreadSafe() )
205            {
206                mojos.add( mojoExecution.getMojoDescriptor() );
207            }
208        }
209        return mojos;
210    }
211
212    // Used by m2e but will be removed, really.
213    @Deprecated
214    public List<MojoExecution> getExecutions()
215    {
216        return getMojoExecutions();
217    }
218
219    public int size()
220    {
221        return planItem.size();
222    }
223
224}