JavaCCGoalMojo.java
/*
* Copyright (c) 2025-2026, Marc Mazas <mazas.marc@gmail.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.javacc.mojo;
import java.io.File;
import java.util.List;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
/**
* The <b>javacc</b> goal, for generating the parser files from a JavaCC grammar.<br>
* It searches the source directory for all grammar files and run JavaCC once for each file it
* finds, managing the output directory from the user setting or the default setting, and adding it
* to the project-wide compile source roots.<br>
* It uses an intermediate output directory to help discard any generated file when the user wants
* instead his customized version he has put in any compile source root.
* <p>
* Detailed information about the JavaCC options can be found on the
* <a href="https://javacc.github.io/javacc-8/">JavaCC website</a>.<br>
* The code repositories can be found within <a href="https://github.com/javacc">JavaCC at
* GitHub</a>.
*
* @since 3.8.0
* @author Maͫzͣaͬsͨ
*/
@Mojo(name = "javacc", defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true)
public class JavaCCGoalMojo extends AbstractPluginMojo {
/** The bean handling JavaCC options as command line arguments. */
private final JavaCCArgumentsBean jjab = new JavaCCArgumentsBean();
/**
* The list of single full command line arguments, which should be in the form accepted by JavaCC.
*
* <p>
* Arguments' values for <code>-CODE_GENERATOR</code>, <code>-GRAMMAR_ENCODING</code> and
* <code>-OUTPUT_DIRECTORY</code> are read and used by the plugin; their default values (i.e. if
* these options are not present) are <code>"Java"</code> (as for JavaCC), system property <code>
* file.encoding</code> (as for JavaCC) and <code>
* ${project.build.directory}/generated-sources/javacc</code> (as for usual maven convention -
* JavaCC's default value is <code>"."</code>).
*
* <p>
* The list will be passed as it is to JavaCC, no control nor modification is done by the plugin,
* except for <code>-OUTPUT_DIRECTORY</code>, whose value will be changed to an intermediate
* temporary directory <code>${project.build.directory}/javacc-nnnnnn</code>. The given output
* directory value will be used after JavaCC generation to copy the generated files from the
* intermediate directory to it.
*
* <p>
* The list has no default value except for the 3 above options (but JavaCC itself supplies
* default values).<br>
* Note that among those 3 default values, the grammar encoding one (platform file encoding) may
* be not adequate as most of the times the project is under an IDE and globally set to a platform
* independent encoding like 'UTF-8'.
*
* <p>
* Example:<br>
*
* <pre>{@code
* <javaccCmdLineArgs>
* <arg>-StAtIc=false</arg>
* <arg>-IGNORE_CASE:true</arg>
* <arg>-LookAhead=2</arg>
* <arg>-CODE_GENERATOR:"C++"</arg>
* <arg>-GRAMMAR_ENCODING="UTF-8"</arg>
* <arg>-OUTPUT_DIRECTORY="$}{{@code
* project.build.directory
* }}{@code /gen-src/jj"</arg>
* </javaccCmdLineArgs>
* }</pre>
*
* <br>
* Note that the <code>javaccCmdLineArgs</code> parameter is of type {@code List<String>}, which
* implies that the inner tags names can have any names and may be appear many times (like
* <code>arg</code> above).<br>
* Note also that if the project has a parent which also configures the plugin with parameters (to
* factorize them for the children for example), the child parameters will complement / replace
* the parent's ones for those that are absent / present in the parent; so
* <ul>
* <li>if one wants to get rid of (all) the parent's ones, he must use the <code>
* combine.self="override"</code> attribute at the list level in the child, and</li>
* <li>if one wants to replace some of the parent's ones and add new ones in the child it is
* recommended to use distinct tag names in the parent, the same tag names in the child for those
* that must be replaced and another tag name or other tag names for the new ones.</li>
* </ul>
* See also
* <a href= "https://github.com/javacc/javacc-maven-plugin#processor-parameters">Processor
* parameters</a>.
*/
@Parameter(property = "javacc.javaccCmdLineArgs") //
protected List<String> javaccCmdLineArgs;
@Override
protected void initProcessor() throws PluginException {
if (includes == null) {
includes = new String[] {
"**/*.jj"
};
}
if (sourceDirectory == null) {
sourceDirectory = new File(project.getBasedir(), JavaCCArgumentsBean.defSrcSubDir);
getLog().debug("sourceDirectory initialized to '" + sourceDirectory + "'");
}
// mojo's log injected after the mojo is constructed, so wait until now to pass it to the bean
jjab.log = getLog();
jjab.setProcCmdLineArgs(javaccCmdLineArgs);
jjab.findSpecificOptions(project, "javaccCmdLineArgs");
}
@Override
protected void processGrammar(final GrammarInfo grammarInfo) throws ProcessorException {
final String ts = String.valueOf(System.currentTimeMillis()).substring(6);
final File javacc = new File(project.getBuild().getDirectory(), "javacc-" + ts);
final File[] dirs = new File[] {
javacc
};
runProcessorOnGrammar(grammarInfo, dirs, false);
}
@Override
protected void runProcessor(final File gram, final File[] dirs) throws ProcessorException {
final JavaCCProcessor jjp = new JavaCCProcessor(getLog());
jjp.inputFile = gram;
jjp.intermediateOutputDirectories = dirs;
jjp.intermedOutDirOptions = new String[] {
JavaCCArgumentsBean.argOutDir
};
jjp.cmdLineArgs = javaccCmdLineArgs;
jjp.run();
}
@Override
protected String getGrammarFileEncoding() {
return (jjab.grammarFileEncodingOptionValue == null)
? AbstractArgumentsBean.defaultGrammarFileEncoding
: jjab.grammarFileEncodingOptionValue;
}
@Override
protected Language getLanguage() {
return jjab.languageOptionValue == null ? AbstractArgumentsBean.defaultLanguage
: jjab.languageOptionValue;
}
@Override
protected File[] getProcessorOutputDirectories() {
return jjab.processorOutputDirectories;
}
@Override
protected File[] getGoalOutputDirectories() {
return jjab.processorOutputDirectories;
}
}