001    package 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    
022    import java.util.ArrayList;
023    import java.util.HashMap;
024    import java.util.HashSet;
025    import java.util.Iterator;
026    import java.util.LinkedHashMap;
027    import java.util.LinkedHashSet;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.Set;
031    
032    import org.apache.maven.lifecycle.internal.ExecutionPlanItem;
033    import org.apache.maven.model.Plugin;
034    import org.apache.maven.plugin.MojoExecution;
035    
036    //TODO: lifecycles being executed
037    //TODO: what runs in each phase
038    //TODO: plugins that need downloading
039    //TODO: project dependencies that need downloading
040    //TODO: unfortunately the plugins need to be downloaded in order to get the plugin.xml file. need to externalize this
041    //      from the plugin archive.
042    //TODO: this will be the class that people get in IDEs to modify
043    
044    public class MavenExecutionPlan
045        implements Iterable<ExecutionPlanItem>
046    {
047    
048        /*
049           At the moment, this class is totally immutable, and this is in line with thoughts about the
050           pre-calculated execution plan that stays the same during the execution.
051    
052           If deciding to add mutable state to this class, it should be at least considered to
053           separate this into a separate mutable structure.
054    
055         */
056    
057        private final List<ExecutionPlanItem> planItem;
058    
059        private final Map<String, ExecutionPlanItem> lastMojoExecutionForAllPhases;
060    
061        final List<String> phasesInExecutionPlan;
062    
063        public MavenExecutionPlan( List<ExecutionPlanItem> planItem, DefaultLifecycles defaultLifecycles )
064        {
065            this.planItem = planItem;
066    
067            lastMojoExecutionForAllPhases = new LinkedHashMap<String, ExecutionPlanItem>();
068    
069            LinkedHashSet<String> totalPhaseSet = new LinkedHashSet<String>();
070            if ( defaultLifecycles != null )
071            {
072                for ( String phase : getDistinctPhasesInOrderOfExecutionPlanAppearance( planItem ) )
073                {
074                    final Lifecycle lifecycle = defaultLifecycles.get( phase );
075                    if ( lifecycle != null )
076                    {
077                        totalPhaseSet.addAll( lifecycle.getPhases() );
078                    }
079                }
080            }
081            this.phasesInExecutionPlan = new ArrayList<String>( totalPhaseSet );
082    
083            Map<String, ExecutionPlanItem> lastInExistingPhases = new HashMap<String, ExecutionPlanItem>();
084            for ( ExecutionPlanItem executionPlanItem : getExecutionPlanItems() )
085            {
086                lastInExistingPhases.put( executionPlanItem.getLifecyclePhase(), executionPlanItem );
087            }
088    
089            ExecutionPlanItem lastSeenExecutionPlanItem = null;
090    
091            for ( String phase : totalPhaseSet )
092            {
093                ExecutionPlanItem forThisPhase = lastInExistingPhases.get( phase );
094                if ( forThisPhase != null )
095                {
096                    lastSeenExecutionPlanItem = forThisPhase;
097                }
098    
099                lastMojoExecutionForAllPhases.put( phase, lastSeenExecutionPlanItem );
100            }
101        }
102    
103        public Iterator<ExecutionPlanItem> iterator()
104        {
105            return getExecutionPlanItems().iterator();
106        }
107    
108        /**
109         * Returns the last ExecutionPlanItem in the supplied phase. If no items are in the specified phase,
110         * the closest executionPlanItem from an earlier phase item will be returned.
111         *
112         * @param requestedPhase the requested phase
113         *                       The execution plan item
114         * @return The ExecutionPlanItem or null if none can be found
115         */
116        public ExecutionPlanItem findLastInPhase( String requestedPhase )
117        {
118            return lastMojoExecutionForAllPhases.get( requestedPhase );
119        }
120    
121        private List<ExecutionPlanItem> getExecutionPlanItems()
122        {
123            return planItem;
124        }
125    
126        private static Iterable<String> getDistinctPhasesInOrderOfExecutionPlanAppearance(
127            List<ExecutionPlanItem> planItems )
128        {
129            LinkedHashSet<String> result = new LinkedHashSet<String>();
130            for ( ExecutionPlanItem executionPlanItem : planItems )
131            {
132                final String phase = executionPlanItem.getLifecyclePhase();
133                if ( !result.contains( phase ) )
134                {
135                    result.add( phase );
136                }
137            }
138            return result;
139        }
140    
141        public void forceAllComplete()
142        {
143            for ( ExecutionPlanItem executionPlanItem : getExecutionPlanItems() )
144            {
145                executionPlanItem.forceComplete();
146            }
147        }
148    
149        public void waitUntilAllDone()
150            throws InterruptedException
151        {
152            for ( ExecutionPlanItem executionPlanItem : getExecutionPlanItems() )
153            {
154                executionPlanItem.waitUntilDone();
155            }
156        }
157    
158        public boolean containsPhase( String phase )
159        {
160            return phasesInExecutionPlan.contains( phase );
161        }
162    
163        public List<MojoExecution> getMojoExecutions()
164        {
165            List<MojoExecution> result = new ArrayList<MojoExecution>();
166            for ( ExecutionPlanItem executionPlanItem : planItem )
167            {
168                result.add( executionPlanItem.getMojoExecution() );
169            }
170            return result;
171        }
172    
173    
174        public Set<Plugin> getNonThreadSafePlugins()
175        {
176            Set<Plugin> plugins = new HashSet<Plugin>();
177            for ( ExecutionPlanItem executionPlanItem : planItem )
178            {
179                final MojoExecution mojoExecution = executionPlanItem.getMojoExecution();
180                if ( !mojoExecution.getMojoDescriptor().isThreadSafe() )
181                {
182                    plugins.add( mojoExecution.getPlugin() );
183                }
184            }
185            return plugins;
186        }
187    
188        // Used by m2e but will be removed, really.
189        @Deprecated
190        public List<MojoExecution> getExecutions()
191        {
192            return getMojoExecutions();
193        }
194    
195        public int size()
196        {
197            return planItem.size();
198        }
199    
200    }