org.codehaus.mojo.versions.DisplayPluginUpdatesMojo.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.mojo.versions.DisplayPluginUpdatesMojo.java

Source

package org.codehaus.mojo.versions;

/*
 * 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 org.apache.maven.BuildFailureException;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.RuntimeInformation;
import org.apache.maven.lifecycle.Lifecycle;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.lifecycle.LifecycleExecutor;
import org.apache.maven.lifecycle.mapping.LifecycleMapping;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.Prerequisites;
import org.apache.maven.model.Profile;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.apache.maven.plugin.InvalidPluginException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.PluginManager;
import org.apache.maven.plugin.PluginManagerException;
import org.apache.maven.plugin.PluginNotFoundException;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugin.version.PluginVersionNotFoundException;
import org.apache.maven.plugin.version.PluginVersionResolutionException;
import org.apache.maven.project.DefaultProjectBuilderConfiguration;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.interpolation.ModelInterpolationException;
import org.apache.maven.project.interpolation.ModelInterpolator;
import org.apache.maven.settings.Settings;
import org.codehaus.mojo.versions.api.ArtifactVersions;
import org.codehaus.mojo.versions.api.PomHelper;
import org.codehaus.mojo.versions.ordering.MavenVersionComparator;
import org.codehaus.mojo.versions.rewriting.ModifiedPomXMLEventReader;
import org.codehaus.mojo.versions.utils.PluginComparator;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.Stack;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Pattern;

/**
 * Displays all plugins that have newer versions available.
 *
 * @author Stephen Connolly
 * @goal display-plugin-updates
 * @requiresProject true
 * @requiresDirectInvocation false
 * @since 1.0-alpha-1
 */
public class DisplayPluginUpdatesMojo extends AbstractVersionsDisplayMojo {

    // ------------------------------ FIELDS ------------------------------

    /**
     * The width to pad warn messages.
     *
     * @since 1.0-alpha-1
     */
    private static final int WARN_PAD_SIZE = 65;

    /**
     * The width to pad info messages.
     *
     * @since 1.0-alpha-1
     */
    private static final int INFO_PAD_SIZE = 68;

    /**
     * String to flag a plugin version being forced by the super-pom.
     *
     * @since 1.0-alpha-1
     */
    private static final String FROM_SUPER_POM = "(from super-pom) ";

    /**
     * @component
     * @since 1.0-alpha-1
     */
    private LifecycleExecutor lifecycleExecutor;

    /**
     * @component
     * @since 1.0-alpha-3
     */
    private ModelInterpolator modelInterpolator;

    /**
     * The plugin manager.
     *
     * @component
     * @since 1.0-alpha-1
     */
    private PluginManager pluginManager;

    /**
     * @component
     * @since 1.3
     */
    private RuntimeInformation runtimeInformation;

    // --------------------- GETTER / SETTER METHODS ---------------------

    /**
     * Returns the pluginManagement section of the super-pom.
     *
     * @return Returns the pluginManagement section of the super-pom.
     * @throws MojoExecutionException when things go wrong.
     */
    private Map<String, String> getSuperPomPluginManagement() throws MojoExecutionException {
        if (new DefaultArtifactVersion("3.0").compareTo(runtimeInformation.getApplicationVersion()) <= 0) {
            getLog().debug("Using Maven 3.x strategy to determine superpom defined plugins");
            try {
                Method getPluginsBoundByDefaultToAllLifecycles = LifecycleExecutor.class
                        .getMethod("getPluginsBoundByDefaultToAllLifecycles", new Class[] { String.class });
                Set<Plugin> plugins = (Set<Plugin>) getPluginsBoundByDefaultToAllLifecycles
                        .invoke(lifecycleExecutor, new Object[] { getProject().getPackaging() });
                // we need to provide a copy with the version blanked out so that inferring from super-pom
                // works as for 2.x as 3.x fills in the version on us!
                Map<String, String> result = new LinkedHashMap<String, String>(plugins.size());
                for (Plugin plugin : plugins) {
                    result.put(getPluginCoords(plugin), getPluginVersion(plugin));
                }
                URL superPom = getClass().getClassLoader().getResource("org/apache/maven/model/pom-4.0.0.xml");
                if (superPom != null) {
                    try {
                        Reader reader = ReaderFactory.newXmlReader(superPom);
                        try {
                            StringBuilder buf = new StringBuilder(IOUtil.toString(reader));
                            ModifiedPomXMLEventReader pom = newModifiedPomXER(buf);

                            Pattern pathRegex = Pattern.compile("/project(/profiles/profile)?"
                                    + "((/build(/pluginManagement)?)|(/reporting))" + "/plugins/plugin");
                            Stack<StackState> pathStack = new Stack<StackState>();
                            StackState curState = null;
                            while (pom.hasNext()) {
                                XMLEvent event = pom.nextEvent();
                                if (event.isStartDocument()) {
                                    curState = new StackState("");
                                    pathStack.clear();
                                } else if (event.isStartElement()) {
                                    String elementName = event.asStartElement().getName().getLocalPart();
                                    if (curState != null && pathRegex.matcher(curState.path).matches()) {
                                        if ("groupId".equals(elementName)) {
                                            curState.groupId = pom.getElementText().trim();
                                            continue;
                                        } else if ("artifactId".equals(elementName)) {
                                            curState.artifactId = pom.getElementText().trim();
                                            continue;

                                        } else if ("version".equals(elementName)) {
                                            curState.version = pom.getElementText().trim();
                                            continue;
                                        }
                                    }

                                    pathStack.push(curState);
                                    curState = new StackState(curState.path + "/" + elementName);
                                } else if (event.isEndElement()) {
                                    if (curState != null && pathRegex.matcher(curState.path).matches()) {
                                        if (curState.artifactId != null) {
                                            Plugin plugin = new Plugin();
                                            plugin.setArtifactId(curState.artifactId);
                                            plugin.setGroupId(curState.groupId == null
                                                    ? PomHelper.APACHE_MAVEN_PLUGINS_GROUPID
                                                    : curState.groupId);
                                            plugin.setVersion(curState.version);
                                            if (!result.containsKey(getPluginCoords(plugin))) {
                                                result.put(getPluginCoords(plugin), getPluginVersion(plugin));
                                            }
                                        }
                                    }
                                    curState = pathStack.pop();
                                }
                            }
                        } finally {
                            IOUtil.close(reader);
                        }
                    } catch (IOException e) {
                        // ignore
                    } catch (XMLStreamException e) {
                        // ignore
                    }
                }

                return result;
            } catch (NoSuchMethodException e1) {
                // no much we can do here
            } catch (InvocationTargetException e1) {
                // no much we can do here
            } catch (IllegalAccessException e1) {
                // no much we can do here
            }
        }
        getLog().debug("Using Maven 2.x strategy to determine superpom defined plugins");
        Map<String, String> superPomPluginManagement = new HashMap();
        try {
            MavenProject superProject = projectBuilder
                    .buildStandaloneSuperProject(new DefaultProjectBuilderConfiguration());
            superPomPluginManagement.putAll(getPluginManagement(superProject.getOriginalModel()));
        } catch (ProjectBuildingException e) {
            throw new MojoExecutionException("Could not determine the super pom.xml", e);
        }
        return superPomPluginManagement;
    }

