# AbstractParentMojo.java

    package org.thewonderlemming.c4plantuml.mojo;

    import java.io.File;
    import java.io.IOException;
    import java.nio.charset.Charset;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.function.Consumer;
    import java.util.regex.Pattern;
    import java.util.stream.Stream;

    import org.apache.maven.plugin.AbstractMojo;
    import org.apache.maven.plugin.MojoExecutionException;
    import org.apache.maven.plugin.MojoFailureException;
    import org.apache.maven.plugins.annotations.Parameter;
    import org.apache.maven.project.MavenProject;

    /**
     * A base implementation of a MOJO that handles shared parameters such as {@link Charset}, {@link MavenProject}, and so
     * on.
     *
     * @author thewonderlemming
     *
     */
    public abstract class AbstractParentMojo extends AbstractMojo {

        /**
         * Default value for the C4 PlantUML files extension.
         */
        public static final String DEFAULT_SOURCE_FILE_EXTENSION = ".puml";

        private static final Pattern SOURCE_FILE_EXTENSION_PATTERN = Pattern.compile("^\\.[a-zA-Z0-9]+$");

        private Charset currentCharset;

        private String outputDirectory;

        @Parameter(property = "project", defaultValue = "${project}", readonly = true, required = true)
        private MavenProject project;

        @Parameter(property = "sourceFileExtension", defaultValue = AbstractParentMojo.DEFAULT_SOURCE_FILE_EXTENSION,
            alias = "sourceFileExtension")
        private String sourceFileExtension;

        private static boolean isFile(final Path path) {
            return path.toFile().isFile();
        }

        /**
         * The default implementation of {@link AbstractMojo#execute()}, that sets and validates shared parameters, then
         * calls the abstract method {@link AbstractParentMojo#doExecute()}, that needs to be implemented by children
         * classes.
         * <p>
         * {@inheritDoc}
         */
        @Override
        public final void execute() throws MojoExecutionException, MojoFailureException {

            validateSourceFileExtension();
            setCurrentCharset();
            setOutputDirectory();

            doExecute();
        }

        /**
         * An abstract method to hold the children classes execution logic.
         *
         * @throws MojoExecutionException if there is an issue with the build that makes it impossible to continue.
         * @throws MojoFailureException if there is any other kind of failures.
         */
        protected abstract void doExecute() throws MojoExecutionException, MojoFailureException;

        /**
         * Returns the current {@link Charset} value.
         *
         * @return the current {@link Charset}.
         */
        protected Charset getCurrentCharset() {
            return currentCharset;
        }

        /**
         * Returns the project output directory path.
         *
         * @return the project output directory.
         */
        protected String getOutputDirectory() {
            return this.outputDirectory;
        }

        /**
         * Returns the current {@link MavenProject} instance.
         *
         * @return the {@link MavenProject} instance.
         */
        protected MavenProject getProject() {
            return this.project;
        }

        /**
         * Returns the current sources filename extension.
         * <p>
         * This is how the plugin will determine which files it should process.
         *
         * @return the sources filename extension.
         */
        protected String getSourceFileExtension() {
            return this.sourceFileExtension;
        }

        /**
         * An utility method that enables the processing of the C4 PlantUML sources in the output directory.
         *
         * @param pumlFileProcessor the source file consumer method.
         * @throws IOException if any I/O error happens while accessing a source file.
         */
        protected void processOutputDirectoryFiles(final Consumer<Path> pumlFileProcessor) throws IOException {

            try (final Stream<Path> pathStream = Files.walk(Paths.get(getOutputDirectory()))) {

                pathStream
                    .filter(AbstractParentMojo::isFile)
                        .filter(this::isPumlSource)
                        .forEach(pumlFileProcessor::accept);
            }
        }

        /**
         * Validates that the current {@code sourceFileExtension} property has a correct value, or sets it to the default
         * value and displays an error message.
         */
        protected void validateSourceFileExtension() {

            assert (this.sourceFileExtension != null) : "Expecting sourceFileExtension to be set";

            if (!SOURCE_FILE_EXTENSION_PATTERN.matcher(this.sourceFileExtension).matches()) {

                final String errMsg = String
                    .format("Bad format for sourceFileExtension: %s. Reverting to default value instead: %s",
                        this.sourceFileExtension,
                        DEFAULT_SOURCE_FILE_EXTENSION);

                getLog().warn(errMsg);
                this.sourceFileExtension = DEFAULT_SOURCE_FILE_EXTENSION;
            }
        }

        private boolean isPumlSource(final Path path) {

            assert (this.sourceFileExtension != null) : "Expecting sourceFileExtension to be set";
            return path.toString().toLowerCase().endsWith(this.sourceFileExtension);
        }

        private void setCurrentCharset() throws MojoExecutionException {

            try {

                final String sourceEncoding = this.project
                    .getProperties()
                        .getProperty("project.build.sourceEncoding", StandardCharsets.UTF_8.toString());

                this.currentCharset = Charset.forName(sourceEncoding);

            } catch (final IllegalArgumentException e) {
                throw new MojoExecutionException("Cannot get character encoding for source files", e);
            }
        }

        private void setOutputDirectory() throws MojoExecutionException {

            this.outputDirectory = getProject().getBuild().getOutputDirectory();

            if (!new File(this.outputDirectory).exists()) {

                final String errMsg = "Cannot process source directory \"" + this.outputDirectory
                    + "\" as it does not exist";
                throw new MojoExecutionException(errMsg);
            }
        }
    }