de.jflex.plugin.maven.JFlexMojo Maven / Gradle / Ivy
Show all versions of jflex-maven-plugin Show documentation
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* JFlex Maven3 plugin *
* Copyright (c) 2007-2017 Régis Décamps *
* All rights reserved. *
* *
* License: BSD *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package de.jflex.plugin.maven;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import jflex.Main;
import jflex.Options;
import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
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.project.MavenProject;
/**
* Generates lexical scanners from one or more JFlex grammar files.
*
* @author Régis Décamps ([email protected])
*/
@Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = false)
public class JFlexMojo extends AbstractMojo {
/** Name of the directory where to look for jflex files by default. */
private static final String SRC_MAIN_JFLEX = "src/main/jflex";
@Parameter(property = "project", required = true, readonly = true)
private MavenProject project;
// cannot use {@value SRC_MAIN_JFLEX} because Maven site goals.html
// is kept raw.
/**
* List of grammar definitions to run the JFlex parser generator on. Each path may either specify
* a single grammar file or a directory. Directories will be recursively scanned for files with
* one of the following extensions: ".jflex", ".flex", ".jlex" or ".lex". By default, all files in
* src/main/jflex
will be processed.
*
* @see #SRC_MAIN_JFLEX
*/
@Parameter private File[] lexDefinitions;
/** Name of the directory into which JFlex should generate the parser. */
@Parameter(defaultValue = "${project.build.directory}/generated-sources/jflex")
private File outputDirectory;
/**
* The granularity in milliseconds of the last modification date for testing whether a source
* needs regeneration.
*/
@Parameter(property = "lastModGranularityMs", defaultValue = "0")
private int staleMillis;
/** Whether source code generation should be verbose. */
@Parameter(defaultValue = "false")
private boolean verbose;
/** Whether a warning will be logged when there are unused macros. */
@Parameter(defaultValue = "true")
private boolean unusedWarning;
/** Whether to dump full debug information. */
@Parameter(defaultValue = "false")
private boolean dump;
/**
* Whether to produce graphviz .dot files for the generated automata. This feature is
* EXPERIMENTAL.
*/
@Parameter(defaultValue = "false")
private boolean dot;
/** Use external skeleton file. */
@Parameter private File skeleton;
/** Strict JLex compatibility. */
@Parameter(defaultValue = "false")
private boolean jlex;
/** The generation method to use for the scanner. The only valid value is pack
. */
@Parameter(defaultValue = "pack")
private String generationMethod = "pack"; // NOPMD
/** A flag whether to perform the DFA minimization step during scanner generation. */
@Parameter(defaultValue = "true")
private boolean minimize = true; // NOPMD
/**
* A flag whether to enable the generation of a backup copy if the generated source file already
* exists.
*/
@Parameter(defaultValue = "true")
private boolean backup = true; // NOPMD
/**
* If true, the dot (.) metachar matches [^\n] instead of [^\n\r\u000B\u000C\u0085\u2028\u2029].
*/
@Parameter(defaultValue = "false")
private boolean legacyDot = false; // NOPMD
/**
* The name of the character encoding for reading lexer specifications. Uses JVM default encoding
* if unset.
*/
@Parameter(defaultValue = "")
private String encodingName = ""; // NOPMD
/**
* Generate java parsers from lexer definition files.
*
* This methods is checks parameters, sets options and calls JFlex.Main.generate()
*/
public void execute() throws MojoExecutionException, MojoFailureException {
this.outputDirectory = getAbsolutePath(this.outputDirectory);
// compiling the generated source in target/generated-sources/ is
// the whole point of this plugin compared to running the ant plugin
project.addCompileSourceRoot(outputDirectory.getPath());
List filesIt;
if (lexDefinitions == null) {
// use default lexfiles if none provided
getLog().debug("Use lexer files found in (default) " + SRC_MAIN_JFLEX);
filesIt = new ArrayList<>();
File defaultDir = getAbsolutePath(new File(SRC_MAIN_JFLEX));
if (defaultDir.isDirectory()) {
filesIt.add(defaultDir);
}
} else {
// use arguments provided in the plugin configuration
filesIt = Arrays.asList(lexDefinitions);
getLog()
.debug(
"Parsing "
+ lexDefinitions.length
+ " jflex files or directories given in configuration");
}
// process all lexDefinitions
for (File lexDefinition : filesIt) {
lexDefinition = getAbsolutePath(lexDefinition);
parseLexDefinition(lexDefinition);
}
}
/**
* Generates java code of a parser from a lexer file.
*
* If the {@code lexDefinition} is a directory, process all lexer files contained within.
*
* @param lexDefinition Lexer definiton file or directory to process.
* @throws MojoFailureException if the file is not found.
* @throws MojoExecutionException if file could not be parsed
*/
@SuppressWarnings("unchecked")
private void parseLexDefinition(File lexDefinition)
throws MojoFailureException, MojoExecutionException {
assert lexDefinition.isAbsolute() : lexDefinition;
if (lexDefinition.isDirectory()) {
// recursively process files contained within
String[] extensions = {"jflex", "jlex", "lex", "flex"};
getLog().debug("Processing lexer files found in " + lexDefinition);
Iterator fileIterator = FileUtils.iterateFiles(lexDefinition, extensions, true);
while (fileIterator.hasNext()) {
File lexFile = fileIterator.next();
parseLexFile(lexFile);
}
} else {
parseLexFile(lexDefinition);
}
}
private void parseLexFile(File lexFile) throws MojoFailureException, MojoExecutionException {
assert lexFile.isAbsolute() : lexFile;
getLog().debug("Generating Java code from " + lexFile.getName());
ClassInfo classInfo;
try {
classInfo = LexSimpleAnalyzer.guessPackageAndClass(lexFile);
} catch (FileNotFoundException e) {
throw new MojoFailureException(e.getMessage(), e);
} catch (IOException e) {
classInfo = new ClassInfo();
classInfo.className = LexSimpleAnalyzer.DEFAULT_NAME;
classInfo.packageName = null; // NOPMD
}
checkParameters(lexFile);
// set destination directory
File generatedFile = new File(outputDirectory, classInfo.getOutputFilename());
// generate only if needs to
if (lexFile.lastModified() - generatedFile.lastModified() <= this.staleMillis) {
getLog().info(" " + generatedFile.getName() + " is up to date.");
getLog().debug("StaleMillis = " + staleMillis + "ms");
return;
}
// set options. Very strange that JFlex expects this in a static way.
Options.setDefaults();
Options.setDir(generatedFile.getParentFile());
Options.dump = dump;
Options.verbose = verbose;
Options.unused_warning = unusedWarning;
Options.dot = dot;
Options.legacy_dot = legacyDot;
if (skeleton != null) {
Options.setSkeleton(skeleton);
}
Options.jlex = jlex;
Options.no_minimize = !minimize; // NOPMD
Options.no_backup = !backup; // NOPMD
if (!"pack".equals(generationMethod)) {
throw new MojoExecutionException("Illegal generation method: " + generationMethod);
}
if (!"".equals(encodingName)) {
try {
Options.setEncoding(encodingName);
} catch (Exception e) {
throw new MojoExecutionException(e.getMessage());
}
}
try {
Main.generate(lexFile);
getLog().info(" generated " + generatedFile);
} catch (Exception e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}
/**
* Check parameter lexFile.
*
* Must not be {@code null} and file must exist.
*
* @param lexFile input file to check.
* @throws MojoExecutionException in case of error
*/
private void checkParameters(File lexFile) throws MojoExecutionException {
if (lexFile == null) {
throw new MojoExecutionException(
" is empty. Please define input file with input.jflex ");
}
if (!lexFile.isFile()) {
throw new MojoExecutionException("Input file does not exist: " + lexFile);
}
}
/**
* Converts the specified path argument into an absolute path. If the path is relative like
* "src/main/jflex", it is resolved against the base directory of the project (in constrast,
* File.getAbsoluteFile() would resolve against the current directory which may be different,
* especially during a reactor build).
*
* @param path The path argument to convert, may be {@code null}.
* @return The absolute path corresponding to the input argument.
*/
private File getAbsolutePath(File path) {
if (path == null || path.isAbsolute()) {
return path;
}
return new File(this.project.getBasedir().getAbsolutePath(), path.getPath());
}
}