    /**
     * Gets the plugin management plugins of a specific project.
     *
     * @param model the model to get the plugin management plugins from.
     * @return The map of effective plugin versions keyed by coordinates.
     * @since 1.0-alpha-1
     */
    private Map<String, String> getPluginManagement(Model model) {
        // we want only those parts of pluginManagement that are defined in this project
        Map<String, String> pluginManagement = new HashMap<String, String>();
        try {
            for (Plugin plugin : model.getBuild().getPluginManagement().getPlugins()) {
                String coord = getPluginCoords(plugin);
                String version = getPluginVersion(plugin);
                if (version != null) {
                    pluginManagement.put(coord, version);
                }
            }
        } catch (NullPointerException e) {
            // guess there are no plugins here
        }
        try {
            for (Profile profile : model.getProfiles()) {
                try {
                    for (Plugin plugin : profile.getBuild().getPluginManagement().getPlugins()) {
                        String coord = getPluginCoords(plugin);
                        String version = getPluginVersion(plugin);
                        if (version != null) {
                            pluginManagement.put(coord, version);
                        }
                    }
                } catch (NullPointerException e) {
                    // guess there are no plugins here
                }
            }
        } catch (NullPointerException e) {
            // guess there are no profiles here
        }

        return pluginManagement;
    }

    // ------------------------ INTERFACE METHODS ------------------------

    // --------------------- Interface Mojo ---------------------

