The Generator
Up: UI Implementation
Options | Generator | UI Specific
The generator is the bit of code that ties the new UI to the Rat CLI. In this section we will explore how the Rat core module exposes the information necessary to build a valuable new UI. We will be using the Maven tooling code in apache-rat-tools
as the basis of the examples in this section. As always, the current code base is the source of truth.
There are two implementations of the Generator concept AntGenerator
and MavenGenerator
.
The MavenGenerator
The MavenGenerator
generates the abstract class that is used by the apache-rat-plugin
module to process Maven elements that are tied to the CLI options. The class starts with a list map of exceptional CLI Option name to Maven option name conversions. In the code below the legacy “addLicense” option is changed to “add-license” so that the standard conversion to camel case will produce the proper name. There is also a list of options that are not supported by Maven. In code below the “–dir”. “–log-level”, and “–help” options are all excluded. “–dir” is excluded because Maven produces a list of files to process, “–log-level” is excluded because the Maven log level is set via a different process, and “–help” is excluded because Maven has it own help facility.
/** A mapping of external name to internal name if not standard */
private static final Map<String, String> RENAME_MAP = new HashMap<>();
/** List of CLI Options that are not supported by Maven. */
private static final List<Option> MAVEN_FILTER_LIST = new ArrayList<>();
static {
RENAME_MAP.put("addLicense", "add-license");
MAVEN_FILTER_LIST.addAll(Arg.DIR.group().getOptions());
MAVEN_FILTER_LIST.addAll(Arg.LOG_LEVEL.group().getOptions());
MAVEN_FILTER_LIST.add(OptionCollection.HELP);
}
/**
* Filter to remove Options not supported by Maven.
*/
private static final Predicate<Option> MAVEN_FILTER = option -> !(MAVEN_FILTER_LIST.contains(option) || option.getLongOpt() == null);
The list of MavenOptions (see Options) can then be generated by
List<MavenOption> options = OptionCollection.buildOptions().getOptions()
.stream().filter(MAVEN_FILTER)
.map(MavenOption::new).collect(Collectors.toList());
The MavenGenerator
uses a template called “Maven.tpl” that looks like:
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
${package}
import org.apache.commons.cli.Option;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.rat.commandline.Arg;
import org.apache.rat.utils.CasedString;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/* DO NOT EDIT - GENERATED FILE */
/**
* Generated class to provide Maven support for standard Rat command line options
*/
${class}
private final static Map<String,String> xlateName = new HashMap<>();
private final static List<String> unsupportedArgs = new ArrayList<>();
static {
${static}
}
public static String createName(String longOpt) {
String name = xlateName.get(longOpt);
return name != null ? name : new CasedString(CasedString.StringCase.KEBAB, longOpt).toCase(CasedString.StringCase.CAMEL);
}
public static List<String> unsupportedArgs() {
return Collections.unmodifiableList(unsupportedArgs);
}
${commonArgs}
${constructor}
/* GENERATED METHODS */
${methods}
}
this template contains replaceable sections for :
- package: The name of the java package that the generated class belongs to.
- class: The line that defines the class for the Maven generated code it reads:
public abstract class BaseRatMojo extends AbstractMojo {
. Note that the Maven example extends a Maven package class. - static: A section to initialize the static data like the translation name table and the unsupportedArgs list.
- commonArgs: A section where the “Args.tpl” is inserted. This section contains the methods for argument manipulation that is common among all the UIs
- constructor: The constructor for the class. In the Maven case this is simply
protected BaseRatMojo() {}
. - methods: A section where the generator will insert the methods that map from the Maven framework into the Rat framework as specified in the “Args.tpl”.
The Args template
The args template defines a number of methods to simplify the interface between an arbitrary UI and the Rat CLI command line format. The template defines a variable called args
that is a map of a CLI based argument name to a list of Strings that are the argument values.
In general classes that include this template will call:
setArg
, addArg
, and/or removeArg
to set values in the args structure. Eventually the UI class will call getArgs
to generate a command line string to pass to the CLI command line parsing code to create a ReportConfiguration
that may be modified before calling Reporter.report()
///////////////////////// Start common Arg manipulation code
/**
* A map of CLI based arguments to values.
*/
protected final Map<String, List<String>> args = new HashMap<>();
/**
* Gets the list of arguments prepared for the CLI code to parse.
* @return the List of arguments for the CLI command line.
*/
protected List<String> args() {
List<String> result = new ArrayList<>();
for (Map.Entry<String, List<String>> entry : args.entrySet()) {
result.add("--" + entry.getKey());
result.addAll(entry.getValue().stream().filter(Objects::nonNull).collect(Collectors.toList()));
}
return result;
}
private String argsKey(Option opt) {
return StringUtils.defaultIfEmpty(opt.getLongOpt(), opt.getKey());
}
private boolean validateSet(String key) {
Arg arg = Arg.findArg(key);
if (arg != null) {
Option opt = arg.find(key);
Option main = arg.option();
if (opt.isDeprecated()) {
args.remove(argsKey(main));
// deprecated options must be explicitly set so let it go.
return true;
}
// non-deprecated options may have default so ignore it if another option has already been set.
for (Option o : arg.group().getOptions()) {
if (!o.equals(main)) {
if (args.containsKey(argsKey(o))) {
return false;
}
}
}
return true;
}
return false;
}
/**
* Set a key and value into the argument list.
* Replaces any existing value.
* @param key the key for the map.
* @param value the value to set.
*/
protected void setArg(String key, String value) {
if (validateSet(key)) {
List<String> values = new ArrayList<>();
values.add(value);
args.put(key, values);
}
}
/**
* Get the list of values for a key.
* @param key the key for the map.
* @return the list of values for the key or {@code null} if not set.
*/
public List<String> getArg(String key) {
return args.get(key);
}
/**
* Add values to the key in the argument list.
* If the key does not exist, adds it.
* @param key the key for the map.
* @param value the value to set.
*/
protected void addArg(String key, String[] value) {
if (validateSet(key)) {
List<String> values = args.get(key);
if (values == null) {
values = new ArrayList<>();
args.put(key, values);
}
values.addAll(Arrays.asList(value));
}
}
/**
* Add a value to the key in the argument list.
* If the key does not exist, adds it.
* @param key the key for the map.
* @param value the value to set.
*/
protected void addArg(String key, String value) {
if (validateSet(key)) {
List<String> values = args.get(key);
if (values == null) {
values = new ArrayList<>();
args.put(key, values);
}
values.add(value);
}
}
/**
* Remove a key from the argument list.
* @param key the key to remove from the map.
*/
protected void removeArg(String key) {
args.remove(key);
}
///////////////////////// End common Arg manipulation code
Generating Methods
For every MavenOption
at least one method is created.
Generating the method signature
The MavenGenerator
needs to generate method signatures based on the org.apache.commons.cli.Option
state so the MavenOption
provides the getMethodSignature
method. This method checks the following conditions.
- Is the option deprecated? If so add the
@Deprecated
annotation. - Does the option accept one or more arguments (i.e. is not a flag option)? If it does the argument should be a
String
otherwise it should be aboolean
. - Does the method accept multiple arguments? If so modify the name to indicate plural as per the Maven standard.
- Create the Maven
@Property
annotation with optional Maven command line property and defaults for the method. - Write all the information into a string and prefix the method name with “set” as per the Maven standard.
With the result being that a fully formed, Maven compliant, Java method signature is developed. Complete with documentation from the Option description. For example “–copyright”, which is a deprecated option, produces the following Maven mojo method signature:
/**
* The copyright message to use in the license headers.
* @param copyright copyright message to use in the license headers.
* @deprecated Deprecated for removal since 0.17: Use <editCopyright> instead.
*/
@Deprecated
@Parameter(property = "rat.Copyright")
public void setCopyright(String copyright) {
Generating the method body
When executing the generated class should take the value passed in from the new UI convert it to a String
(or boolean
if a flag option) and call the setArg
(or possibly the removeArg
) method defined in Args.tpl
. The key for the arg method is the CLI option name (without leading dashes). In the case of the setCopyright
method above this looks like:
public void setCopyright(String copyright) {
setArg("copyright", copyright);
}
Maven also supports the case where multiple values are accepted for one Property. To support this the MavenGenerator
will generate methods that accept multiple String arguments and have a name that pluralizes the standard “set” based name. Details are available in the MavenGenerator
code.
AntGenerator
The AntGenerator follows the same design as the MavenGenerator. The main difference is that Ant allows attributes in the XML elements.
Up: UI Implementation >
Options | Generator | UI Specific