package org.apache.maven.reporting.exec;

/*
 * 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.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.maven.lifecycle.LifecycleExecutor;
import org.apache.maven.model.Build;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.MavenPluginManager;
import org.apache.maven.plugin.Mojo;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoNotFoundException;
import org.apache.maven.plugin.PluginConfigurationException;
import org.apache.maven.plugin.PluginContainerException;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
import org.apache.maven.plugin.version.PluginVersionRequest;
import org.apache.maven.plugin.version.PluginVersionResolutionException;
import org.apache.maven.plugin.version.PluginVersionResolver;
import org.apache.maven.plugin.version.PluginVersionResult;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.MavenReport;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.logging.Logger;
import org.apache.maven.shared.utils.StringUtils;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomUtils;

/**
 * <p>
 * This component will build some {@link MavenReportExecution} from {@link MavenReportExecutorRequest}. If a
 * {@link MavenReport} needs to fork a lifecycle, this fork is executed here. It will ask the core to get some
 * informations in order to correctly setup {@link MavenReport}.
 * </p>
 * <p>
 * <b>Note</b> if no version is defined in the report plugin, the version will be searched with
 * {@link #resolvePluginVersion(ReportPlugin, MavenReportExecutorRequest) resolvePluginVersion(...)} method:
 * <ol>
 * <li>use the one defined in the reportPlugin configuration,</li>
 * <li>search similar (same groupId and artifactId) plugin in the build/plugins section of the pom,</li>
 * <li>search similar (same groupId and artifactId) plugin in the build/pluginManagement section of the pom,</li>
 * <li>ask {@link PluginVersionResolver} to get a fallback version (display a warning as it's not a recommended use).
 * </li>
 * </ol>
 * </p>
 * <p>
 * Following steps are done:
 * <ul>
 * <li>get {@link PluginDescriptor} from the {@link MavenPluginManager} (through
 * {@link MavenPluginManagerHelper#getPluginDescriptor(Plugin, org.apache.maven.execution.MavenSession)
 * MavenPluginManagerHelper.getPluginDescriptor(...)} to protect from core API change)</li>
 * <li>setup a {@link ClassLoader}, with the Site plugin classloader as parent for the report execution. <br>
 * Notice that some classes are imported from the current Site plugin ClassRealm: see {@link #IMPORTS}. Corresponding
 * artifacts are excluded from the artifact resolution: <code>doxia-site-renderer</code>, <code>doxia-sink-api</code>
 *  and <code>maven-reporting-api</code>.<br>
 * Work is done using {@link MavenPluginManager} (through
 * {@link MavenPluginManagerHelper#setupPluginRealm(PluginDescriptor, MavenSession, ClassLoader, List, List)
 * MavenPluginManagerHelper.setupPluginRealm(...)} to protect from core API change)</li>
 * <li>setup the mojo using {@link MavenPluginManager#getConfiguredMojo(Class, MavenSession, MojoExecution)
 * MavenPluginManager.getConfiguredMojo(...)}</li>
 * <li>verify with {@link LifecycleExecutor#calculateForkedExecutions(MojoExecution, MavenSession)
 * LifecycleExecutor.calculateForkedExecutions(...)} if any forked execution is needed: if yes, execute the forked
 * execution here</li>
 * </ul>
 * </p>
 * 
 * @author Olivier Lamy
 */