    /**
     * @throws MojoExecutionException when things go wrong
     * @throws MojoFailureException   when things go wrong in a very bad way
     * @see AbstractVersionsUpdaterMojo#execute()
     * @since 1.0-alpha-1
     */
    public void execute() throws MojoExecutionException, MojoFailureException {
        logInit();
        Set<String> pluginsWithVersionsSpecified;
        try {
            pluginsWithVersionsSpecified = findPluginsWithVersionsSpecified(getProject());
        } catch (XMLStreamException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        } catch (IOException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }

        Map<String, String> superPomPluginManagement = getSuperPomPluginManagement();
        getLog().debug("superPom plugins = " + superPomPluginManagement);

        Map<String, String> parentPluginManagement = new HashMap<String, String>();
        Map<String, String> parentBuildPlugins = new HashMap<String, String>();
        Map<String, String> parentReportPlugins = new HashMap<String, String>();

        List<MavenProject> parents = getParentProjects(getProject());

        for (MavenProject parentProject : parents) {
            getLog().debug("Processing parent: " + parentProject.getGroupId() + ":" + parentProject.getArtifactId()
                    + ":" + parentProject.getVersion() + " -> " + parentProject.getFile());

            StringWriter writer = new StringWriter();
            boolean havePom = false;
            Model interpolatedModel;
            try {
                Model originalModel = parentProject.getOriginalModel();
                if (originalModel == null) {
                    getLog().warn("project.getOriginalModel()==null for  " + parentProject.getGroupId() + ":"
                            + parentProject.getArtifactId() + ":" + parentProject.getVersion()
                            + " is null, substituting project.getModel()");
                    originalModel = parentProject.getModel();
                }
                try {
                    new MavenXpp3Writer().write(writer, originalModel);
                    writer.close();
                    havePom = true;
                } catch (IOException e) {
                    // ignore
                }
                interpolatedModel = modelInterpolator.interpolate(originalModel, null,
                        new DefaultProjectBuilderConfiguration()
                                .setExecutionProperties(getProject().getProperties()),
                        false);
            } catch (ModelInterpolationException e) {
                throw new MojoExecutionException(e.getMessage(), e);
            }
            if (havePom) {
                try {
                    Set<String> withVersionSpecified = findPluginsWithVersionsSpecified(
                            new StringBuilder(writer.toString()));
                    Map<String, String> map = getPluginManagement(interpolatedModel);
                    map.keySet().retainAll(withVersionSpecified);
                    parentPluginManagement.putAll(map);

                    map = getBuildPlugins(interpolatedModel, true);
                    map.keySet().retainAll(withVersionSpecified);
                    parentPluginManagement.putAll(map);

                    map = getReportPlugins(interpolatedModel, true);
                    map.keySet().retainAll(withVersionSpecified);
                    parentPluginManagement.putAll(map);
                } catch (IOException e) {
                    throw new MojoExecutionException(e.getMessage(), e);
                } catch (XMLStreamException e) {
                    throw new MojoExecutionException(e.getMessage(), e);
                }
            } else {
                parentPluginManagement.putAll(getPluginManagement(interpolatedModel));
                parentPluginManagement.putAll(getBuildPlugins(interpolatedModel, true));
                parentPluginManagement.putAll(getReportPlugins(interpolatedModel, true));
            }
        }

        Set<Plugin> plugins = getProjectPlugins(superPomPluginManagement, parentPluginManagement,
                parentBuildPlugins, parentReportPlugins, pluginsWithVersionsSpecified);
        List<String> updates = new ArrayList<String>();
        List<String> lockdowns = new ArrayList<String>();
        Map<ArtifactVersion, Map<String, String>> upgrades = new TreeMap<ArtifactVersion, Map<String, String>>(
                new MavenVersionComparator());
        ArtifactVersion curMavenVersion = runtimeInformation.getApplicationVersion();
        ArtifactVersion specMavenVersion = new DefaultArtifactVersion(getRequiredMavenVersion(getProject(), "2.0"));
        ArtifactVersion minMavenVersion = null;
        boolean superPomDrivingMinVersion = false;
        Iterator<Plugin> i = plugins.iterator();
        while (i.hasNext()) {
            Object plugin = i.next();
            String groupId = getPluginGroupId(plugin);
            String artifactId = getPluginArtifactId(plugin);
            String version = getPluginVersion(plugin);
            String coords = ArtifactUtils.versionlessKey(groupId, artifactId);

            if (version == null) {
                version = parentPluginManagement.get(coords);
            }
            getLog().debug(new StringBuilder().append("Checking ").append(coords).append(" for updates newer than ")
                    .append(version).toString());
            String effectiveVersion = version;

            VersionRange versionRange;
            boolean unspecified = version == null;
            try {
                versionRange = unspecified ? VersionRange.createFromVersionSpec("[0,)")
                        : VersionRange.createFromVersionSpec(version);
            } catch (InvalidVersionSpecificationException e) {
                throw new MojoExecutionException("Invalid version range specification: " + version, e);
            }

            Artifact artifact = artifactFactory.createPluginArtifact(groupId, artifactId, versionRange);

            ArtifactVersion artifactVersion = null;
            try {
                // now we want to find the newest version that is compatible with the invoking version of Maven
                ArtifactVersions artifactVersions = getHelper().lookupArtifactVersions(artifact, true);
                ArtifactVersion[] newerVersions = artifactVersions
                        .getVersions(Boolean.TRUE.equals(this.allowSnapshots));
                ArtifactVersion minRequires = null;
                for (int j = newerVersions.length - 1; j >= 0; j--) {
                    Artifact probe = artifactFactory.createDependencyArtifact(groupId, artifactId,
                            VersionRange.createFromVersion(newerVersions[j].toString()), "pom", null, "runtime");
                    try {
                        getHelper().resolveArtifact(probe, true);
                        MavenProject mavenProject = projectBuilder.buildFromRepository(probe,
                                remotePluginRepositories, localRepository);
                        ArtifactVersion requires = new DefaultArtifactVersion(
                                getRequiredMavenVersion(mavenProject, "2.0"));
                        if (specMavenVersion.compareTo(requires) >= 0 && artifactVersion == null) {
                            artifactVersion = newerVersions[j];
                        }
                        if (effectiveVersion == null && curMavenVersion.compareTo(requires) >= 0) {
                            // version was unspecified, current version of maven thinks it should use this
                            effectiveVersion = newerVersions[j].toString();
                        }
                        if (artifactVersion != null && effectiveVersion != null) {
                            // no need to look at any older versions.
                            break;
                        }
                        if (minRequires == null || minRequires.compareTo(requires) > 0) {
                            Map<String, String> upgradePlugins = upgrades.get(requires);
                            if (upgradePlugins == null) {
                                upgrades.put(requires, upgradePlugins = new LinkedHashMap<String, String>());
                            }
                            String upgradePluginKey = compactKey(groupId, artifactId);
                            if (!upgradePlugins.containsKey(upgradePluginKey)) {
                                upgradePlugins.put(upgradePluginKey, newerVersions[j].toString());
                            }
                            minRequires = requires;
                        }
                    } catch (ArtifactResolutionException e) {
                        // ignore bad version
                    } catch (ArtifactNotFoundException e) {
                        // ignore bad version
                    } catch (ProjectBuildingException e) {
                        // ignore bad version
                    }
                }
                if (effectiveVersion != null) {
                    VersionRange currentVersionRange = VersionRange.createFromVersion(effectiveVersion);
                    Artifact probe = artifactFactory.createDependencyArtifact(groupId, artifactId,
                            currentVersionRange, "pom", null, "runtime");
                    try {
                        getHelper().resolveArtifact(probe, true);
                        MavenProject mavenProject = projectBuilder.buildFromRepository(probe,
                                remotePluginRepositories, localRepository);
                        ArtifactVersion requires = new DefaultArtifactVersion(
                                getRequiredMavenVersion(mavenProject, "2.0"));
                        if (minMavenVersion == null || minMavenVersion.compareTo(requires) < 0) {
                            minMavenVersion = requires;
                        }
                    } catch (ArtifactResolutionException e) {
                        // ignore bad version
                    } catch (ArtifactNotFoundException e) {
                        // ignore bad version
                    } catch (ProjectBuildingException e) {
                        // ignore bad version
                    }
                }
            } catch (ArtifactMetadataRetrievalException e) {
                throw new MojoExecutionException(e.getMessage(), e);
            }

            String newVersion;

            if (version == null && pluginsWithVersionsSpecified.contains(coords)) {
                // Hack ALERT!
                //
                // All this should be re-written in a less "pom is xml" way... but it'll
                // work for now :-(
                //
                // we have removed the version information, as it was the same as from
                // the super-pom... but it actually was specified.
                version = artifactVersion != null ? artifactVersion.toString() : null;
            }

            getLog().debug("[" + coords + "].version=" + version);
            getLog().debug("[" + coords + "].artifactVersion=" + artifactVersion);
            getLog().debug("[" + coords + "].effectiveVersion=" + effectiveVersion);
            getLog().debug("[" + coords + "].specified=" + pluginsWithVersionsSpecified.contains(coords));
            if (version == null || !pluginsWithVersionsSpecified.contains(coords)) {
                version = (String) superPomPluginManagement.get(ArtifactUtils.versionlessKey(artifact));
                getLog().debug("[" + coords + "].superPom.version=" + version);

                newVersion = artifactVersion != null ? artifactVersion.toString()
                        : (version != null ? version : (effectiveVersion != null ? effectiveVersion : "(unknown)"));
                StringBuilder buf = new StringBuilder(compactKey(groupId, artifactId));
                buf.append(' ');
                int padding = WARN_PAD_SIZE - newVersion.length() - (version != null ? FROM_SUPER_POM.length() : 0);
                while (buf.length() < padding) {
                    buf.append('.');
                }
                buf.append(' ');
                if (version != null) {
                    buf.append(FROM_SUPER_POM);
                    superPomDrivingMinVersion = true;
                }
                buf.append(newVersion);
                lockdowns.add(buf.toString());
            } else if (artifactVersion != null) {
                newVersion = artifactVersion.toString();
            } else {
                newVersion = null;
            }
            if (version != null && artifactVersion != null && newVersion != null && effectiveVersion != null
                    && new DefaultArtifactVersion(effectiveVersion)
                            .compareTo(new DefaultArtifactVersion(newVersion)) < 0) {
                StringBuilder buf = new StringBuilder(compactKey(groupId, artifactId));
                buf.append(' ');
                int padding = INFO_PAD_SIZE - version.length() - newVersion.length() - 4;
                while (buf.length() < padding) {
                    buf.append('.');
                }
                buf.append(' ');
                buf.append(effectiveVersion);
                buf.append(" -> ");
                buf.append(newVersion);
                updates.add(buf.toString());
            }
        }
        logLine(false, "");
        if (updates.isEmpty()) {
            logLine(false, "All plugins with a version specified are using the latest versions.");
        } else {
            logLine(false, "The following plugin updates are available:");
            for (String update : updates) {
                logLine(false, "  " + update);
            }
        }
        logLine(false, "");
        if (lockdowns.isEmpty()) {
            logLine(false, "All plugins have a version specified.");
        } else {
            getLog().warn("The following plugins do not have their version specified:");
            for (String lockdown : lockdowns) {
                getLog().warn("  " + lockdown);
            }
        }
        logLine(false, "");
        boolean noMavenMinVersion = getRequiredMavenVersion(getProject(), null) == null;
        boolean noExplicitMavenMinVersion = getProject().getPrerequisites() == null
                || getProject().getPrerequisites().getMaven() == null;
        if (noMavenMinVersion) {
            getLog().warn("Project does not define minimum Maven version, default is: 2.0");
        } else if (noExplicitMavenMinVersion) {
            logLine(false, "Project inherits minimum Maven version as: " + specMavenVersion);
        } else {
            ArtifactVersion explicitMavenVersion = new DefaultArtifactVersion(
                    getProject().getPrerequisites().getMaven());
            if (explicitMavenVersion.compareTo(specMavenVersion) < 0) {
                logLine(true, "Project's effective minimum Maven (from parent) is: " + specMavenVersion);
                logLine(true, "Project defines minimum Maven version as: " + explicitMavenVersion);
            } else {
                logLine(false, "Project defines minimum Maven version as: " + specMavenVersion);
            }
        }
        logLine(false, "Plugins require minimum Maven version of: " + minMavenVersion);
        if (superPomDrivingMinVersion) {
            logLine(false, "Note: the super-pom from Maven " + curMavenVersion + " defines some of the plugin");
            logLine(false, "      versions and may be influencing the plugins required minimum Maven");
            logLine(false, "      version.");
        }
        logLine(false, "");
        if ("maven-plugin".equals(getProject().getPackaging())) {
            if (noMavenMinVersion) {
                getLog().warn(
                        "Project (which is a Maven Plugin) does not define required minimum version of Maven.");
                getLog().warn("Update the pom.xml to contain");
                getLog().warn("    <prerequisites>");
                getLog().warn("      <maven><!-- minimum version of Maven that the plugin works with --></maven>");
                getLog().warn("    </prerequisites>");
                getLog().warn("To build this plugin you need at least Maven " + minMavenVersion);
                getLog().warn(
                        "A Maven Enforcer rule can be used to enforce this if you have not already set one up");
            } else if (minMavenVersion != null && specMavenVersion.compareTo(minMavenVersion) < 0) {
                getLog().warn("Project (which is a Maven Plugin) targets Maven " + specMavenVersion + " or newer");
                getLog().warn("but requires Maven " + minMavenVersion + " or newer to build.");
                getLog().warn("This may or may not be a problem. A Maven Enforcer rule can help ");
                getLog().warn("enforce that the correct version of Maven is used to build this plugin.");
            } else {
                logLine(false, "No plugins require a newer version of Maven than specified by the pom.");
            }
        } else {
            if (noMavenMinVersion) {
                logLine(true, "Project does not define required minimum version of Maven.");
                logLine(true, "Update the pom.xml to contain");
                logLine(true, "    <prerequisites>");
                logLine(true, "      <maven>" + minMavenVersion + "</maven>");
                logLine(true, "    </prerequisites>");
            } else if (minMavenVersion != null && specMavenVersion.compareTo(minMavenVersion) < 0) {
                logLine(true, "Project requires an incorrect minimum version of Maven.");
                logLine(true, "Either change plugin versions to those compatible with " + specMavenVersion);
                logLine(true, "or update the pom.xml to contain");
                logLine(true, "    <prerequisites>");
                logLine(true, "      <maven>" + minMavenVersion + "</maven>");
                logLine(true, "    </prerequisites>");
            } else {
                logLine(false, "No plugins require a newer version of Maven than specified by the pom.");
            }
        }
        for (Map.Entry<ArtifactVersion, Map<String, String>> mavenUpgrade : upgrades.entrySet()) {
            ArtifactVersion mavenUpgradeVersion = (ArtifactVersion) mavenUpgrade.getKey();
            Map<String, String> upgradePlugins = mavenUpgrade.getValue();
            if (upgradePlugins.isEmpty() || specMavenVersion.compareTo(mavenUpgradeVersion) >= 0) {
                continue;
            }
            logLine(false, "");
            logLine(false, "Require Maven " + mavenUpgradeVersion + " to use the following plugin updates:");
            for (Map.Entry<String, String> entry : upgradePlugins.entrySet()) {
                StringBuilder buf = new StringBuilder("  ");
                buf.append(entry.getKey());
                buf.append(' ');
                String s = entry.getValue();
                int padding = INFO_PAD_SIZE - s.length() + 2;
                while (buf.length() < padding) {
                    buf.append('.');
                }
                buf.append(' ');
                buf.append(s);
                logLine(false, buf.toString());
            }
        }
        logLine(false, "");
    }

