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<>();
069
070        LinkedHashSet<String> totalPhaseSet = new LinkedHashSet<>();
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<>( totalPhaseSet );
083
084        Map<String, ExecutionPlanItem> lastInExistingPhases = new HashMap<>();
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<>();
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 List<MojoExecution> getMojoExecutions()
143    {
144        List<MojoExecution> result = new ArrayList<>();
145        for ( ExecutionPlanItem executionPlanItem : planItem )
146        {
147            result.add( executionPlanItem.getMojoExecution() );
148        }
149        return result;
150    }
151
152    /**
153     * Get set of plugins having a goal/mojo used but not marked @threadSafe
154     *
155     * @return the set of plugins (without info on which goal is concerned)
156     */
157    public Set<Plugin> getNonThreadSafePlugins()
158    {
159        Set<Plugin> plugins = new HashSet<>();
160        for ( ExecutionPlanItem executionPlanItem : planItem )
161        {
162            final MojoExecution mojoExecution = executionPlanItem.getMojoExecution();
163            if ( !mojoExecution.getMojoDescriptor().isThreadSafe() )
164            {
165                plugins.add( mojoExecution.getPlugin() );
166            }
167        }
168        return plugins;
169    }
170
171    /**
172     * Get set of mojos used but not marked @threadSafe
173     *
174     * @return the set of mojo descriptors
175     */
176    public Set<MojoDescriptor> getNonThreadSafeMojos()
177    {
178        Set<MojoDescriptor> mojos = new HashSet<>();
179        for ( ExecutionPlanItem executionPlanItem : planItem )
180        {
181            final MojoExecution mojoExecution = executionPlanItem.getMojoExecution();
182            if ( !mojoExecution.getMojoDescriptor().isThreadSafe() )
183            {
184                mojos.add( mojoExecution.getMojoDescriptor() );
185            }
186        }
187        return mojos;
188    }
189
190    // Used by m2e but will be removed, really.
191    @Deprecated
192    public List<MojoExecution> getExecutions()
193    {
194        return getMojoExecutions();
195    }
196
197    public int size()
198    {
199        return planItem.size();
200    }
201
202}