@Component( role = MavenReportExecutor.class )
public class DefaultMavenReportExecutor
    implements MavenReportExecutor
{
    @Requirement
    private Logger logger;

    @Requirement
    protected MavenPluginManager mavenPluginManager;

    @Requirement
    protected MavenPluginManagerHelper mavenPluginManagerHelper;

    @Requirement
    protected LifecycleExecutor lifecycleExecutor;

    @Requirement
    protected PluginVersionResolver pluginVersionResolver;

    private static final List<String> IMPORTS = Arrays.asList( "org.apache.maven.reporting.MavenReport",
                                                               "org.apache.maven.reporting.MavenMultiPageReport",
                                                               "org.apache.maven.doxia.siterenderer.Renderer",
                                                               "org.apache.maven.doxia.sink.SinkFactory",
                                                               "org.codehaus.doxia.sink.Sink",
                                                               "org.apache.maven.doxia.sink.Sink",
                                                               "org.apache.maven.doxia.sink.SinkEventAttributes",
                                                               "org.apache.maven.doxia.logging.LogEnabled",
                                                               "org.apache.maven.doxia.logging.Log" );

    private static final List<String> EXCLUDES = Arrays.asList( "doxia-site-renderer", "doxia-sink-api",
                                                                "maven-reporting-api" );

    public List<MavenReportExecution> buildMavenReports( MavenReportExecutorRequest mavenReportExecutorRequest )
        throws MojoExecutionException
    {
        if ( mavenReportExecutorRequest.getReportPlugins() == null )
        {
            return Collections.emptyList();
        }
        getLog().debug( "DefaultMavenReportExecutor.buildMavenReports()" );

        Set<String> reportPluginKeys = new HashSet<String>();
        List<MavenReportExecution> reportExecutions = new ArrayList<MavenReportExecution>();

        String pluginKey = "";
        try
        {
            for ( ReportPlugin reportPlugin : mavenReportExecutorRequest.getReportPlugins() )
            {
                pluginKey = reportPlugin.getGroupId() + ':' + reportPlugin.getArtifactId();

                if ( !reportPluginKeys.add( pluginKey ) )
                {
                    logger.info( "plugin " + pluginKey + " will be executed more than one time" );
                }

                reportExecutions.addAll( buildReportPlugin( mavenReportExecutorRequest, reportPlugin ) );
            }
        }
        catch ( Exception e )
        {
            throw new MojoExecutionException( "failed to get report for " + pluginKey, e );
        }

        return reportExecutions;
    }

    protected List<MavenReportExecution> buildReportPlugin( MavenReportExecutorRequest mavenReportExecutorRequest,
                                                            ReportPlugin reportPlugin )
        throws Exception
    {
        // step 1: prepare the plugin
        Plugin plugin = new Plugin();
        plugin.setGroupId( reportPlugin.getGroupId() );
        plugin.setArtifactId( reportPlugin.getArtifactId() );
        plugin.setVersion( resolvePluginVersion( reportPlugin, mavenReportExecutorRequest ) );
        logger.info( "configuring report plugin " + plugin.getId() );

        mergePluginToReportPlugin( mavenReportExecutorRequest, plugin, reportPlugin );

        PluginDescriptor pluginDescriptor =
            mavenPluginManagerHelper.getPluginDescriptor( plugin, mavenReportExecutorRequest.getMavenSession() );

        // step 2: prepare the goals
        List<GoalWithConf> goalsWithConfiguration = new ArrayList<GoalWithConf>();
        boolean hasUserDefinedReports = prepareGoals( reportPlugin, pluginDescriptor, goalsWithConfiguration );

        // step 3: prepare the reports
        List<MavenReportExecution> reports = new ArrayList<MavenReportExecution>();
        for ( GoalWithConf report : goalsWithConfiguration )
        {
            MavenReportExecution mavenReportExecution =
                prepareReportExecution( mavenReportExecutorRequest, report, hasUserDefinedReports );

            if ( mavenReportExecution != null )
            {
                // ok, report is ready to generate
                reports.add( mavenReportExecution );
            }
        }

        if ( !reports.isEmpty() )
        {
            // log reports, either configured or detected
            StringBuilder buff = new StringBuilder();
            for ( MavenReportExecution mre : reports )
            {
                if ( buff.length() > 0 )
                {
                    buff.append( ", " );
                }
                buff.append( mre.getGoal() );
            }
            logger.info( reports.size() + " report" + ( reports.size() > 1 ? "s" : "" ) + " "
                + ( hasUserDefinedReports ? "configured" : "detected" ) + " for " + plugin.getArtifactId() + ":"
                + plugin.getVersion() + ": " + buff );
        }

        return reports;
    }

    private boolean prepareGoals( ReportPlugin reportPlugin, PluginDescriptor pluginDescriptor,
                               List<GoalWithConf> goalsWithConfiguration )
    {
        if ( reportPlugin.getReportSets().isEmpty() && reportPlugin.getReports().isEmpty() )
        {
            // by default, use every goal, which will be filtered later to only keep reporting goals
            List<MojoDescriptor> mojoDescriptors = pluginDescriptor.getMojos();
            for ( MojoDescriptor mojoDescriptor : mojoDescriptors )
            {
                goalsWithConfiguration.add( new GoalWithConf( reportPlugin, pluginDescriptor, mojoDescriptor.getGoal(),
                                                              mojoDescriptor.getConfiguration() ) );
            }

            return false;
        }

        Set<String> goals = new HashSet<String>();
        for ( String report : reportPlugin.getReports() )
        {
            if ( goals.add( report ) )
            {
                goalsWithConfiguration.add( new GoalWithConf( reportPlugin, pluginDescriptor, report,
                                                              reportPlugin.getConfiguration() ) );
            }
            else
            {
                logger.warn( report + " report is declared twice in default reports" );
            }
        }

        for ( ReportSet reportSet : reportPlugin.getReportSets() )
        {
            goals = new HashSet<String>();
            for ( String report : reportSet.getReports() )
            {
                if ( goals.add( report ) )
                {
                    goalsWithConfiguration.add( new GoalWithConf( reportPlugin, pluginDescriptor, report,
                                                                  reportSet.getConfiguration() ) );
                }
                else
                {
                    logger.warn( report + " report is declared twice in " + reportSet.getId() + " reportSet" );
                }
            }
        }

        return true;
    }

    private MavenReportExecution prepareReportExecution( MavenReportExecutorRequest mavenReportExecutorRequest,
                                                         GoalWithConf report, boolean hasUserDefinedReports )
        throws Exception
    {
        ReportPlugin reportPlugin = report.getReportPlugin();
        PluginDescriptor pluginDescriptor = report.getPluginDescriptor();

        MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( report.getGoal() );
        if ( mojoDescriptor == null )
        {
            throw new MojoNotFoundException( report.getGoal(), pluginDescriptor );
        }

        MavenProject project = mavenReportExecutorRequest.getProject();
        if ( !hasUserDefinedReports && mojoDescriptor.isAggregator() && !canAggregate( project ) )
        {
            // aggregator mojos automatically added from plugin are only run at execution root
            return null;
        }

        MojoExecution mojoExecution = new MojoExecution( pluginDescriptor.getPlugin(), report.getGoal(), null );

        mojoExecution.setMojoDescriptor( mojoDescriptor );

        mavenPluginManagerHelper.setupPluginRealm( pluginDescriptor, mavenReportExecutorRequest.getMavenSession(),
                                                   Thread.currentThread().getContextClassLoader(), IMPORTS,
                                                   EXCLUDES );

        if ( !isMavenReport( mojoExecution, pluginDescriptor ) )
        {
            if ( hasUserDefinedReports )
            {
                // reports were explicitly written in the POM
                logger.warn( "ignoring " + mojoExecution.getPlugin().getId() + ':' + report.getGoal()
                    + " goal since it is not a report: should be removed from reporting configuration in POM" );
            }
            return null;
        }

        Xpp3Dom pluginMgmtConfiguration = null;
        if ( project.getBuild() != null && project.getBuild().getPluginManagement() != null )
        {
            Plugin pluginMgmt = find( reportPlugin, project.getBuild().getPluginManagement().getPlugins() );

            if ( pluginMgmt != null )
            {
                pluginMgmtConfiguration = (Xpp3Dom) pluginMgmt.getConfiguration();
            }
        }

        mojoExecution.setConfiguration( mergeConfiguration( mojoDescriptor.getMojoConfiguration(),
                                                            pluginMgmtConfiguration,
                                                            reportPlugin.getConfiguration(),
                                                            report.getConfiguration(),
                                                            mojoDescriptor.getParameterMap().keySet() ) );

        MavenReport mavenReport =
            getConfiguredMavenReport( mojoExecution, pluginDescriptor, mavenReportExecutorRequest );

        MavenReportExecution mavenReportExecution =
            new MavenReportExecution( report.getGoal(), mojoExecution.getPlugin(), mavenReport,
                                      pluginDescriptor.getClassRealm() );

        lifecycleExecutor.calculateForkedExecutions( mojoExecution,
                                                     mavenReportExecutorRequest.getMavenSession() );

        if ( !mojoExecution.getForkedExecutions().isEmpty() )
        {
            String reportDescription = pluginDescriptor.getArtifactId() + ":" + report.getGoal() + " report";

            String execution;
            if ( StringUtils.isNotEmpty( mojoDescriptor.getExecutePhase() ) )
            {
                // forked phase
                execution = "'"
                    + ( StringUtils.isEmpty( mojoDescriptor.getExecuteLifecycle() ) ? ""
                                    : ( '[' + mojoDescriptor.getExecuteLifecycle() + ']' ) )
                    + mojoDescriptor.getExecutePhase() + "' forked phase execution";
            }
            else
            {
                // forked goal
                execution = "'" + mojoDescriptor.getExecuteGoal() + "' forked goal execution";
            }

            logger.info( "preparing " + reportDescription + " requires " + execution );

            lifecycleExecutor.executeForkedExecutions( mojoExecution,
                                                       mavenReportExecutorRequest.getMavenSession() );

            logger.info( execution + " for " + reportDescription + " preparation done" );
        }

        return mavenReportExecution;
    }

    private boolean canAggregate( MavenProject project )
    {
        return project.isExecutionRoot() && "pom".equals( project.getPackaging() ) && ( project.getModules() != null )
            && !project.getModules().isEmpty();
    }

    private MavenReport getConfiguredMavenReport( MojoExecution mojoExecution, PluginDescriptor pluginDescriptor,
                                                  MavenReportExecutorRequest mavenReportExecutorRequest )
        throws PluginContainerException, PluginConfigurationException
    {
        try
        {
            Mojo mojo =
                mavenPluginManager.getConfiguredMojo( Mojo.class, mavenReportExecutorRequest.getMavenSession(),
                                                      mojoExecution );

            return (MavenReport) mojo;
        }
        catch ( ClassCastException e )
        {
            getLog().warn( "skip ClassCastException " + e.getMessage() );
            return null;
        }
        catch ( PluginContainerException e )
        {
            /**
             * ignore old plugin which are using removed PluginRegistry [INFO] Caused by:
             * java.lang.NoClassDefFoundError: org/apache/maven/plugin/registry/PluginRegistry
             */
            if ( e.getCause() != null && e.getCause() instanceof NoClassDefFoundError
                && e.getMessage().contains( "PluginRegistry" ) )
            {
                getLog().warn( "skip NoClassDefFoundError with PluginRegistry " );
                // too noisy, only in debug mode + e.getMessage() );
                if ( getLog().isDebugEnabled() )
                {
                    getLog().debug( e.getMessage(), e );
                }
                return null;
            }
            throw e;
        }
    }

    private boolean isMavenReport( MojoExecution mojoExecution, PluginDescriptor pluginDescriptor )
    {
        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();

        // get the plugin's goal Mojo class
        Class<?> mojoClass;
        try
        {
            Thread.currentThread().setContextClassLoader( mojoExecution.getMojoDescriptor().getRealm() );

            mojoClass =
                pluginDescriptor.getClassRealm().loadClass( mojoExecution.getMojoDescriptor().getImplementation() );
        }
        catch ( ClassNotFoundException e )
        {
            getLog().warn( "skip ClassNotFoundException mojoExecution.goal '" + mojoExecution.getGoal() + "': "
                               + e.getMessage(), e );
            return false;
        }
        finally
        {
            Thread.currentThread().setContextClassLoader( originalClassLoader );
        }

        // check if it is a report
        try
        {
            Thread.currentThread().setContextClassLoader( mojoExecution.getMojoDescriptor().getRealm() );
            MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( mojoExecution.getGoal() );

            boolean isMavenReport = MavenReport.class.isAssignableFrom( mojoClass );

            if ( getLog().isDebugEnabled() )
            {
                if ( mojoDescriptor != null && mojoDescriptor.getImplementationClass() != null )
                {
                    getLog().debug( "class " + mojoDescriptor.getImplementationClass().getName() + " isMavenReport: "
                                        + isMavenReport );
                }

                if ( !isMavenReport )
                {
                    getLog().debug( "skip non MavenReport " + mojoExecution.getMojoDescriptor().getId() );
                }
            }

            return isMavenReport;
        }
        catch ( LinkageError e )
        {
            getLog().warn( "skip LinkageError mojoExecution.goal '" + mojoExecution.getGoal() + "': " + e.getMessage(),
                           e );
            return false;
        }
        finally
        {
            Thread.currentThread().setContextClassLoader( originalClassLoader );
        }
    }

    /**
     * Merge plugin configuration and reportset configuration to mojo configuration to get effective
     * mojo configuration.
     *
     * @param mojoConf configuration done at mojo descriptor level
     * @param pluginMgmtConfig configuration done at build.pluginManagement level
     * @param pluginConf configuration done at reporting plugin level
     * @param reportSetConf configuration done at reportSet level
     * @param parameters set of supported parameters: any other parameter will be removed
     * @return the effective configuration to be used
     */
    private Xpp3Dom mergeConfiguration( PlexusConfiguration mojoConf, Xpp3Dom pluginMgmtConfig,
                                        PlexusConfiguration pluginConf, PlexusConfiguration reportSetConf,
                                        Set<String> parameters )
    {
        Xpp3Dom mojoConfig = ( mojoConf != null ) ? convert( mojoConf ) : new Xpp3Dom( "configuration" );

        if ( pluginMgmtConfig != null || pluginConf != null || reportSetConf != null )
        {
            Xpp3Dom pluginConfig = ( pluginConf == null ) ? new Xpp3Dom( "fake" ) : convert( pluginConf );

            // merge pluginConf into reportSetConf
            Xpp3Dom mergedConfig = Xpp3DomUtils.mergeXpp3Dom( convert( reportSetConf ), pluginConfig );
            // then merge pluginMgmtConfig
            mergedConfig = Xpp3DomUtils.mergeXpp3Dom( mergedConfig, pluginMgmtConfig );
            // then merge mojoConf
            mergedConfig = Xpp3DomUtils.mergeXpp3Dom( mergedConfig, mojoConfig );

            // clean result
            Xpp3Dom cleanedConfig = new Xpp3Dom( "configuration" );
            if ( mergedConfig.getChildren() != null )
            {
                for ( Xpp3Dom parameter : mergedConfig.getChildren() )
                {
                    if ( parameters.contains( parameter.getName() ) )
                    {
                        cleanedConfig.addChild( parameter );
                    }
                }
            }

            mojoConfig = cleanedConfig;
        }

        return mojoConfig;
    }

    private Xpp3Dom convert( PlexusConfiguration config )
    {
        if ( config == null )
        {
            return null;
        }

        Xpp3Dom dom = new Xpp3Dom( config.getName() );
        dom.setValue( config.getValue( null ) );

        for ( String attrib : config.getAttributeNames() )
        {
            dom.setAttribute( attrib, config.getAttribute( attrib, null ) );
        }

        for ( int n = config.getChildCount(), i = 0; i < n; i++ )
        {
            dom.addChild( convert( config.getChild( i ) ) );
        }

        return dom;
    }

    private Logger getLog()
    {
        return logger;
    }

    /**
     * Resolve report plugin version. Steps to find a plugin version stop after each step if a non <code>null</code>
     * value has been found:
     * <ol>
     * <li>use the one defined in the reportPlugin configuration,</li>
     * <li>search similar (same groupId and artifactId) mojo in the build/plugins section of the pom,</li>
     * <li>search similar (same groupId and artifactId) mojo in the build/pluginManagement section of the pom,</li>
     * <li>ask {@link PluginVersionResolver} to get a fallback version and display a warning as it's not a recommended
     * use.</li>
     * </ol>
     * 
     * @param reportPlugin the report plugin to resolve the version
     * @param mavenReportExecutorRequest the current report execution context
     * @return the report plugin version
     * @throws PluginVersionResolutionException
     */
    protected String resolvePluginVersion( ReportPlugin reportPlugin,
                                           MavenReportExecutorRequest mavenReportExecutorRequest )
        throws PluginVersionResolutionException
    {
        String reportPluginKey = reportPlugin.getGroupId() + ':' + reportPlugin.getArtifactId();
        if ( getLog().isDebugEnabled() )
        {
            getLog().debug( "resolving version for " + reportPluginKey );
        }

        // look for version defined in the reportPlugin configuration
        if ( reportPlugin.getVersion() != null )
        {
            if ( getLog().isDebugEnabled() )
            {
                logger.debug( "resolved " + reportPluginKey + " version from the reporting.plugins section: "
                    + reportPlugin.getVersion() );
            }
            return reportPlugin.getVersion();
        }

        MavenProject project = mavenReportExecutorRequest.getProject();

        // search in the build section
        if ( project.getBuild() != null )
        {
            Plugin plugin = find( reportPlugin, project.getBuild().getPlugins() );

            if ( plugin != null && plugin.getVersion() != null )
            {
                if ( getLog().isDebugEnabled() )
                {
                    logger.debug( "resolved " + reportPluginKey + " version from the build.plugins section: "
                        + plugin.getVersion() );
                }
                return plugin.getVersion();
            }
        }

        // search in pluginManagement section
        if ( project.getBuild() != null && project.getBuild().getPluginManagement() != null )
        {
            Plugin plugin = find( reportPlugin, project.getBuild().getPluginManagement().getPlugins() );

            if ( plugin != null && plugin.getVersion() != null )
            {
                if ( getLog().isDebugEnabled() )
                {
                    logger.debug( "resolved " + reportPluginKey
                        + " version from the build.pluginManagement.plugins section: " + plugin.getVersion() );
                }
                return plugin.getVersion();
            }
        }

        logger.warn( "Report plugin " + reportPluginKey + " has an empty version." );
        logger.warn( "" );
        logger.warn( "It is highly recommended to fix these problems"
            + " because they threaten the stability of your build." );
        logger.warn( "" );
        logger.warn( "For this reason, future Maven versions might no"
            + " longer support building such malformed projects." );

        Plugin plugin = new Plugin();
        plugin.setGroupId( reportPlugin.getGroupId() );
        plugin.setArtifactId( reportPlugin.getArtifactId() );

        PluginVersionRequest pluginVersionRequest =
            new DefaultPluginVersionRequest( plugin, mavenReportExecutorRequest.getMavenSession() );

        PluginVersionResult result = pluginVersionResolver.resolve( pluginVersionRequest );
        if ( getLog().isDebugEnabled() )
        {
            getLog().debug( "resolved " + reportPluginKey + " version from repository: " + result.getVersion() );
        }
        return result.getVersion();
    }

    /**
     * Search similar (same groupId and artifactId) plugin as a given report plugin.
     * 
     * @param reportPlugin the report plugin to search for a similar plugin
     * @param plugins the candidate plugins
     * @return the first similar plugin
     */
    private Plugin find( ReportPlugin reportPlugin, List<Plugin> plugins )
    {
        if ( plugins == null )
        {
            return null;
        }
        for ( Plugin plugin : plugins )
        {
            if ( StringUtils.equals( plugin.getArtifactId(), reportPlugin.getArtifactId() )
                && StringUtils.equals( plugin.getGroupId(), reportPlugin.getGroupId() ) )
            {
                return plugin;
            }
        }
        return null;
    }

    /**
     * TODO other stuff to merge ?
     * <p>
     * this method will "merge" some part of the plugin declaration existing in the build section to the fake plugin
     * build for report execution:
     * <ul>
     * <li>dependencies</li>
     * </ul>
     * </p>
     * The plugin could only be present in the dependency management section.
     * 
     * @param mavenReportExecutorRequest
     * @param buildPlugin
     * @param reportPlugin
     */
    private void mergePluginToReportPlugin( MavenReportExecutorRequest mavenReportExecutorRequest, Plugin buildPlugin,
                                            ReportPlugin reportPlugin )
    {
        Build build = mavenReportExecutorRequest.getProject().getBuild();
        Plugin configuredPlugin = find( reportPlugin, build.getPlugins() );
        if ( configuredPlugin == null && build.getPluginManagement() != null )
        {
            configuredPlugin = find( reportPlugin, build.getPluginManagement().getPlugins() );
        }
        if ( configuredPlugin != null )
        {
            if ( !configuredPlugin.getDependencies().isEmpty() )
            {
                buildPlugin.getDependencies().addAll( configuredPlugin.getDependencies() );
            }
        }
    }

    private static class GoalWithConf
    {
        private final String goal;

        private final PlexusConfiguration configuration;

        private final ReportPlugin reportPlugin;

        private final PluginDescriptor pluginDescriptor;

        GoalWithConf( ReportPlugin reportPlugin, PluginDescriptor pluginDescriptor, String goal,
                             PlexusConfiguration configuration )
        {
            this.reportPlugin = reportPlugin;
            this.pluginDescriptor = pluginDescriptor;
            this.goal = goal;
            this.configuration = configuration;
        }

        public ReportPlugin getReportPlugin()
        {
            return reportPlugin;
        }

        public PluginDescriptor getPluginDescriptor()
        {
            return pluginDescriptor;
        }

        public String getGoal()
        {
            return goal;
        }

        public PlexusConfiguration getConfiguration()
        {
            return configuration;
        }
    }
}
