PluginDescriptorBuilder.java
/*
 * 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.
 */
package org.apache.maven.plugin.descriptor;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.maven.api.xml.XmlNode;
import org.apache.maven.api.xml.XmlService;
import org.apache.maven.internal.xml.XmlPlexusConfiguration;
import org.apache.maven.plugin.descriptor.io.PluginDescriptorStaxReader;
import org.codehaus.plexus.component.repository.ComponentDependency;
import org.codehaus.plexus.component.repository.ComponentRequirement;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.PlexusConfigurationException;
/**
 * Build plugin descriptor object from {@code plugin.xml}.
 *
 * @author Jason van Zyl
 */
public class PluginDescriptorBuilder {
    public static final String PLUGIN_2_0_0 = "http://maven.apache.org/PLUGIN/2.0.0";
    private static final int BUFFER_SIZE = 8192;
    public interface StreamSupplier {
        InputStream open() throws IOException;
    }
    public interface ReaderSupplier {
        Reader open() throws IOException;
    }
    /**
     * @deprecated use {@link #build(ReaderSupplier)}
     */
    @Deprecated
    public PluginDescriptor build(Reader reader) throws PlexusConfigurationException {
        return build(reader, null);
    }
    /**
     * @deprecated use {@link #build(ReaderSupplier, String)}
     */
    @Deprecated
    public PluginDescriptor build(Reader reader, String source) throws PlexusConfigurationException {
        return build(() -> reader, source);
    }
    public PluginDescriptor build(ReaderSupplier readerSupplier) throws PlexusConfigurationException {
        return build(readerSupplier, null);
    }
    public PluginDescriptor build(ReaderSupplier readerSupplier, String source) throws PlexusConfigurationException {
        try (BufferedReader br = new BufferedReader(readerSupplier.open(), BUFFER_SIZE)) {
            br.mark(BUFFER_SIZE);
            XMLStreamReader xsr = XMLInputFactory.newFactory().createXMLStreamReader(br);
            xsr.nextTag();
            String nsUri = xsr.getNamespaceURI();
            try (BufferedReader br2 = reset(readerSupplier, br)) {
                xsr = XMLInputFactory.newFactory().createXMLStreamReader(br2);
                return build(source, nsUri, xsr);
            }
        } catch (XMLStreamException | IOException e) {
            throw new PlexusConfigurationException(e.getMessage(), e);
        }
    }
    /**
     * @deprecated use {@link #build(StreamSupplier, String)}
     */
    @Deprecated
    public PluginDescriptor build(InputStream input, String source) throws PlexusConfigurationException {
        return build(() -> input, source);
    }
    public PluginDescriptor build(StreamSupplier inputSupplier) throws PlexusConfigurationException {
        return build(inputSupplier, null);
    }
    public PluginDescriptor build(StreamSupplier inputSupplier, String source) throws PlexusConfigurationException {
        try (BufferedInputStream bis = new BufferedInputStream(inputSupplier.open(), BUFFER_SIZE)) {
            bis.mark(BUFFER_SIZE);
            XMLStreamReader xsr = XMLInputFactory.newFactory().createXMLStreamReader(bis);
            xsr.nextTag();
            String nsUri = xsr.getNamespaceURI();
            try (BufferedInputStream bis2 = reset(inputSupplier, bis)) {
                xsr = XMLInputFactory.newFactory().createXMLStreamReader(bis2);
                return build(source, nsUri, xsr);
            }
        } catch (XMLStreamException | IOException e) {
            throw new PlexusConfigurationException(e.getMessage(), e);
        }
    }
    private static BufferedInputStream reset(StreamSupplier inputSupplier, BufferedInputStream bis) throws IOException {
        try {
            bis.reset();
            return bis;
        } catch (IOException e) {
            return new BufferedInputStream(inputSupplier.open(), BUFFER_SIZE);
        }
    }
    private static BufferedReader reset(ReaderSupplier readerSupplier, BufferedReader br) throws IOException {
        try {
            br.reset();
            return br;
        } catch (IOException e) {
            return new BufferedReader(readerSupplier.open(), BUFFER_SIZE);
        }
    }
    private PluginDescriptor build(String source, String nsUri, XMLStreamReader xsr)
            throws XMLStreamException, PlexusConfigurationException {
        if (PLUGIN_2_0_0.equals(nsUri)) {
            org.apache.maven.api.plugin.descriptor.PluginDescriptor pd =
                    new PluginDescriptorStaxReader().read(xsr, true);
            return new PluginDescriptor(pd);
        } else {
            XmlNode node = XmlService.read(xsr);
            PlexusConfiguration cfg = XmlPlexusConfiguration.toPlexusConfiguration(node);
            return build(source, cfg);
        }
    }
    private PluginDescriptor build(String source, PlexusConfiguration c) throws PlexusConfigurationException {
        PluginDescriptor pluginDescriptor = new PluginDescriptor();
        pluginDescriptor.setSource(source);
        pluginDescriptor.setGroupId(extractGroupId(c));
        pluginDescriptor.setArtifactId(extractArtifactId(c));
        pluginDescriptor.setVersion(extractVersion(c));
        pluginDescriptor.setGoalPrefix(extractGoalPrefix(c));
        pluginDescriptor.setName(extractName(c));
        pluginDescriptor.setDescription(extractDescription(c));
        pluginDescriptor.setIsolatedRealm(extractIsolatedRealm(c));
        pluginDescriptor.setInheritedByDefault(extractInheritedByDefault(c));
        pluginDescriptor.setRequiredJavaVersion(extractRequiredJavaVersion(c).orElse(null));
        pluginDescriptor.setRequiredMavenVersion(extractRequiredMavenVersion(c).orElse(null));
        pluginDescriptor.addMojos(extractMojos(c, pluginDescriptor));
        pluginDescriptor.setDependencies(extractComponentDependencies(c));
        return pluginDescriptor;
    }
    private String extractGroupId(PlexusConfiguration c) {
        return c.getChild("groupId").getValue();
    }
    private String extractArtifactId(PlexusConfiguration c) {
        return c.getChild("artifactId").getValue();
    }
    private String extractVersion(PlexusConfiguration c) {
        return c.getChild("version").getValue();
    }
    private String extractGoalPrefix(PlexusConfiguration c) {
        return c.getChild("goalPrefix").getValue();
    }
    private String extractName(PlexusConfiguration c) {
        return c.getChild("name").getValue();
    }
    private String extractDescription(PlexusConfiguration c) {
        return c.getChild("description").getValue();
    }
    private List<MojoDescriptor> extractMojos(PlexusConfiguration c, PluginDescriptor pluginDescriptor)
            throws PlexusConfigurationException {
        List<MojoDescriptor> mojos = new ArrayList<>();
        PlexusConfiguration[] mojoConfigurations = c.getChild("mojos").getChildren("mojo");
        for (PlexusConfiguration component : mojoConfigurations) {
            mojos.add(buildComponentDescriptor(component, pluginDescriptor));
        }
        return mojos;
    }
    private boolean extractInheritedByDefault(PlexusConfiguration c) {
        String inheritedByDefault = c.getChild("inheritedByDefault").getValue();
        if (inheritedByDefault != null) {
            return Boolean.parseBoolean(inheritedByDefault);
        }
        return false;
    }
    private boolean extractIsolatedRealm(PlexusConfiguration c) {
        String isolatedRealm = c.getChild("isolatedRealm").getValue();
        if (isolatedRealm != null) {
            return Boolean.parseBoolean(isolatedRealm);
        }
        return false;
    }
    private Optional<String> extractRequiredJavaVersion(PlexusConfiguration c) {
        return Optional.ofNullable(c.getChild("requiredJavaVersion")).map(PlexusConfiguration::getValue);
    }
    private Optional<String> extractRequiredMavenVersion(PlexusConfiguration c) {
        return Optional.ofNullable(c.getChild("requiredMavenVersion")).map(PlexusConfiguration::getValue);
    }
    private List<ComponentDependency> extractComponentDependencies(PlexusConfiguration c) {
        PlexusConfiguration[] dependencyConfigurations =
                c.getChild("dependencies").getChildren("dependency");
        List<ComponentDependency> dependencies = new ArrayList<>();
        for (PlexusConfiguration d : dependencyConfigurations) {
            dependencies.add(extractComponentDependency(d));
        }
        return dependencies;
    }
    private ComponentDependency extractComponentDependency(PlexusConfiguration d) {
        ComponentDependency cd = new ComponentDependency();
        cd.setArtifactId(extractArtifactId(d));
        cd.setGroupId(extractGroupId(d));
        cd.setType(d.getChild("type").getValue());
        cd.setVersion(extractVersion(d));
        return cd;
    }
    @SuppressWarnings("checkstyle:methodlength")
    public MojoDescriptor buildComponentDescriptor(PlexusConfiguration c, PluginDescriptor pluginDescriptor)
            throws PlexusConfigurationException {
        MojoDescriptor mojo = new MojoDescriptor();
        mojo.setPluginDescriptor(pluginDescriptor);
        mojo.setGoal(c.getChild("goal").getValue());
        mojo.setImplementation(c.getChild("implementation").getValue());
        PlexusConfiguration langConfig = c.getChild("language");
        if (langConfig != null) {
            mojo.setLanguage(langConfig.getValue());
        }
        PlexusConfiguration configuratorConfig = c.getChild("configurator");
        if (configuratorConfig != null) {
            mojo.setComponentConfigurator(configuratorConfig.getValue());
        }
        PlexusConfiguration composerConfig = c.getChild("composer");
        if (composerConfig != null) {
            mojo.setComponentComposer(composerConfig.getValue());
        }
        String since = c.getChild("since").getValue();
        if (since != null) {
            mojo.setSince(since);
        }
        PlexusConfiguration deprecated = c.getChild("deprecated", false);
        if (deprecated != null) {
            mojo.setDeprecated(deprecated.getValue());
        }
        String phase = c.getChild("phase").getValue();
        if (phase != null) {
            mojo.setPhase(phase);
        }
        String executePhase = c.getChild("executePhase").getValue();
        if (executePhase != null) {
            mojo.setExecutePhase(executePhase);
        }
        String executeMojo = c.getChild("executeGoal").getValue();
        if (executeMojo != null) {
            mojo.setExecuteGoal(executeMojo);
        }
        String executeLifecycle = c.getChild("executeLifecycle").getValue();
        if (executeLifecycle != null) {
            mojo.setExecuteLifecycle(executeLifecycle);
        }
        mojo.setInstantiationStrategy(c.getChild("instantiationStrategy").getValue());
        mojo.setDescription(extractDescription(c));
        PlexusConfiguration dependencyResolution = c.getChild("requiresDependencyResolution", false);
        if (dependencyResolution != null) {
            mojo.setDependencyResolutionRequired(dependencyResolution.getValue());
        }
        PlexusConfiguration dependencyCollection = c.getChild("requiresDependencyCollection", false);
        if (dependencyCollection != null) {
            mojo.setDependencyCollectionRequired(dependencyCollection.getValue());
        }
        String directInvocationOnly = c.getChild("requiresDirectInvocation").getValue();
        if (directInvocationOnly != null) {
            mojo.setDirectInvocationOnly(Boolean.parseBoolean(directInvocationOnly));
        }
        String requiresProject = c.getChild("requiresProject").getValue();
        if (requiresProject != null) {
            mojo.setProjectRequired(Boolean.parseBoolean(requiresProject));
        }
        String requiresReports = c.getChild("requiresReports").getValue();
        if (requiresReports != null) {
            mojo.setRequiresReports(Boolean.parseBoolean(requiresReports));
        }
        String aggregator = c.getChild("aggregator").getValue();
        if (aggregator != null) {
            mojo.setAggregator(Boolean.parseBoolean(aggregator));
        }
        String requiresOnline = c.getChild("requiresOnline").getValue();
        if (requiresOnline != null) {
            mojo.setOnlineRequired(Boolean.parseBoolean(requiresOnline));
        }
        String inheritedByDefault = c.getChild("inheritedByDefault").getValue();
        if (inheritedByDefault != null) {
            mojo.setInheritedByDefault(Boolean.parseBoolean(inheritedByDefault));
        }
        String threadSafe = c.getChild("threadSafe").getValue();
        if (threadSafe != null) {
            mojo.setThreadSafe(Boolean.parseBoolean(threadSafe));
        }
        // ----------------------------------------------------------------------
        // Configuration
        // ----------------------------------------------------------------------
        PlexusConfiguration mojoConfig = c.getChild("configuration");
        mojo.setMojoConfiguration(mojoConfig);
        // ----------------------------------------------------------------------
        // Parameters
        // ----------------------------------------------------------------------
        PlexusConfiguration[] parameterConfigurations = c.getChild("parameters").getChildren("parameter");
        List<Parameter> parameters = new ArrayList<>();
        for (PlexusConfiguration d : parameterConfigurations) {
            Parameter parameter = new Parameter();
            parameter.setName(extractName(d));
            parameter.setAlias(d.getChild("alias").getValue());
            parameter.setType(d.getChild("type").getValue());
            String required = d.getChild("required").getValue();
            parameter.setRequired(Boolean.parseBoolean(required));
            PlexusConfiguration editableConfig = d.getChild("editable");
            // we need the null check for pre-build legacy plugins...
            if (editableConfig != null) {
                String editable = d.getChild("editable").getValue();
                parameter.setEditable(editable == null || Boolean.parseBoolean(editable));
            }
            parameter.setDescription(extractDescription(d));
            parameter.setDeprecated(d.getChild("deprecated").getValue());
            parameter.setImplementation(d.getChild("implementation").getValue());
            parameter.setSince(d.getChild("since").getValue());
            PlexusConfiguration paramConfig = mojoConfig.getChild(parameter.getName(), false);
            if (paramConfig != null) {
                parameter.setExpression(paramConfig.getValue(null));
                parameter.setDefaultValue(paramConfig.getAttribute("default-value"));
            }
            parameters.add(parameter);
        }
        mojo.setParameters(parameters);
        // TODO this should not need to be handed off...
        // ----------------------------------------------------------------------
        // Requirements
        // ----------------------------------------------------------------------
        PlexusConfiguration[] requirements = c.getChild("requirements").getChildren("requirement");
        for (PlexusConfiguration requirement : requirements) {
            ComponentRequirement cr = new ComponentRequirement();
            cr.setRole(requirement.getChild("role").getValue());
            cr.setRoleHint(requirement.getChild("role-hint").getValue());
            cr.setFieldName(requirement.getChild("field-name").getValue());
            mojo.addRequirement(cr);
        }
        return mojo;
    }
    // ----------------------------------------------------------------------
    //
    // ----------------------------------------------------------------------
    public PlexusConfiguration buildConfiguration(Reader configuration) throws PlexusConfigurationException {
        try {
            XMLStreamReader reader = XMLInputFactory.newFactory().createXMLStreamReader(configuration);
            return XmlPlexusConfiguration.toPlexusConfiguration(XmlService.read(reader));
        } catch (XMLStreamException e) {
            throw new PlexusConfigurationException(e.getMessage(), e);
        }
    }
    public PlexusConfiguration buildConfiguration(InputStream configuration) throws PlexusConfigurationException {
        try {
            XMLStreamReader reader = XMLInputFactory.newFactory().createXMLStreamReader(configuration);
            return XmlPlexusConfiguration.toPlexusConfiguration(XmlService.read(reader));
        } catch (XMLStreamException e) {
            throw new PlexusConfigurationException(e.getMessage(), e);
        }
    }
}