001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.tools.plugin.generator;
020
021import java.io.File;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.InputStreamReader;
025import java.io.OutputStreamWriter;
026import java.io.StringWriter;
027import java.io.Writer;
028
029import org.apache.maven.project.MavenProject;
030import org.apache.velocity.VelocityContext;
031import org.codehaus.plexus.logging.AbstractLogEnabled;
032import org.codehaus.plexus.logging.Logger;
033import org.codehaus.plexus.logging.console.ConsoleLogger;
034import org.codehaus.plexus.util.StringUtils;
035import org.codehaus.plexus.util.io.CachingOutputStream;
036import org.codehaus.plexus.velocity.VelocityComponent;
037
038import static java.nio.charset.StandardCharsets.UTF_8;
039
040/**
041 * Generates an <code>HelpMojo</code> class from <code>help-class-source.vm</code> template.
042 * The generated mojo reads help content from <code>META-INF/maven/${groupId}/${artifactId}/plugin-help.xml</code>
043 * resource, which is generated by this {@link PluginDescriptorFilesGenerator}.
044 *
045 * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
046 * @since 2.4
047 */
048public class PluginHelpGenerator extends AbstractLogEnabled {
049    /**
050     * Default generated class name
051     */
052    private static final String HELP_MOJO_CLASS_NAME = "HelpMojo";
053
054    private String helpPackageName;
055    private String goalPrefix;
056    private MavenProject mavenProject;
057    private boolean useMaven4Api;
058    private VelocityComponent velocityComponent;
059
060    /**
061     * Default constructor
062     */
063    public PluginHelpGenerator() {
064        this.enableLogging(new ConsoleLogger(Logger.LEVEL_INFO, "PluginHelpGenerator"));
065    }
066
067    // ----------------------------------------------------------------------
068    // Public methods
069    // ----------------------------------------------------------------------
070
071    public void execute(File destinationDirectory) throws GeneratorException {
072        String helpImplementation = getImplementation();
073
074        useMaven4Api = mavenProject.getDependencies().stream()
075                .anyMatch(dep ->
076                        "org.apache.maven".equals(dep.getGroupId()) && "maven-api-core".equals(dep.getArtifactId()));
077
078        try {
079            String sourcePath = helpImplementation.replace('.', File.separatorChar) + ".java";
080
081            File helpClass = new File(destinationDirectory, sourcePath);
082            helpClass.getParentFile().mkdirs();
083
084            String helpClassSources = getHelpClassSources(getPluginHelpPath(mavenProject));
085
086            try (Writer w = new OutputStreamWriter(new CachingOutputStream(helpClass), UTF_8)) {
087                w.write(helpClassSources);
088            }
089        } catch (IOException e) {
090            throw new GeneratorException(e.getMessage(), e);
091        }
092    }
093
094    public PluginHelpGenerator setHelpPackageName(String helpPackageName) {
095        this.helpPackageName = helpPackageName;
096        return this;
097    }
098
099    public PluginHelpGenerator setVelocityComponent(VelocityComponent velocityComponent) {
100        this.velocityComponent = velocityComponent;
101        return this;
102    }
103
104    public PluginHelpGenerator setGoalPrefix(String goalPrefix) {
105        this.goalPrefix = goalPrefix;
106        return this;
107    }
108
109    public PluginHelpGenerator setMavenProject(MavenProject mavenProject) {
110        this.mavenProject = mavenProject;
111        return this;
112    }
113
114    // ----------------------------------------------------------------------
115    // Private methods
116    // ----------------------------------------------------------------------
117
118    private String getHelpClassSources(String pluginHelpPath) throws IOException {
119        VelocityContext context = new VelocityContext();
120        boolean useAnnotations =
121                mavenProject.getArtifactMap().containsKey("org.apache.maven.plugin-tools:maven-plugin-annotations");
122
123        context.put("helpPackageName", helpPackageName);
124        context.put("pluginHelpPath", pluginHelpPath);
125        context.put("artifactId", mavenProject.getArtifactId());
126        // TODO: evaluate prefix from deserialized plugin
127        context.put("goalPrefix", goalPrefix);
128        context.put("useAnnotations", useAnnotations);
129
130        StringWriter stringWriter = new StringWriter();
131
132        // plugin-tools sources are UTF-8 (and even ASCII in this case))
133        try (InputStream is = Thread.currentThread()
134                        .getContextClassLoader()
135                        .getResourceAsStream(useMaven4Api ? "help-class-source-v4.vm" : "help-class-source.vm"); //
136                InputStreamReader isReader = new InputStreamReader(is, UTF_8)) {
137            // isReader =
138            velocityComponent.getEngine().evaluate(context, stringWriter, "", isReader);
139        }
140        // Apply OS lineSeparator instead of template's lineSeparator to have consistent separators for
141        // all source files.
142        return stringWriter.toString().replaceAll("(\r\n|\n|\r)", System.lineSeparator());
143    }
144
145    /**
146     * @return The implementation.
147     */
148    private String getImplementation() {
149        return StringUtils.isEmpty(helpPackageName)
150                ? HELP_MOJO_CLASS_NAME
151                : helpPackageName + '.' + HELP_MOJO_CLASS_NAME;
152    }
153
154    static String getPluginHelpPath(MavenProject mavenProject) {
155        return mavenProject.getGroupId() + "/" + mavenProject.getArtifactId() + "/plugin-help.xml";
156    }
157}