/* * #%L * Wisdom-Framework * %% * Copyright (C) 2013 - 2014 Wisdom Framework * %% * Licensed 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. * #L% */ package ${package}; import com.google.common.collect.ImmutableList; import org.apache.commons.io.FileUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.pegdown.Extensions; import org.pegdown.PegDownProcessor; import org.wisdom.maven.Constants; import org.wisdom.maven.WatchingException; import org.wisdom.maven.mojos.AbstractWisdomWatcherMojo; import org.wisdom.maven.utils.WatcherUtils; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * This is our generated watcher. It compiles Markdown files to HTML documents. It locates all Markdown files from the * internal assets and external assets directories (i.e. src/main/resources/assets and src/main/assets) and processed * them using PegDown. * <p> * Watchers are enhanced Mojos (Maven Plugin), that comply with the Mojo rules (i.e. having an {@code execute} * methods). By default, our Watcher-Mojo defines the "compile-markdown" goal, executed in the "COMPILE" phase. Check * the Mojo Developer guide to select your right set of date ( * http://maven.apache.org/guides/plugin/guide-java-plugin-development.html). * <p> * Unlike regular Mojo, extending {@code AbstractMojo}, watchers must extend the {@code AbstractWisdomWatcherMojo} * class to be considered as a {@code Watcher}. By default, watchers are automatically registered to the Watcher * Pipeline and so are participating in the Wisdom Watch Mode. */ @Mojo(name = "compile-markdown", threadSafe = false, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true, defaultPhase = LifecyclePhase.COMPILE) public class MarkdownMojo extends AbstractWisdomWatcherMojo implements Constants { /** * The extension of output files. */ public static final String OUTPUT_EXTENSION = "html"; /** * A sets of extensions handled by the mojo. By default, it supports {@code .md} and {@code .markdown} files. * <p> * As stated above, Watchers are regular Mojos and so can be configured as a plain Mojo (using {@code @Parameter} * or {@code @Component}. Notice that the injected values are also accessible in the watch mode. */ @Parameter(property = "extensions") protected List<String> extensions = new ArrayList<>(); /** * The Pegdown instance (can be reused). */ protected PegDownProcessor instance; /** * Compiles all markdown files located in the internal and external asset directories. * <p> * This is the main Mojo entry point. The {@code execute} method is invoked by the regular Maven execution. * * @throws MojoExecutionException if a markdown file cannot be processed */ public void execute() throws MojoExecutionException { if (extensions == null || extensions.isEmpty()) { extensions = ImmutableList.of("md", "markdown"); } if (instance == null) { instance = new PegDownProcessor(Extensions.ALL); } try { // The getResources method locates all the assets files from "src/main/resources/assets" (internal // assets) and "src/main/assets" (exernal assets) having on of the given extensions. for (File f : getResources(extensions)) { process(f); } } catch (IOException e) { throw new MojoExecutionException("Error while processing a Markdown file", e); } } /** * Processes the given markdown file. When the 'filtered' version of the file exists, it uses it. * <p> * It's generally a good practice to define such a method used by the {@code execute} method and watcher methods. * * @param input the input file * @throws IOException if the file cannot be processed. */ public void process(File input) throws IOException { // The file may have been filtered (copied to the output directory and placeholders have been filled with // actual values. In this case, use the filtered version. File filtered = getFilteredVersion(input); if (filtered == null) { // It was not copied. getLog().warn("Cannot find the filtered version of " + input.getAbsolutePath() + ", " + "using source file."); filtered = input; } // Actual markdown processing. String result = instance.markdownToHtml(FileUtils.readFileToString(filtered)); // Write the output file. We can compute the output file using the 'getOutputFile' method taking as parameter // the input file and the output extension. FileUtils.write(getOutputFile(input, OUTPUT_EXTENSION), result); } /** * The markdown mojo only accepts Markdown files, i.e. files using the {@code .md, .markdown} extensions, * or onle of the custom extensions set. * <p> * This is the first watcher method. It fileters files handled by the watcher. Implementations must not check * for the file existence, as this method is also used on deleted files. In most cases, * it checks for extensions and / or for locations. The WatcherUtils classes provides two handy methods for this: * {@code isInDirectory} checks whether a file is in a given directory (including sub-directories), * and {@code hasExtension} checking for the file extension. * * @param file is the file. * @return {@code true} if the file is accepted. */ @Override public boolean accept(File file) { return WatcherUtils.hasExtension(file, extensions); } /** * An accepted file was created - processes it. * <p> * This is also a watcher method called when an accepted file is created. The returned value determines whether * the pipeline must continue after the execution of the current watcher. If {@code false} is returned, * the subsequent watchers are not invoked (the pipeline traversal is interupted). * * @param file is the file. * @return {@code true} * @throws WatchingException if the file cannot be processed correctly */ @Override public boolean fileCreated(File file) throws WatchingException { try { process(file); } catch (IOException e) { // Throwing WatchingException is pretty important. You can configure the files having provoked the error, // as well as the error's line number and position. More detailed the exception is, // better the error message. Watching Exceptions are analysed and displayed to the developer in a web // page. throw new WatchingException(e.getMessage(), file, e); } return true; } /** * An accepted file was updated - re-processes it. * <p> * This is also a watcher method called when an accepted file is updated. The returned value determines whether * the pipeline must continue after the execution of the current watcher. If {@code false} is returned, * the subsequent watchers are not invoked (the pipeline traversal is interupted). * * @param file is the file. * @return {@code true} * @throws WatchingException if the file cannot be processed correctly */ @Override public boolean fileUpdated(File file) throws WatchingException { return fileCreated(file); } /** * An accepted file was deleted - deletes the output file. * <p> * This is also a watcher method called when an accepted file is deleted. The returned value determines whether * the pipeline must continue after the execution of the current watcher. If {@code false} is returned, * the subsequent watchers are not invoked (the pipeline traversal is interupted). * <p> * This methods is often used to clear the output generated for the given input file. * * @param file the file * @return {@code true} */ @Override public boolean fileDeleted(File file) { File output = getOutputFile(file, OUTPUT_EXTENSION); FileUtils.deleteQuietly(output); return true; } }