Package org.apache.maven.plugin

Source Code of org.apache.maven.plugin.GoalToJellyScriptHousingMapper

package org.apache.maven.plugin;

/* ====================================================================
*   Licensed to the Apache Software Foundation (ASF) under one or more
*   contributor license agreements.  See the NOTICE file distributed with
*   this work for additional information regarding copyright ownership.
*   The ASF licenses this file to You under the Apache License, Version 2.0
*   (the "License"); you may not use this file except in compliance with
*   the License.  You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
* ====================================================================
*/

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.maven.util.InsertionOrderedSet;
import org.apache.maven.werkz.CyclicGoalChainException;
import org.apache.maven.werkz.Goal;
import org.apache.maven.werkz.NoSuchGoalException;
import org.apache.maven.werkz.WerkzProject;
import org.codehaus.plexus.util.StringUtils;

/**
* This is the process that we use:
*
* - Start with goal X
* - Gather prereqs for X
*
* We want to find all goals that will be executed due to X being executed
* we don't care about ordering here because when werkz executes it will sort
* that out for us. We just need to make sure all the appropriate plugins have
* been loaded so that werkz can find the goals it needs.
*
* For a single goal we will collect the set of goals that will be executed with
* it.
*/
public class GoalToJellyScriptHousingMapper
    implements PluginDefinitionHandler
{
    /** Logger */
    private static final Log LOGGER = LogFactory.getLog( GoalToJellyScriptHousingMapper.class );   
   
    /** dyna tag -> plugin map. Where dyna tags live. */
    private HashMap dynaTagPluginMap = new HashMap();

    /** A plugins dyna tag dependencies. */
    private HashMap pluginDynaTagDepsMap = new HashMap();

    /** Goal -> plugin map. */
    private HashMap goalPluginMap = new HashMap();

    /** Goals with a pointer to plugins with callbacks. */
    private HashMap preGoalDecoratorsMap = new HashMap();

    /** Goals with a pointer to plugins with callbacks. */
    private HashMap postGoalDecoratorsMap = new HashMap();

    /** */
    private final WerkzProject goalProject = new WerkzProject();

    /** */
    private String defaultGoalName;

    private final HashSet resolvedPlugins = new HashSet();

    /**
     * Default constructor.
     */
    public GoalToJellyScriptHousingMapper()
    {
    }

    /**
     * Merge parent mapper. Used to setup a transient submapper.
     *
     * @param mapper mapper to copy from
     * @todo figure out which are actually needed, change redundant initialisers by removing here,
     * or adding others to default constructor (eg goalProject init)
     * @todo We should have a way to inherit entire werkz project instead of hokey goal copying
     */
    void merge( GoalToJellyScriptHousingMapper mapper )
        throws CyclicGoalChainException
    {
        mergeMap( dynaTagPluginMap, mapper.dynaTagPluginMap );
        mergeMap( pluginDynaTagDepsMap, mapper.pluginDynaTagDepsMap );
        mergeMap( goalPluginMap, mapper.goalPluginMap );

        for ( Iterator i = mapper.preGoalDecoratorsMap.keySet().iterator(); i.hasNext(); )
        {
            String goalName = (String) i.next();
            Set s = (Set) mapper.preGoalDecoratorsMap.get( goalName );
            Set sExist = (Set) preGoalDecoratorsMap.get( goalName );
            if ( sExist == null )
            {
                preGoalDecoratorsMap.put( goalName, s );
            }
            else
            {
                sExist.addAll( s );
            }
        }
        for ( Iterator i = mapper.postGoalDecoratorsMap.keySet().iterator(); i.hasNext(); )
        {
            String goalName = (String) i.next();
            Set s = (Set) mapper.postGoalDecoratorsMap.get( goalName );
            Set sExist = (Set) postGoalDecoratorsMap.get( goalName );
            if ( sExist == null )
            {
                postGoalDecoratorsMap.put( goalName, s );
            }
            else
            {
                sExist.addAll( s );
            }
        }

        if ( defaultGoalName == null )
        {
            defaultGoalName = mapper.defaultGoalName;
        }

        for ( Iterator i = mapper.goalProject.getGoals().iterator(); i.hasNext(); )
        {
            Goal goal = (Goal) i.next();
            Goal existingGoal = goalProject.getGoal( goal.getName() );
            if ( existingGoal == null )
            {
                goalProject.addGoal( goal );
            }
            else
            {
                for ( Iterator j = goal.getPrecursors().iterator(); j.hasNext(); )
                {
                    existingGoal.addPrecursor( (Goal) j.next() );
                }
                for ( Iterator j = goal.getPostcursors().iterator(); j.hasNext(); )
                {
                    existingGoal.addPostcursor( (Goal) j.next() );
                }
            }
        }
        resolvedPlugins.addAll( mapper.resolvedPlugins );
    }

    /**
     * merge source into target, but don't override anything. Kind of the
     * opposite of putAll to some extent.
     *
     * @todo Surely can be more efficient.
     * @todo if there isn't already a util function for this there should be.
     */
    private static void mergeMap( Map target, Map source )
    {
        Map map = new HashMap( target );
        target.putAll( source );
        target.putAll( map );
    }

    // ----------------------------------------------------------------------
    // Accessors
    // ----------------------------------------------------------------------

    public String getDefaultGoalName()
    {
        return defaultGoalName;
    }

    public void setDefaultGoalName( String defaultGoalName )
    {
        // first definition wins
        if ( this.defaultGoalName == null )
        {
            this.defaultGoalName = defaultGoalName;
        }
    }

    // ----------------------------------------------------------------------
    // Implementation
    // ----------------------------------------------------------------------

    public JellyScriptHousing getPluginHousing( String goal )
    {
        return (JellyScriptHousing) goalPluginMap.get( goal );
    }

    private HashSet getPluginDynaTagDeps( Object pluginHousing )
    {
        HashSet pluginDynaTagDeps = (HashSet) pluginDynaTagDepsMap.get( pluginHousing );

        if ( pluginDynaTagDeps == null )
        {
            pluginDynaTagDeps = new HashSet();
            pluginDynaTagDepsMap.put( pluginHousing, pluginDynaTagDeps );
        }

        return pluginDynaTagDeps;
    }

    HashSet getPostGoalDecorators( String goalName )
    {
        HashSet decorators = (HashSet) postGoalDecoratorsMap.get( goalName );

        if ( decorators == null )
        {
            decorators = new HashSet();
            postGoalDecoratorsMap.put( goalName, decorators );
        }
        return decorators;
    }

    HashSet getPreGoalDecorators( String goalName )
    {
        HashSet decorators = (HashSet) preGoalDecoratorsMap.get( goalName );

        if ( decorators == null )
        {
            decorators = new HashSet();
            preGoalDecoratorsMap.put( goalName, decorators );
        }
        return decorators;
    }

    /**
     * Find the appropriate plugins that provide the give goal and its precursors.
     * Goals such as ${report}:register will need to be resolved lazily.
     *
     * @param goal the goal to find
     * @return the set of plugins
     * @throws NoSuchGoalException if the given goal is in no plugins
     * @see org.apache.maven.jelly.tags.werkz.MavenAttainGoalTag
     */
    Set resolveJellyScriptHousings( String goal )
        throws NoSuchGoalException
    {
        LOGGER.debug( "preparing goal: " + goal );

        Set pluginSet = new InsertionOrderedSet();

        Goal[] chain = getExecutionChain( goal, goalProject );
        LOGGER.debug( "execution chain: " + Arrays.asList( chain ).toString() );

        for ( int i = 0; i < chain.length; i++ )
        {
            Goal g = chain[i];
            Object plugin = goalPluginMap.get( g.getName() );
            if ( plugin == null )
            {
                throw new NoSuchGoalException( g.getName() );
            }
            pluginSet.add( plugin );
            Collection decorators = getPostGoalDecorators( g.getName() );
            if ( LOGGER.isDebugEnabled() && !decorators.isEmpty() )
            {
                LOGGER.debug( "goal " + g.getName() + " has postGoal decorators " + decorators );
            }
            pluginSet.addAll( decorators );
            decorators = getPreGoalDecorators( g.getName() );
            if ( LOGGER.isDebugEnabled() && !decorators.isEmpty() )
            {
                LOGGER.debug( "goal " + g.getName() + " has preGoal decorators " + decorators );
            }
            pluginSet.addAll( decorators );
        }

        // Done like this as the dynatag plugin dependencies must be first in the list
        InsertionOrderedSet newPluginSet = resolveDynaTagPlugins( pluginSet );
        if ( LOGGER.isDebugEnabled() && !newPluginSet.isEmpty() )
        {
            LOGGER.debug( "dynatag dependencies: " + newPluginSet );
        }
        newPluginSet.addAll( pluginSet );
        pluginSet = newPluginSet;

        // Here we make the resolved plugins field be the full set of plugins, and the plugins returned just the
        // additional ones resolved.
        // TODO: skip the remove step - don't add if it is already loaded
        pluginSet.removeAll( resolvedPlugins );
        resolvedPlugins.addAll( pluginSet );

        LOGGER.debug( "final list of plugins to prepare: " + pluginSet );

        return pluginSet;
    }

    /**
     * @todo [1.0] use werkz beta-11 version instead
     */
    public Goal[] getExecutionChain( String name, WerkzProject project )
        throws NoSuchGoalException
    {
        Goal goal = project.getGoal( name );

        LinkedList chain = new LinkedList();
        LinkedList stack = new LinkedList();

        stack.addLast( goal );

        while ( !stack.isEmpty() )
        {
            goal = (Goal) stack.removeFirst();

            if ( ( goal == null ) || chain.contains( goal ) )
            {
                continue;
            }

            chain.addFirst( goal );

            List precursors = goal.getPrecursors();

            for ( Iterator i = precursors.iterator(); i.hasNext(); )
            {
                Goal eachPrecursor = (Goal) i.next();

                if ( !chain.contains( eachPrecursor ) )
                {
                    stack.addLast( eachPrecursor );
                }
            }
        }

        return (Goal[]) chain.toArray( Goal.EMPTY_ARRAY );
    }

    /**
     * Resolve plugins that provide dynamic tags for the given set of plugins.
     *
     * @param plugins the plugins containing tags
     * @return the plugins providing tags, in insertion order
     */
    private InsertionOrderedSet resolveDynaTagPlugins( Set plugins )
    {
        // Important to return an insertion ordered set as the calling function is going to add to it and
        // depends on these remaining first
        InsertionOrderedSet resolvedDynaTagPlugins = new InsertionOrderedSet();

        for ( Iterator i = plugins.iterator(); i.hasNext(); )
        {
            JellyScriptHousing plugin = (JellyScriptHousing) i.next();
            Set dynaTagDeps = getPluginDynaTagDeps( plugin );

            for ( Iterator j = dynaTagDeps.iterator(); j.hasNext(); )
            {
                LinkedList dynaTagUris = new LinkedList();
                Set seen = new HashSet();

                Object next = j.next();
                dynaTagUris.add( next );

                while ( !dynaTagUris.isEmpty() )
                {
                    String dynaTagUri = (String) dynaTagUris.removeFirst();

                    if ( seen.contains( dynaTagUri ) )
                    {
                        continue;
                    }

                    seen.add( dynaTagUri );

                    Object dynaTagPluginHome = dynaTagPluginMap.get( dynaTagUri );
                    if ( dynaTagPluginHome == null )
                    {
                        // This is essentially allowed for bootstrap so
                        // plugins can import taglibs they don't use until later
                        LOGGER.warn( "Tag library requested that is not present: '" + dynaTagUri + "' in plugin: '"
                            + plugin.getName() + "'" );
                    }
                    else
                    {
                        Set set = getPluginDynaTagDeps( dynaTagPluginHome );
                        dynaTagUris.addAll( set );
                        resolvedDynaTagPlugins.add( dynaTagPluginHome );
                    }
                }
            }
        }
        return resolvedDynaTagPlugins;
    }

    // ----------------------------------------------------------------------
    // Handler implementation
    // ----------------------------------------------------------------------

    public void addPluginDynaTagDep( JellyScriptHousing housing, String uri )
    {
        getPluginDynaTagDeps( housing ).add( uri );
    }

    /* (non-Javadoc)
     * @see org.apache.maven.plugin.PluginDefintionHandler#removePluginDynaTagDep(org.apache.maven.plugin.JellyScriptHousing, java.lang.String)
     */
    public void removePluginDynaTagDep( JellyScriptHousing housing, String uri )
    {
        getPluginDynaTagDeps( housing ).remove( uri );
    }

    public void addDynaTagLib( String tagLibUri, JellyScriptHousing jellyScriptHousing )
    {
        dynaTagPluginMap.put( tagLibUri, jellyScriptHousing );
    }

    public void addPostGoal( String name, JellyScriptHousing jellyScriptHousing )
    {
        getPostGoalDecorators( name ).add( jellyScriptHousing );
    }

    public void addPreGoal( String name, JellyScriptHousing jellyScriptHousing )
    {
        getPreGoalDecorators( name ).add( jellyScriptHousing );
    }

    public void addGoal( String name, String prereqs, String description, JellyScriptHousing jellyScriptHousing )
    {
        // We load plugins in order of priority, so don't add goals that already exist.
        if ( !goalPluginMap.containsKey( name ) )
        {
            Goal goal = goalProject.getGoal( name, true );

            goal.setDescription( description );

            goalProject.addGoal( goal );

            // Add to the goal -> plugin map.
            goalPluginMap.put( name, jellyScriptHousing );

            if ( prereqs != null )
            {
                String[] s = StringUtils.split( prereqs, "," );

                for ( int i = 0; i < s.length; i++ )
                {
                    try
                    {
                        Goal prereq = goalProject.getGoal( s[i].trim(), true );
                        goal.addPrecursor( prereq );
                    }
                    catch ( CyclicGoalChainException e )
                    {
                        // do nothing.
                    }
                }
            }
        }
    }

    /**
     * @return goal names
     */
    Set getGoalNames()
    {
        return goalPluginMap.keySet();
    }

    String getGoalDescription( String goalName )
    {
        Goal goal = goalProject.getGoal( goalName );
        String goalDescription = ( goal != null ? goal.getDescription() : null );
        if ( goalDescription != null )
        {
            goalDescription = goalDescription.trim();
            if ( "null".equals( goalDescription ) )
            {
                goalDescription = null;
            }
            else if ( goalDescription.length() == 0 )
            {
                goalDescription = null;
            }
        }
        return goalDescription;
    }

    void addResolvedPlugins( List projectHousings )
    {
        resolvedPlugins.addAll( projectHousings );
    }

    void clearResolvedPlugins()
    {
        resolvedPlugins.clear();
    }

    void invalidatePlugin( JellyScriptHousing housing )
    {
        resolvedPlugins.remove( housing );
        for ( Iterator i = dynaTagPluginMap.keySet().iterator(); i.hasNext(); )
        {
            String uri = (String) i.next();
            if ( dynaTagPluginMap.get( uri ).equals( housing ) )
            {
                i.remove();
            }
        }
        pluginDynaTagDepsMap.remove( housing );

        for ( Iterator i = goalPluginMap.keySet().iterator(); i.hasNext(); )
        {
            String goal = (String) i.next();
            if ( goalPluginMap.get( goal ).equals( housing ) )
            {
                i.remove();
                goalProject.removeGoal( goal );
            }
        }
        for ( Iterator i = preGoalDecoratorsMap.keySet().iterator(); i.hasNext(); )
        {
            String preGoal = (String) i.next();
            Set decorators = (Set) preGoalDecoratorsMap.get( preGoal );
            decorators.remove( housing );
        }
        for ( Iterator i = postGoalDecoratorsMap.keySet().iterator(); i.hasNext(); )
        {
            String postGoal = (String) i.next();
            Set decorators = (Set) postGoalDecoratorsMap.get( postGoal );
            decorators.remove( housing );
        }
    }
}
TOP

Related Classes of org.apache.maven.plugin.GoalToJellyScriptHousingMapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.