    private String compactKey(String groupId, String artifactId) {
        if (PomHelper.APACHE_MAVEN_PLUGINS_GROUPID.equals(groupId)) {
            // a core plugin... group id is not needed
            return artifactId;
        }
        return groupId + ":" + artifactId;
    }

    private String getRequiredMavenVersion(MavenProject mavenProject, String defaultValue) {
        ArtifactVersion requiredMavenVersion = null;
        while (mavenProject != null) {
            final Prerequisites prerequisites = mavenProject.getPrerequisites();
            final String mavenVersion = prerequisites == null ? null : prerequisites.getMaven();
            if (mavenVersion != null) {
                final ArtifactVersion v = new DefaultArtifactVersion(mavenVersion);
                if (requiredMavenVersion == null || requiredMavenVersion.compareTo(v) < 0) {
                    requiredMavenVersion = v;
                }
            }
            mavenProject = mavenProject.getParent();
        }
        return requiredMavenVersion == null ? defaultValue : requiredMavenVersion.toString();
    }

    private static final class StackState {
        private final String path;

        private String groupId;

        private String artifactId;

        private String version;

        public StackState(String path) {
            this.path = path;
        }

        public String toString() {
            return path + "[groupId=" + groupId + ", artifactId=" + artifactId + ", version=" + version + "]";
        }
    }

    /**
     * Returns a set of Strings which correspond to the plugin coordinates where there is a version
     * specified.
     *
     * @param project The project to get the plugins with versions specified.
     * @return a set of Strings which correspond to the plugin coordinates where there is a version
     *         specified.
     */
    private Set<String> findPluginsWithVersionsSpecified(MavenProject project)
            throws IOException, XMLStreamException {
        return findPluginsWithVersionsSpecified(PomHelper.readXmlFile(project.getFile()));
    }

    /**
     * Returns a set of Strings which correspond to the plugin coordinates where there is a version
     * specified.
     *
     * @param pomContents The project to get the plugins with versions specified.
     * @return a set of Strings which correspond to the plugin coordinates where there is a version
     *         specified.
     */
    private Set<String> findPluginsWithVersionsSpecified(StringBuilder pomContents)
            throws IOException, XMLStreamException {
        Set<String> result = new HashSet<String>();
        ModifiedPomXMLEventReader pom = newModifiedPomXER(pomContents);

        Pattern pathRegex = Pattern.compile(
                "/project(/profiles/profile)?" + "((/build(/pluginManagement)?)|(/reporting))" + "/plugins/plugin");
        Stack<StackState> pathStack = new Stack<StackState>();
        StackState curState = null;
        while (pom.hasNext()) {
            XMLEvent event = pom.nextEvent();
            if (event.isStartDocument()) {
                curState = new StackState("");
                pathStack.clear();
            } else if (event.isStartElement()) {
                String elementName = event.asStartElement().getName().getLocalPart();
                if (curState != null && pathRegex.matcher(curState.path).matches()) {
                    if ("groupId".equals(elementName)) {
                        curState.groupId = pom.getElementText().trim();
                        continue;
                    } else if ("artifactId".equals(elementName)) {
                        curState.artifactId = pom.getElementText().trim();
                        continue;

                    } else if ("version".equals(elementName)) {
                        curState.version = pom.getElementText().trim();
                        continue;
                    }
                }

                pathStack.push(curState);
                curState = new StackState(curState.path + "/" + elementName);
            } else if (event.isEndElement()) {
                if (curState != null && pathRegex.matcher(curState.path).matches()) {
                    if (curState.artifactId != null && curState.version != null) {
                        if (curState.groupId == null) {
                            curState.groupId = PomHelper.APACHE_MAVEN_PLUGINS_GROUPID;
                        }
                        result.add(curState.groupId + ":" + curState.artifactId);
                    }
                }
                curState = pathStack.pop();
            }
        }

        return result;

    }

    // -------------------------- OTHER METHODS --------------------------

    /**
     * Gets the build plugins of a specific project.
     *
     * @param model                the model to get the build plugins from.
     * @param onlyIncludeInherited <code>true</code> to only return the plugins definitions that will be
     *                             inherited by child projects.
     * @return The map of effective plugin versions keyed by coordinates.
     * @since 1.0-alpha-1
     */
    private Map<String, String> getBuildPlugins(Model model, boolean onlyIncludeInherited) {
        Map<String, String> buildPlugins = new HashMap();
        try {
            for (Plugin plugin : model.getBuild().getPlugins()) {
                String coord = getPluginCoords(plugin);
                String version = getPluginVersion(plugin);
                if (version != null && (!onlyIncludeInherited || getPluginInherited(plugin))) {
                    buildPlugins.put(coord, version);
                }
            }
        } catch (NullPointerException e) {
            // guess there are no plugins here
        }
        try {
            for (Profile profile : model.getProfiles()) {
                try {
                    for (Plugin plugin : profile.getBuild().getPlugins()) {
                        String coord = getPluginCoords(plugin);
                        String version = getPluginVersion(plugin);
                        if (version != null && (!onlyIncludeInherited || getPluginInherited(plugin))) {
                            buildPlugins.put(coord, version);
                        }
                    }
                } catch (NullPointerException e) {
                    // guess there are no plugins here
                }
            }
        } catch (NullPointerException e) {
            // guess there are no profiles here
        }
        return buildPlugins;
    }

    /**
     * Returns the Inherited of a {@link Plugin} or {@link ReportPlugin}
     *
     * @param plugin the {@link Plugin} or {@link ReportPlugin}
     * @return the Inherited of the {@link Plugin} or {@link ReportPlugin}
     * @since 1.0-alpha-1
     */
    private static boolean getPluginInherited(Object plugin) {
        return "true".equalsIgnoreCase(plugin instanceof ReportPlugin ? ((ReportPlugin) plugin).getInherited()
                : ((Plugin) plugin).getInherited());
    }

    /**
     * Returns the lifecycle plugins of a specific project.
     *
     * @param project the project to get the lifecycle plugins from.
     * @return The map of effective plugin versions keyed by coordinates.
     * @throws org.apache.maven.plugin.MojoExecutionException
     *          if things go wrong.
     * @since 1.0-alpha-1
     */
    private Map<String, Plugin> getLifecyclePlugins(MavenProject project) throws MojoExecutionException {
        Map<String, Plugin> lifecyclePlugins = new HashMap<String, Plugin>();
        try {
            Set<Plugin> plugins = getBoundPlugins(project, "clean,deploy,site");
            for (Plugin plugin : plugins) {
                lifecyclePlugins.put(getPluginCoords(plugin), plugin);
            }
        } catch (PluginNotFoundException e) {
            throw new MojoExecutionException("Could not find plugin", e);
        } catch (LifecycleExecutionException e) {
            throw new MojoExecutionException("Could not determine lifecycle", e);
        } catch (IllegalAccessException e) {
            throw new MojoExecutionException("Could not determine lifecycles", e);
        } catch (NullPointerException e) {
            // Maven 3.x

        }
        return lifecyclePlugins;
    }

    /**
     * Gets the plugins that are bound to the defined phases. This does not find plugins bound in the pom to a phase
     * later than the plugin is executing.
     *
     * @param project   the project
     * @param thePhases the the phases
     * @return the bound plugins
     * @throws org.apache.maven.plugin.PluginNotFoundException
     *                                     the plugin not found exception
     * @throws LifecycleExecutionException the lifecycle execution exception
     * @throws IllegalAccessException      the illegal access exception
     */
    // pilfered this from enforcer-rules
    // TODO coordinate with Brian Fox to remove the duplicate code
    private Set<Plugin> getBoundPlugins(MavenProject project, String thePhases)
            throws PluginNotFoundException, LifecycleExecutionException, IllegalAccessException {
        if (new DefaultArtifactVersion("3.0").compareTo(runtimeInformation.getApplicationVersion()) <= 0) {
            getLog().debug("Using Maven 3.0+ strategy to determine lifecycle defined plugins");
            try {
                Method getPluginsBoundByDefaultToAllLifecycles = LifecycleExecutor.class
                        .getMethod("getPluginsBoundByDefaultToAllLifecycles", new Class[] { String.class });
                Set<Plugin> plugins = (Set<Plugin>) getPluginsBoundByDefaultToAllLifecycles.invoke(
                        lifecycleExecutor,
                        new Object[] { project.getPackaging() == null ? "jar" : project.getPackaging() });
                // we need to provide a copy with the version blanked out so that inferring from super-pom
                // works as for 2.x as 3.x fills in the version on us!
                Set<Plugin> result = new LinkedHashSet<Plugin>(plugins.size());
                for (Plugin plugin : plugins) {
                    Plugin dup = new Plugin();
                    dup.setGroupId(plugin.getGroupId());
                    dup.setArtifactId(plugin.getArtifactId());
                    result.add(dup);
                }
                return result;
            } catch (NoSuchMethodException e1) {
                // no much we can do here
            } catch (InvocationTargetException e1) {
                // no much we can do here
            } catch (IllegalAccessException e1) {
                // no much we can do here
            }
        }
        List lifecycles = null;
        getLog().debug("Using Maven 2.0.10+ strategy to determine lifecycle defined plugins");
        try {
            Method getLifecycles = LifecycleExecutor.class.getMethod("getLifecycles", new Class[0]);
            lifecycles = (List) getLifecycles.invoke(lifecycleExecutor, new Object[0]);
        } catch (NoSuchMethodException e1) {
            // no much we can do here
        } catch (InvocationTargetException e1) {
            // no much we can do here
        } catch (IllegalAccessException e1) {
            // no much we can do here
        }

        Set<Plugin> allPlugins = new HashSet<Plugin>();

        // lookup the bindings for all the passed in phases
        for (String lifecyclePhase : thePhases.split(",")) {
            if (StringUtils.isNotEmpty(lifecyclePhase)) {
                try {
                    Lifecycle lifecycle = getLifecycleForPhase(lifecycles, lifecyclePhase);
                    allPlugins.addAll(getAllPlugins(project, lifecycle));
                } catch (BuildFailureException e) {
                    // i'm going to swallow this because the
                    // user may have declared a phase that
                    // doesn't exist for every module.
                }
            }
        }
        return allPlugins;
    }

    /**
     * Gets the lifecycle for phase.
     *
     * @param lifecycles The list of lifecycles.
     * @param phase      the phase
     * @return the lifecycle for phase
     * @throws BuildFailureException       the build failure exception
     * @throws LifecycleExecutionException the lifecycle execution exception
     */
    private Lifecycle getLifecycleForPhase(List lifecycles, String phase)
            throws BuildFailureException, LifecycleExecutionException {
        Lifecycle lifecycle = (Lifecycle) getPhaseToLifecycleMap(lifecycles).get(phase);

        if (lifecycle == null) {
            throw new BuildFailureException("Unable to find lifecycle for phase '" + phase + "'");
        }
        return lifecycle;
    }

    /*
     * Uses borrowed lifecycle code to get a list of all plugins bound to the lifecycle.
     */

    /**
     * Gets the all plugins.
     *
     * @param project   the project
     * @param lifecycle the lifecycle
     * @return the all plugins
     * @throws PluginNotFoundException     the plugin not found exception
     * @throws LifecycleExecutionException the lifecycle execution exception
     */
    private Set<Plugin> getAllPlugins(MavenProject project, Lifecycle lifecycle)
            throws PluginNotFoundException, LifecycleExecutionException

    {
        Set<Plugin> plugins = new HashSet<Plugin>();
        // first, bind those associated with the packaging
        Map mappings = findMappingsForLifecycle(project, lifecycle);

        Iterator iter = mappings.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            String value = (String) entry.getValue();
            String[] tokens = value.split(":");

            Plugin plugin = new Plugin();
            plugin.setGroupId(tokens[0]);
            plugin.setArtifactId(tokens[1]);
            plugins.add(plugin);
        }

        for (String value : findOptionalMojosForLifecycle(project, lifecycle)) {
            String[] tokens = value.split(":");

            Plugin plugin = new Plugin();
            plugin.setGroupId(tokens[0]);
            plugin.setArtifactId(tokens[1]);
            plugins.add(plugin);
        }

        plugins.addAll((List<Plugin>) project.getBuildPlugins());

        return plugins;
    }

    /**
     * Find mappings for lifecycle.
     *
     * @param project   the project
     * @param lifecycle the lifecycle
     * @return the map
     * @throws LifecycleExecutionException the lifecycle execution exception
     * @throws PluginNotFoundException     the plugin not found exception
     */
    private Map findMappingsForLifecycle(MavenProject project, Lifecycle lifecycle)
            throws LifecycleExecutionException, PluginNotFoundException {
        String packaging = project.getPackaging();
        Map mappings = null;

        LifecycleMapping m = (LifecycleMapping) findExtension(project, LifecycleMapping.ROLE, packaging,
                session.getSettings(), session.getLocalRepository());
        if (m != null) {
            mappings = m.getPhases(lifecycle.getId());
        }

        Map defaultMappings = lifecycle.getDefaultPhases();

        if (mappings == null) {
            try {
                m = (LifecycleMapping) session.lookup(LifecycleMapping.ROLE, packaging);
                mappings = m.getPhases(lifecycle.getId());
            } catch (ComponentLookupException e) {
                if (defaultMappings == null) {
                    throw new LifecycleExecutionException(
                            "Cannot find lifecycle mapping for packaging: \'" + packaging + "\'.", e);
                }
            }
        }

        if (mappings == null) {
            if (defaultMappings == null) {
                throw new LifecycleExecutionException("Cannot find lifecycle mapping for packaging: \'" + packaging
                        + "\', and there is no default");
            } else {
                mappings = defaultMappings;
            }
        }

        return mappings;
    }

    /**
     * Find optional mojos for lifecycle.
     *
     * @param project   the project
     * @param lifecycle the lifecycle
     * @return the list
     * @throws LifecycleExecutionException the lifecycle execution exception
     * @throws PluginNotFoundException     the plugin not found exception
     */
    private List<String> findOptionalMojosForLifecycle(MavenProject project, Lifecycle lifecycle)
            throws LifecycleExecutionException, PluginNotFoundException {
        String packaging = project.getPackaging();
        List<String> optionalMojos = null;

        LifecycleMapping m = (LifecycleMapping) findExtension(project, LifecycleMapping.ROLE, packaging,
                session.getSettings(), session.getLocalRepository());

        if (m != null) {
            optionalMojos = m.getOptionalMojos(lifecycle.getId());
        }

        if (optionalMojos == null) {
            try {
                m = (LifecycleMapping) session.lookup(LifecycleMapping.ROLE, packaging);
                optionalMojos = m.getOptionalMojos(lifecycle.getId());
            } catch (ComponentLookupException e) {
                getLog().debug("Error looking up lifecycle mapping to retrieve optional mojos. Lifecycle ID: "
                        + lifecycle.getId() + ". Error: " + e.getMessage(), e);
            }
        }

        if (optionalMojos == null) {
            optionalMojos = Collections.emptyList();
        }

        return optionalMojos;
    }

    /**
     * Find extension.
     *
     * @param project         the project
     * @param role            the role
     * @param roleHint        the role hint
     * @param settings        the settings
     * @param localRepository the local repository
     * @return the object
     * @throws LifecycleExecutionException the lifecycle execution exception
     * @throws PluginNotFoundException     the plugin not found exception
     */
    private Object findExtension(MavenProject project, String role, String roleHint, Settings settings,
            ArtifactRepository localRepository) throws LifecycleExecutionException, PluginNotFoundException {
        Object pluginComponent = null;

        for (Iterator i = project.getBuildPlugins().iterator(); i.hasNext() && pluginComponent == null;) {
            Plugin plugin = (Plugin) i.next();

            if (plugin.isExtensions()) {
                loadPluginDescriptor(plugin, project, session);

                // TODO: if moved to the plugin manager we
                // already have the descriptor from above
                // and so do can lookup the container
                // directly
                try {
                    pluginComponent = pluginManager.getPluginComponent(plugin, role, roleHint);
                } catch (ComponentLookupException e) {
                    getLog().debug("Unable to find the lifecycle component in the extension", e);
                } catch (PluginManagerException e) {
                    throw new LifecycleExecutionException(
                            "Error getting extensions from the plugin '" + plugin.getKey() + "': " + e.getMessage(),
                            e);
                }
            }
        }
        return pluginComponent;
    }

    /**
     * Verify plugin.
     *
     * @param plugin  the plugin
     * @param project the project
     * @param session the session
     * @return the plugin descriptor
     * @throws LifecycleExecutionException the lifecycle execution exception
     * @throws PluginNotFoundException     the plugin not found exception
     */
    private PluginDescriptor loadPluginDescriptor(Plugin plugin, MavenProject project, MavenSession session)
            throws LifecycleExecutionException, PluginNotFoundException {
        PluginDescriptor pluginDescriptor;
        try {
            pluginDescriptor = pluginManager.loadPluginDescriptor(plugin, project, session);
        } catch (PluginManagerException e) {
            throw new LifecycleExecutionException("Internal error in the plugin manager getting plugin '"
                    + plugin.getKey() + "': " + e.getMessage(), e);
        } catch (PluginVersionResolutionException e) {
            throw new LifecycleExecutionException(e.getMessage(), e);
        } catch (InvalidVersionSpecificationException e) {
            throw new LifecycleExecutionException(e.getMessage(), e);
        } catch (InvalidPluginException e) {
            throw new LifecycleExecutionException(e.getMessage(), e);
        } catch (ArtifactNotFoundException e) {
            throw new LifecycleExecutionException(e.getMessage(), e);
        } catch (ArtifactResolutionException e) {
            throw new LifecycleExecutionException(e.getMessage(), e);
        } catch (PluginVersionNotFoundException e) {
            throw new LifecycleExecutionException(e.getMessage(), e);
        }
        return pluginDescriptor;
    }

    /**
     * Returns all the parent projects of the specified project, with the root project first.
     *
     * @param project The maven project to get the parents of
     * @return the parent projects of the specified project, with the root project first.
     * @throws org.apache.maven.plugin.MojoExecutionException
     *          if the super-pom could not be created.
     * @since 1.0-alpha-1
     */
    private List<MavenProject> getParentProjects(MavenProject project) throws MojoExecutionException {
        List<MavenProject> parents = new ArrayList<MavenProject>();
        while (project.getParent() != null) {
            project = project.getParent();
            parents.add(0, project);
        }
        return parents;
    }

    /*
     * NOTE: All the code following this point was scooped from the DefaultLifecycleExecutor. There must be a better way
     * but for now it should work.
     */

    /**
     * Gets the phase to lifecycle map.
     *
     * @param lifecycles The list of lifecycles.
     * @return the phase to lifecycle map.
     * @throws LifecycleExecutionException the lifecycle execution exception.
     */
    public Map getPhaseToLifecycleMap(List lifecycles) throws LifecycleExecutionException {
        Map phaseToLifecycleMap = new HashMap();

        for (Iterator i = lifecycles.iterator(); i.hasNext();) {
            Lifecycle lifecycle = (Lifecycle) i.next();

            for (Iterator p = lifecycle.getPhases().iterator(); p.hasNext();) {
                String phase = (String) p.next();

                if (phaseToLifecycleMap.containsKey(phase)) {
                    Lifecycle prevLifecycle = (Lifecycle) phaseToLifecycleMap.get(phase);
                    throw new LifecycleExecutionException(
                            "Phase '" + phase + "' is defined in more than one lifecycle: '" + lifecycle.getId()
                                    + "' and '" + prevLifecycle.getId() + "'");
                } else {
                    phaseToLifecycleMap.put(phase, lifecycle);
                }
            }
        }
        return phaseToLifecycleMap;
    }

    /**
     * Returns the set of all plugins used by the project.
     *
     * @param superPomPluginManagement     the super pom's pluginManagement plugins.
     * @param parentPluginManagement       the parent pom's pluginManagement plugins.
     * @param parentBuildPlugins           the parent pom's build plugins.
     * @param parentReportPlugins          the parent pom's report plugins.
     * @param pluginsWithVersionsSpecified the plugin coords that have a version defined in the project.
     * @return the set of plugins used by the project.
     * @throws org.apache.maven.plugin.MojoExecutionException
     *          if things go wrong.
     */
    private Set<Plugin> getProjectPlugins(Map<String, String> superPomPluginManagement,
            Map<String, String> parentPluginManagement, Map<String, String> parentBuildPlugins,
            Map<String, String> parentReportPlugins, Set<String> pluginsWithVersionsSpecified)
            throws MojoExecutionException {
        Map<String, Plugin> plugins = new HashMap<String, Plugin>();

        getLog().debug("Building list of project plugins...");

        if (getLog().isDebugEnabled()) {
            StringWriter origModel = new StringWriter();

            try {
                origModel.write("Original model:\n");
                getProject().writeOriginalModel(origModel);
                getLog().debug(origModel.toString());
            } catch (IOException e) {
                // ignore
            }
        }

        debugVersionMap("super-pom version map", superPomPluginManagement);
        debugVersionMap("parent version map", parentPluginManagement);

        Map<String, String> excludePluginManagement = new HashMap<String, String>(superPomPluginManagement);
        excludePluginManagement.putAll(parentPluginManagement);

        debugVersionMap("aggregate version map", excludePluginManagement);

        excludePluginManagement.keySet().removeAll(pluginsWithVersionsSpecified);

        debugVersionMap("final aggregate version map", excludePluginManagement);

        Model originalModel;
        try {
            originalModel = modelInterpolator.interpolate(getProject().getOriginalModel(),
                    getProject().getBasedir(),
                    new DefaultProjectBuilderConfiguration().setExecutionProperties(getProject().getProperties()),
                    true);
        } catch (ModelInterpolationException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
        try {
            addProjectPlugins(plugins, originalModel.getBuild().getPluginManagement().getPlugins(),
                    excludePluginManagement);
        } catch (NullPointerException e) {
            // guess there are no plugins here
        }
        debugPluginMap("after adding local pluginManagement", plugins);

        try {
            List<Plugin> lifecyclePlugins = new ArrayList<Plugin>(getLifecyclePlugins(getProject()).values());
            for (Iterator<Plugin> i = lifecyclePlugins.iterator(); i.hasNext();) {
                Plugin lifecyclePlugin = i.next();
                if (getPluginVersion(lifecyclePlugin) != null) {
                    // version comes from lifecycle, therefore cannot modify
                    i.remove();
                } else {
                    // lifecycle leaves version open
                    String parentVersion = parentPluginManagement.get(getPluginCoords(lifecyclePlugin));
                    if (parentVersion != null) {
                        // parent controls version
                        i.remove();
                    }
                }
            }
            addProjectPlugins(plugins, lifecyclePlugins, parentPluginManagement);

            debugPluginMap("after adding lifecycle plugins", plugins);
        } catch (NullPointerException e) {
            // using maven 3.x or newer
        }

        try {
            List<Plugin> buildPlugins = new ArrayList<Plugin>(originalModel.getBuild().getPlugins());
            for (Iterator<Plugin> i = buildPlugins.iterator(); i.hasNext();) {
                Plugin buildPlugin = i.next();
                if (getPluginVersion(buildPlugin) == null) {
                    String parentVersion = parentPluginManagement.get(getPluginCoords(buildPlugin));
                    if (parentVersion != null) {
                        // parent controls version
                        i.remove();
                    }
                }
            }
            addProjectPlugins(plugins, buildPlugins, parentBuildPlugins);
        } catch (NullPointerException e) {
            // guess there are no plugins here
        }
        debugPluginMap("after adding build plugins", plugins);

        try {
            List<ReportPlugin> reportPlugins = new ArrayList<ReportPlugin>(
                    originalModel.getReporting().getPlugins());
            for (Iterator<ReportPlugin> i = reportPlugins.iterator(); i.hasNext();) {
                ReportPlugin reportPlugin = i.next();
                if (getPluginVersion(reportPlugin) == null) {
                    String parentVersion = parentPluginManagement.get(getPluginCoords(reportPlugin));
                    if (parentVersion != null) {
                        // parent controls version
                        i.remove();
                    }
                }
            }
            addProjectPlugins(plugins, toPlugins(reportPlugins), parentReportPlugins);
        } catch (NullPointerException e) {
            // guess there are no plugins here
        }
        debugPluginMap("after adding reporting plugins", plugins);

        for (Profile profile : originalModel.getProfiles()) {
            try {
                addProjectPlugins(plugins, profile.getBuild().getPluginManagement().getPlugins(),
                        excludePluginManagement);
            } catch (NullPointerException e) {
                // guess there are no plugins here
            }
            debugPluginMap("after adding build pluginManagement for profile " + profile.getId(), plugins);

            try {
                addProjectPlugins(plugins, profile.getBuild().getPlugins(), parentBuildPlugins);
            } catch (NullPointerException e) {
                // guess there are no plugins here
            }
            debugPluginMap("after adding build plugins for profile " + profile.getId(), plugins);

            try {
                addProjectPlugins(plugins, toPlugins(profile.getReporting().getPlugins()), parentReportPlugins);
            } catch (NullPointerException e) {
                // guess there are no plugins here
            }
            debugPluginMap("after adding reporting plugins for profile " + profile.getId(), plugins);
        }
        Set<Plugin> result = new TreeSet<Plugin>(new PluginComparator());
        result.addAll(plugins.values());
        return result;
    }

    /**
     * Adds those project plugins which are not inherited from the parent definitions to the list of plugins.
     *
     * @param plugins           The list of plugins.
     * @param projectPlugins    The project's plugins.
     * @param parentDefinitions The parent plugin definitions.
     * @since 1.0-alpha-1
     */
    private void addProjectPlugins(Map<String, Plugin> plugins, Collection<Plugin> projectPlugins,
            Map<String, String> parentDefinitions) {
        for (Plugin plugin : projectPlugins) {
            String coord = getPluginCoords(plugin);
            String version = getPluginVersion(plugin);
            String parentVersion = parentDefinitions.get(coord);
            if (version == null && (!plugins.containsKey(coord) || getPluginVersion(plugins.get(coord)) == null)
                    && parentVersion != null) {
                Plugin parentPlugin = new Plugin();
                parentPlugin.setGroupId(getPluginGroupId(plugin));
                parentPlugin.setArtifactId(getPluginArtifactId(plugin));
                parentPlugin.setVersion(parentVersion);
                plugins.put(coord, parentPlugin);
            } else if (parentVersion == null || !parentVersion.equals(version)) {
                if (!plugins.containsKey(coord) || getPluginVersion(plugins.get(coord)) == null) {
                    plugins.put(coord, plugin);
                }
            }
            if (!plugins.containsKey(coord)) {
                plugins.put(coord, plugin);
            }
        }
    }

    /**
     * Logs at debug level a map of plugins keyed by versionless key.
     *
     * @param description log description
     * @param plugins     a map with keys being the {@link String} corresponding to the versionless artifact key and
     *                    values being {@link Plugin} or {@link ReportPlugin}.
     */
    private void debugPluginMap(String description, Map plugins) {
        if (getLog().isDebugEnabled()) {
            Set sorted = new TreeSet(new PluginComparator());
            sorted.addAll(plugins.values());
            StringBuilder buf = new StringBuilder(description);
            Iterator i = sorted.iterator();
            while (i.hasNext()) {
                Object plugin = i.next();
                buf.append("\n    ");
                buf.append(getPluginCoords(plugin));
                buf.append(":");
                buf.append(getPluginVersion(plugin));
            }
            getLog().debug(buf.toString());
        }
    }

    /**
     * Logs at debug level a map of plugin versions keyed by versionless key.
     *
     * @param description log description
     * @param plugins     a map with keys being the {@link String} corresponding to the versionless artifact key and
     *                    values being {@link String} plugin version.
     */
    private void debugVersionMap(String description, Map plugins) {
        if (getLog().isDebugEnabled()) {
            StringBuilder buf = new StringBuilder(description);
            Iterator i = plugins.entrySet().iterator();
            while (i.hasNext()) {
                Map.Entry plugin = (Map.Entry) i.next();
                buf.append("\n    ");
                buf.append(plugin.getKey());
                buf.append(":");
                buf.append(plugin.getValue());
            }
            getLog().debug(buf.toString());
        }
    }

    /**
     * Returns the coordinates of a plugin.
     *
     * @param plugin The plugin
     * @return The groupId and artifactId separated by a colon.
     * @since 1.0-alpha-1
     */
    private static String getPluginCoords(Object plugin) {
        return getPluginGroupId(plugin) + ":" + getPluginArtifactId(plugin);
    }

    /**
     * Returns the ArtifactId of a {@link Plugin} or {@link ReportPlugin}
     *
     * @param plugin the {@link Plugin} or {@link ReportPlugin}
     * @return the ArtifactId of the {@link Plugin} or {@link ReportPlugin}
     * @since 1.0-alpha-1
     */
    private static String getPluginArtifactId(Object plugin) {
        return plugin instanceof ReportPlugin ? ((ReportPlugin) plugin).getArtifactId()
                : ((Plugin) plugin).getArtifactId();
    }

    private static Plugin toPlugin(ReportPlugin reportPlugin) {
        Plugin plugin = new Plugin();
        plugin.setGroupId(reportPlugin.getGroupId());
        plugin.setArtifactId(reportPlugin.getArtifactId());
        plugin.setVersion(reportPlugin.getVersion());
        return plugin;
    }

    private static ReportPlugin toReportPlugin(Plugin plugin) {
        ReportPlugin reportPlugin = new ReportPlugin();
        reportPlugin.setGroupId(plugin.getGroupId());
        reportPlugin.setArtifactId(plugin.getArtifactId());
        reportPlugin.setVersion(plugin.getVersion());
        return reportPlugin;
    }

    private static Set<Plugin> toPlugins(Set<ReportPlugin> reportPlugins) {
        Set<Plugin> result;
        if (reportPlugins instanceof LinkedHashSet) {
            result = new LinkedHashSet<Plugin>(reportPlugins.size());
        } else if (reportPlugins instanceof SortedSet) {
            final Comparator<? super ReportPlugin> comparator = ((SortedSet<ReportPlugin>) reportPlugins)
                    .comparator();
            result = new TreeSet<Plugin>(new Comparator<Plugin>() {
                public int compare(Plugin o1, Plugin o2) {
                    return comparator.compare(toReportPlugin(o1), toReportPlugin(o2));
                }
            });
        } else {
            result = new HashSet<Plugin>(reportPlugins.size());
        }
        for (ReportPlugin reportPlugin : reportPlugins) {
            result.add(toPlugin(reportPlugin));
        }
        return result;
    }

    private static List<Plugin> toPlugins(List<ReportPlugin> reportPlugins) {
        List<Plugin> result = new ArrayList<Plugin>(reportPlugins.size());
        for (ReportPlugin reportPlugin : reportPlugins) {
            result.add(toPlugin(reportPlugin));
        }
        return result;
    }

    private static Collection<Plugin> toPlugins(Collection<ReportPlugin> reportPlugins) {
        if (reportPlugins instanceof Set) {
            return toPlugins((Set<ReportPlugin>) reportPlugins);
        }
        if (reportPlugins instanceof List) {
            return toPlugins((List<ReportPlugin>) reportPlugins);
        }
        return toPlugins(new ArrayList<ReportPlugin>(reportPlugins));
    }

    /**
     * Returns the GroupId of a {@link Plugin} or {@link ReportPlugin}
     *
     * @param plugin the {@link Plugin} or {@link ReportPlugin}
     * @return the GroupId of the {@link Plugin} or {@link ReportPlugin}
     * @since 1.0-alpha-1
     */
    private static String getPluginGroupId(Object plugin) {
        return plugin instanceof ReportPlugin ? ((ReportPlugin) plugin).getGroupId()
                : ((Plugin) plugin).getGroupId();
    }

    /**
     * Returns the Version of a {@link Plugin} or {@link ReportPlugin}
     *
     * @param plugin the {@link Plugin} or {@link ReportPlugin}
     * @return the Version of the {@link Plugin} or {@link ReportPlugin}
     * @since 1.0-alpha-1
     */
    private static String getPluginVersion(Object plugin) {
        return plugin instanceof ReportPlugin ? ((ReportPlugin) plugin).getVersion()
                : ((Plugin) plugin).getVersion();
    }

    /**
     * Gets the report plugins of a specific project.
     *
     * @param model                the model to get the report plugins from.
     * @param onlyIncludeInherited <code>true</code> to only return the plugins definitions that will be
     *                             inherited by child projects.
     * @return The map of effective plugin versions keyed by coordinates.
     * @since 1.0-alpha-1
     */
    private Map<String, String> getReportPlugins(Model model, boolean onlyIncludeInherited) {
        Map<String, String> reportPlugins = new HashMap<String, String>();
        try {
            for (ReportPlugin plugin : model.getReporting().getPlugins()) {
                String coord = getPluginCoords(plugin);
                String version = getPluginVersion(plugin);
                if (version != null && (!onlyIncludeInherited || getPluginInherited(plugin))) {
                    reportPlugins.put(coord, version);
                }
            }
        } catch (NullPointerException e) {
            // guess there are no plugins here
        }
        try {
            for (Profile profile : model.getProfiles()) {
                try {
                    for (ReportPlugin plugin : profile.getReporting().getPlugins()) {
                        String coord = getPluginCoords(plugin);
                        String version = getPluginVersion(plugin);
                        if (version != null && (!onlyIncludeInherited || getPluginInherited(plugin))) {
                            reportPlugins.put(coord, version);
                        }
                    }
                } catch (NullPointerException e) {
                    // guess there are no plugins here
                }
            }
        } catch (NullPointerException e) {
            // guess there are no profiles here
        }
        return reportPlugins;
    }

    /**
     * @param pom the pom to update.
     * @throws MojoExecutionException when things go wrong
     * @throws MojoFailureException   when things go wrong in a very bad way
     * @throws XMLStreamException     when things go wrong with XML streaming
     * @see AbstractVersionsUpdaterMojo#update(ModifiedPomXMLEventReader)
     * @since 1.0-alpha-1
     */
    protected void update(ModifiedPomXMLEventReader pom)
            throws MojoExecutionException, MojoFailureException, XMLStreamException {
        // do nothing
    }

}