You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@creadur.apache.org by cl...@apache.org on 2026/08/06 05:52:50 UTC

(creadur-rat) branch UIOption_update created (now 6f29248d)

This is an automated email from the ASF dual-hosted git repository.

Claudenw pushed a change to branch UIOption_update
in repository https://gitbox.apache.org/repos/asf/creadur-rat.git


      at 6f29248d fixed tests

This branch includes the following new commits:

     new 6f29248d fixed tests

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(creadur-rat) 01/01: fixed tests

Posted by cl...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

Claudenw pushed a commit to branch UIOption_update
in repository https://gitbox.apache.org/repos/asf/creadur-rat.git

commit 6f29248df310e451ee922a57291a0f258516ebef
Author: Claude Warren <cl...@xenei.com>
AuthorDate: Thu Aug 6 06:46:57 2026 +0100

    fixed tests
---
 .../src/main/java/org/apache/rat/CLIOption.java    |  24 ++-
 .../java/org/apache/rat/CLIOptionCollection.java   |  10 +-
 .../main/java/org/apache/rat/OptionCollection.java |  72 ++++---
 .../org/apache/rat/OptionCollectionParser.java     |  60 ++++--
 .../apache/rat/commandline/ArgumentContext.java    |  41 +++-
 .../java/org/apache/rat/help/AbstractHelp.java     |   7 +-
 .../src/main/java/org/apache/rat/ui/UIOption.java  | 153 ++++++++++++--
 .../java/org/apache/rat/ui/UIOptionCollection.java | 136 +++++++------
 .../org/apache/rat/ui/UpdatableOptionGroup.java    |   1 +
 .../java/org/apache/rat/utils/CasedString.java     |  14 +-
 .../test/java/org/apache/rat/CLIOptionTest.java    |  10 +-
 .../org/apache/rat/OptionCollectionParserTest.java |  62 +++++-
 .../java/org/apache/rat/OptionCollectionTest.java  |   5 +-
 .../src/test/java/org/apache/rat/ReporterTest.java | 226 ++++++++++++---------
 .../java/org/apache/rat/commandline/ArgTests.java  |  21 +-
 .../org/apache/rat/testhelpers/BaseOption.java     |  61 ++++++
 .../rat/testhelpers/BaseOptionCollection.java      |  36 ++++
 .../org/apache/rat/ui/ArgumentTrackerTest.java     |   2 +-
 .../org/apache/rat/ui/UIOptionCollectionTest.java  |  24 ++-
 .../test/java/org/apache/rat/ui/UIOptionTest.java  |   3 +-
 .../main/java/org/apache/rat/anttasks/Help.java    |   2 +-
 .../apache/rat/anttasks/GeneratedReportTest.java   |   6 +-
 .../org/apache/rat/anttasks/ReportOptionTest.java  |   5 +-
 .../rat/documentation/options/AntOption.java       |  76 ++++++-
 .../documentation/options/AntOptionCollection.java | 105 +++++-----
 .../rat/documentation/options/MavenOption.java     |  69 +++++--
 .../options/MavenOptionCollection.java             |  45 +---
 .../apache/rat/documentation/velocity/RatTool.java |  27 ++-
 .../org/apache/rat/tools/AntDocumentation.java     |   9 +-
 .../java/org/apache/rat/tools/AntGenerator.java    |   7 +-
 .../java/org/apache/rat/tools/MavenGenerator.java  |  13 +-
 .../src/main/java/org/apache/rat/tools/Naming.java | 129 ++++++------
 .../rat/documentation/options/MavenOptionTest.java |   3 +-
 .../rat/documentation/velocity/RatToolTest.java    |  11 +-
 .../org/apache/rat/tools/AntDocumentationTest.java |   8 +-
 35 files changed, 1012 insertions(+), 471 deletions(-)

diff --git a/apache-rat-core/src/main/java/org/apache/rat/CLIOption.java b/apache-rat-core/src/main/java/org/apache/rat/CLIOption.java
index 7b75cdb8..39545215 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/CLIOption.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/CLIOption.java
@@ -18,19 +18,21 @@
  */
 package org.apache.rat;
 
+import java.util.function.Function;
+
 import org.apache.commons.cli.Option;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.rat.ui.ArgumentTracker;
 import org.apache.rat.ui.UIOption;
-import org.apache.rat.ui.UIOptionCollection;
+import org.apache.rat.utils.CasedString;
 
 /**
  * The CLI option definition.
  */
 public final class CLIOption extends UIOption<CLIOption> {
 
-    public CLIOption(final UIOptionCollection<CLIOption> collection, final Option option) {
-        super(collection, option, ArgumentTracker.extractName(option));
+    private CLIOption(final CLIBuilder builder) {
+        super(builder);
     }
 
     @Override
@@ -70,4 +72,20 @@ public final class CLIOption extends UIOption<CLIOption> {
         }
         return sb.toString();
     }
+
+    /**
+     * Builder for a CLI Option.
+     */
+    public static class CLIBuilder extends UIOption.Builder<CLIOption, CLIBuilder> {
+
+        @Override
+        protected Function<Option, CasedString> getNameFactory() {
+            return ArgumentTracker::extractName;
+        }
+
+        @Override
+        protected CLIOption doBuild() {
+            return new CLIOption(this);
+        }
+    }
 }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/CLIOptionCollection.java b/apache-rat-core/src/main/java/org/apache/rat/CLIOptionCollection.java
index 9abb0ca5..9b192b16 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/CLIOptionCollection.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/CLIOptionCollection.java
@@ -21,20 +21,20 @@ package org.apache.rat;
 import org.apache.commons.cli.Option;
 import org.apache.rat.ui.UIOptionCollection;
 
+/**
+ * The collection of CLI Options.
+ */
 public final class CLIOptionCollection extends UIOptionCollection<CLIOption> {
     /** The Help option */
     static final Option HELP = new Option("?", "help", false, "Print help for the RAT command line interface and exit.");
 
-    /** The instance of the collection */
-    public static final CLIOptionCollection INSTANCE = new CLIOptionCollection();
-
-    private CLIOptionCollection() {
+    public CLIOptionCollection() {
         super(new Builder().uiOption(HELP));
     }
 
     private static final class Builder extends UIOptionCollection.Builder<CLIOption, Builder> {
         private Builder() {
-            super(CLIOption::new);
+            super(CLIOption.CLIBuilder::new);
         }
     }
 }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/OptionCollection.java b/apache-rat-core/src/main/java/org/apache/rat/OptionCollection.java
index 2d4bdd63..55bbdf62 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/OptionCollection.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/OptionCollection.java
@@ -35,7 +35,6 @@ import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.DefaultParser;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
@@ -51,6 +50,8 @@ import org.apache.rat.help.Licenses;
 import org.apache.rat.license.LicenseSetFactory;
 import org.apache.rat.report.Reportable;
 import org.apache.rat.report.claim.ClaimStatistic;
+import org.apache.rat.ui.ArgumentTracker;
+import org.apache.rat.ui.UIOptionCollection;
 import org.apache.rat.utils.DefaultLog;
 import org.apache.rat.utils.Log.Level;
 import org.apache.rat.walker.ArchiveWalker;
@@ -68,6 +69,11 @@ public final class OptionCollection {
         // do not instantiate
     }
 
+    /**
+     * The collection of UI Options.
+     */
+    private static UIOptionCollection baseOptionCollection = new CLIOptionCollection();
+
     /**
      * The Option comparator to sort the help.
      */
@@ -130,29 +136,23 @@ public final class OptionCollection {
      */
     public static ReportConfiguration parseCommands(final File workingDirectory, final String[] args,
                                                     final Consumer<Options> helpCmd, final boolean noArgs) throws IOException {
-
         Options opts = buildOptions();
-        CommandLine commandLine;
+        ArgumentContext argumentContext;
         try {
-            commandLine = DefaultParser.builder().setDeprecatedHandler(DeprecationReporter.getLogReporter())
-                    .setAllowPartialMatching(true).build().parse(opts, args);
+            argumentContext = new ArgumentContext(workingDirectory, opts, args);
         } catch (ParseException e) {
-            DefaultLog.getInstance().error(e.getMessage());
-            DefaultLog.getInstance().error("Please use the \"--help\" option to see a list of valid commands and options.", e);
             System.exit(1);
             return null; // dummy return (won't be reached) to avoid Eclipse complaint about possible NPE
             // for "commandLine"
         }
+        Arg.processLogLevel(argumentContext, baseOptionCollection);
 
-        ArgumentContext argumentContext = new ArgumentContext(workingDirectory, commandLine);
-        Arg.processLogLevel(argumentContext, CLIOptionCollection.INSTANCE);
-
-        if (commandLine.hasOption(HELP)) {
+        if (argumentContext.getCommandLine().hasOption(HELP)) {
             helpCmd.accept(opts);
             return null;
         }
 
-        if (commandLine.hasOption(Arg.HELP_LICENSES.option())) {
+        if (argumentContext.getCommandLine().hasOption(Arg.HELP_LICENSES.option())) {
             new Licenses(createConfiguration(argumentContext), new PrintWriter(System.out, false, StandardCharsets.UTF_8)).printHelp();
             return null;
         }
@@ -178,25 +178,38 @@ public final class OptionCollection {
      * @see #parseCommands(File, String[], Consumer, boolean)
      */
     public static ReportConfiguration createConfiguration(final ArgumentContext argumentContext) {
-        argumentContext.processArgs(CLIOptionCollection.INSTANCE);
-        final ReportConfiguration configuration = argumentContext.getConfiguration();
-        final CommandLine commandLine = argumentContext.getCommandLine();
-        Optional<Option> dirOpt = CLIOptionCollection.INSTANCE.getSelected(Arg.DIR);
-        if (dirOpt.isPresent()) {
-            try {
-                configuration.addSource(getReportable(commandLine.getParsedOptionValue(
-                        dirOpt.get()), configuration));
-            } catch (ParseException e) {
-                throw new ConfigurationException("Unable to set parse " + dirOpt.get(), e);
+        try {
+            argumentContext.processArgs(baseOptionCollection);
+            final ReportConfiguration configuration = argumentContext.getConfiguration();
+            final CommandLine commandLine = argumentContext.getCommandLine();
+            Optional<Option> dirOpt = baseOptionCollection.getSelected(Arg.DIR);
+            if (dirOpt.isPresent()) {
+                try {
+                    File directoryName = commandLine.getParsedOptionValue(dirOpt.get());
+                    configuration.addSource(getReportable(directoryName, configuration));
+                } catch (ParseException e) {
+                    throw new ConfigurationException("Unable to set parse " + dirOpt.get(), e);
+                }
             }
-        }
-        for (String s : commandLine.getArgs()) {
-            Reportable reportable = getReportable(new File(s), configuration);
-            if (reportable != null) {
-                configuration.addSource(reportable);
+            for (String s : commandLine.getArgs()) {
+                Reportable reportable = getReportable(new File(s), configuration);
+                if (reportable != null) {
+                    configuration.addSource(reportable);
+                }
+            }
+            return configuration;
+        } catch (RuntimeException e) {
+            try (PrintWriter pw = new PrintWriter(DefaultLog.getInstance().asWriter(Level.ERROR))) {
+                pw.println("Unable to create Configuration: " + e.getMessage());
+                pw.println("=== Command line options ===");
+                for (Option opt : argumentContext.getCommandLine().getOptions()) {
+                    String[] values = opt.getValues();
+                    pw.printf("   %s: %s%n", ArgumentTracker.extractKey(opt), values == null ? "" : String.join(", ", values));
+                }
             }
+            throw new ConfigurationException("Unable to create Configuration", e);
         }
-        return configuration;
+
     }
 
     /**
@@ -205,7 +218,8 @@ public final class OptionCollection {
      * @return the Options comprised of the Options defined in this class.
      */
     public static Options buildOptions() {
-        return CLIOptionCollection.INSTANCE.getOptions();
+        baseOptionCollection.resetSelected();
+        return baseOptionCollection.getOptions();
     }
 
     /**
diff --git a/apache-rat-core/src/main/java/org/apache/rat/OptionCollectionParser.java b/apache-rat-core/src/main/java/org/apache/rat/OptionCollectionParser.java
index 71bef61a..5d256141 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/OptionCollectionParser.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/OptionCollectionParser.java
@@ -32,10 +32,12 @@ import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.rat.api.RatException;
 import org.apache.rat.commandline.Arg;
 import org.apache.rat.commandline.ArgumentContext;
 import org.apache.rat.help.Licenses;
 import org.apache.rat.report.Reportable;
+import org.apache.rat.ui.UIOption;
 import org.apache.rat.ui.UIOptionCollection;
 import org.apache.rat.utils.DefaultLog;
 
@@ -44,15 +46,21 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 /**
  * Uses the AbstractOptionCollection to parse the command line options.
  * Contains utility methods to ReportConfiguration from the options and an array of arguments.
+ *
+ * @param <T> The UIOption type that this parser is handeling.
  */
 @SuppressFBWarnings("EI_EXPOSE_REP2")
-public final class OptionCollectionParser {
+public final class OptionCollectionParser<T extends UIOption<T>> {
     /**
      * The OptionCollection that we are working with.
      */
-    private final UIOptionCollection<?> uiOptionCollection;
+    private final UIOptionCollection<T> uiOptionCollection;
 
-    public OptionCollectionParser(final UIOptionCollection<?> optionCollection) {
+    /**
+     * Constructor.
+     * @param optionCollection  The option collection to use for
+     */
+    public OptionCollectionParser(final UIOptionCollection<T> optionCollection) {
         this.uiOptionCollection = optionCollection;
     }
 
@@ -62,11 +70,10 @@ public final class OptionCollectionParser {
      * @param workingDirectory The directory to resolve relative file names against.
      * @param args the arguments to parse
      * @return the ArgumentContext for the process.
-     * @throws IOException on error.
-     * @throws ParseException on option parsing error.
+     * @throws RatException on error.
      */
     public ArgumentContext parseCommands(final File workingDirectory, final String[] args)
-            throws IOException, ParseException {
+            throws RatException {
         return parseCommands(workingDirectory, args, uiOptionCollection.getOptions());
     }
 
@@ -77,8 +84,7 @@ public final class OptionCollectionParser {
      * @return the CommandLine
      * @throws ParseException on option parsing error.
      */
-    //@VisibleForTesting
-    CommandLine parseCommandLine(final Options opts, final String[] args) throws ParseException {
+    public static CommandLine parseCommandLine(final Options opts, final String[] args) throws ParseException {
         try {
             return DefaultParser.builder().setDeprecatedHandler(DeprecationReporter.getLogReporter())
                     .setAllowPartialMatching(true).build().parse(opts, args);
@@ -89,6 +95,16 @@ public final class OptionCollectionParser {
         }
     }
 
+    // visible for testing
+    void printHelp(final ArgumentContext argumentContext) throws RatException {
+        try {
+            new Licenses(argumentContext.getConfiguration(),
+                    new PrintWriter(argumentContext.getConfiguration().getOutput().get(),
+                            false, StandardCharsets.UTF_8)).printHelp();
+        } catch (IOException e) {
+            throw new RatException("Unable to print help: " + e.getMessage(), e);
+        }
+    }
     /**
      * Parses the standard options to create a ReportConfiguration.
      *
@@ -96,22 +112,22 @@ public final class OptionCollectionParser {
      * @param args the arguments to parse.
      * @param options An Options object containing Apache command line options.
      * @return the ArgumentContext for the process.
-     * @throws IOException on error.
-     * @throws ParseException on option parsing error.
+     * @throws RatException on error.
      */
-    private ArgumentContext parseCommands(final File workingDirectory, final String[] args,
-                                                                       final Options options) throws IOException, ParseException {
-        CommandLine commandLine = parseCommandLine(options, args);
-        ArgumentContext argumentContext = new ArgumentContext(workingDirectory, commandLine);
-        Arg.processLogLevel(argumentContext, uiOptionCollection);
-        populateConfiguration(argumentContext);
-        if (uiOptionCollection.isSelected(Arg.HELP_LICENSES)) {
-            new Licenses(argumentContext.getConfiguration(),
-                    new PrintWriter(argumentContext.getConfiguration().getOutput().get(),
-                            false, StandardCharsets.UTF_8)).printHelp();
+    // visible for testing
+    ArgumentContext parseCommands(final File workingDirectory, final String[] args,
+                                                                       final Options options) throws RatException {
+        try {
+            ArgumentContext argumentContext = new ArgumentContext(workingDirectory, options, args);
+            Arg.processLogLevel(argumentContext, uiOptionCollection);
+            populateConfiguration(argumentContext);
+            if (uiOptionCollection.isSelected(Arg.HELP_LICENSES)) {
+                printHelp(argumentContext);
+            }
+            return argumentContext;
+        } catch (ParseException e) {
+            throw new RatException("Unable to parse command line: " + e.getMessage(), e);
         }
-
-        return argumentContext;
     }
 
     /**
diff --git a/apache-rat-core/src/main/java/org/apache/rat/commandline/ArgumentContext.java b/apache-rat-core/src/main/java/org/apache/rat/commandline/ArgumentContext.java
index d4374a57..cd6135db 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/commandline/ArgumentContext.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/commandline/ArgumentContext.java
@@ -20,9 +20,13 @@ package org.apache.rat.commandline;
 
 import java.io.File;
 
+import org.apache.commons.cli.AlreadySelectedException;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionGroup;
+import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
+import org.apache.rat.OptionCollectionParser;
 import org.apache.rat.ReportConfiguration;
 import org.apache.rat.document.DocumentName;
 import org.apache.rat.ui.UIOptionCollection;
@@ -46,28 +50,51 @@ public final class ArgumentContext {
      * Creates a context with the specified configuration.
      * @param workingDirectory the directory from which relative file names will be resolved.
      * @param configuration The configuration that is being built.
-     * @param commandLine The command line that is building the configuration.
+     * @param opts the Options for the command line.
+     * @param args the arguments for the options.
+     * @throws ParseException if the options can not parse the arguments.
      */
-    public ArgumentContext(final File workingDirectory, final ReportConfiguration configuration, final CommandLine commandLine) {
+    public ArgumentContext(final File workingDirectory, final ReportConfiguration configuration, final Options opts, final String[] args)
+            throws ParseException {
         this.workingDirectory = DocumentName.builder(workingDirectory).build();
-        this.commandLine = commandLine;
+        this.commandLine = OptionCollectionParser.parseCommandLine(clearSelected(opts), args);
         this.configuration = configuration;
     }
 
     /**
      * Creates a context with an empty configuration.
      * @param workingDirectory The directory from which to resolve relative file names.
-     * @param commandLine The command line.
+     * @param opts the Options for the command line.
+     * @param args the arguments for the options.
+     * @throws ParseException if the options can not parse the arguments.
      */
-    public ArgumentContext(final File workingDirectory, final CommandLine commandLine) {
-        this(workingDirectory, new ReportConfiguration(), commandLine);
+    public ArgumentContext(final File workingDirectory, final Options opts, final String[] args) throws ParseException {
+        this(workingDirectory, new ReportConfiguration(), opts, args);
     }
 
+    /**
+     * Clears the group selections in the options.
+     * @param options the options to clear.
+     * @return the options with all selections cleared.
+     */
+    private static Options clearSelected(final Options options) {
+        for (Option opt : options.getOptions()) {
+            OptionGroup group = options.getOptionGroup(opt);
+            if (group != null) {
+                try {
+                    group.setSelected(null);
+                } catch (AlreadySelectedException e) {
+                    throw new RuntimeException("Unexpected error with null argument", e);
+                }
+            }
+        }
+        return options;
+    }
     /**
      * Process the arguments specified in this context.
      */
     public void processArgs(final UIOptionCollection<?> uiOptionCollection) {
-        Arg.processArgs(this,  uiOptionCollection);
+        Arg.processArgs(this, uiOptionCollection);
     }
 
     /**
diff --git a/apache-rat-core/src/main/java/org/apache/rat/help/AbstractHelp.java b/apache-rat-core/src/main/java/org/apache/rat/help/AbstractHelp.java
index 87952338..a1e4c4f0 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/help/AbstractHelp.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/help/AbstractHelp.java
@@ -54,6 +54,11 @@ public abstract class AbstractHelp {
     /** The version info for this instance */
     protected final VersionInfo versionInfo;
 
+    /**
+     * The collection of client options.
+     */
+    protected final CLIOptionCollection cliOptionCollection = new CLIOptionCollection();
+
     /**
      * Base class to perform help output.
      */
@@ -185,7 +190,7 @@ public abstract class AbstractHelp {
                     optBuf.append(END_OF_OPTION_MSG);
                 }
                 // check for default value
-                String defaultValue = CLIOptionCollection.INSTANCE.defaultValue(option);
+                String defaultValue = cliOptionCollection.defaultValue(option);
                 if (defaultValue != null) {
                     optBuf.append(format(" (Default value = %s)", defaultValue));
                 }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/ui/UIOption.java b/apache-rat-core/src/main/java/org/apache/rat/ui/UIOption.java
index fa5533bc..9d04f67d 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/ui/UIOption.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/ui/UIOption.java
@@ -21,7 +21,9 @@ package org.apache.rat.ui;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -38,27 +40,37 @@ import static java.lang.String.format;
  * @param <T> the concrete implementation of AbstractOption.
  */
 public abstract class UIOption<T extends UIOption<T>> {
-    /** The pattern to match CLI options in text */
+    /**
+     * The pattern to match CLI options in text
+     */
     protected static final Pattern PATTERN = Pattern.compile("-?-([A-Za-z0-9]+-?)+"); // NOSONAR
-    /** The actual UI-specific name for the option */
+    /**
+     * The actual UI-specific name for the option
+     */
     protected final Option option;
-    /** The name for the option */
+    /**
+     * The name for the option
+     */
     protected final CasedString name;
-    /** The argument type for this option */
+    /**
+     * The argument type for this option
+     */
     protected final OptionCollection.ArgumentType argumentType;
-    /** The AbstractOptionCollection associated with this AbstractOption */
+    /**
+     * The AbstractOptionCollection associated with this AbstractOption
+     */
     protected final UIOptionCollection<T> optionCollection;
 
     /**
      * Constructor.
      *
-     * @param option The CLI option
-     * @param name the UI-specific name for the option
+     * @param builder UIOption builder.
      */
-    protected <C extends UIOptionCollection<T>> UIOption(final C optionCollection, final Option option, final CasedString name) {
-        this.optionCollection = optionCollection;
-        this.option = option;
-        this.name = name;
+    protected UIOption(final Builder<T, ?> builder) {
+        this.optionCollection = builder.optionCollection;
+        this.option = builder.option;
+        this.name = builder.name();
+
         OptionCollection.ArgumentType argType;
         if (option.hasArg()) {
             if (option.getArgName() == null) {
@@ -77,6 +89,20 @@ public abstract class UIOption<T extends UIOption<T>> {
         this.argumentType = argType;
     }
 
+    @Override
+    public boolean equals(final Object o) {
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        UIOption<?> uiOption = (UIOption<?>) o;
+        return getOption().equals(uiOption.getOption()) && name.equals(uiOption.name) && argumentType == uiOption.argumentType;
+    }
+
+    @Override
+    public int hashCode() {
+        return getName().hashCode();
+    }
+
     /**
      * Gets the AbstractOptionCollection that this option is a member of.
      * @return the AbstractOptionCollection that this option is a member of.
@@ -240,6 +266,109 @@ public abstract class UIOption<T extends UIOption<T>> {
      * @return the deprecated string if the option is deprecated, or an empty string otherwise.
      */
     public final String getDeprecated() {
-        return  option.isDeprecated() ? cleanup(StringUtils.defaultIfEmpty(option.getDeprecated().toString(), StringUtils.EMPTY)) : StringUtils.EMPTY;
+        return option.isDeprecated() ? cleanup(StringUtils.defaultIfEmpty(option.getDeprecated().toString(), StringUtils.EMPTY)) : StringUtils.EMPTY;
+    }
+
+    /**
+     * The abstract UIOption Builder.
+     * @param <T> the concrete type of the UIOption this builder produces.
+     * @param <B> the concrete type of this builder.
+     */
+    public abstract static class Builder<T extends UIOption<T>, B extends Builder<T, B>> {
+        /**
+         * The collection to add the new UI Option to.
+         */
+        private UIOptionCollection<T> optionCollection;
+        /**
+         * THe base option that is being mapped.
+         */
+        private Option option;
+
+        /**
+         * Constructor.
+         */
+        protected Builder() {
+        }
+
+        /**
+         * Returns a function to convert an Option to a CasedString that is the native name for the option.
+         * @return a function to convert an Option to a CasedString that is the native name for the option.
+         */
+        protected abstract Function<Option, CasedString> getNameFactory();
+
+        /**
+         * Gets the option.
+         * @return the option or {@code null} if the option is not set.
+         */
+        protected final Option option() {
+            return option;
+        }
+
+        /**
+         * Gets UI name for the .
+         * @return the option or {@code null} if the option is not set.
+         */
+        protected final CasedString name() {
+            return option == null ? null : getNameFactory().apply(option);
+        }
+
+        /**
+         * Gets the UIOptionCollection that the options will be added to.
+         * @return UIOptionCollection that the options will be added to.
+         */
+        protected final UIOptionCollection<T> optionCollection() {
+            return optionCollection;
+        }
+
+        /**
+         * Returns this builder cast to the builder type.
+         * Useful for implementing fluent builders.
+         * @return this builder.
+         */
+        protected final B self() {
+            return (B) this;
+        }
+
+        /**
+         * Sets the UIOptionCollection that the UIOptions will be added to.
+         * @param optionCollection the UIOptionCollection that the UIOptions will be added to.
+         * @return this
+         */
+        public B optionCollection(final UIOptionCollection<T> optionCollection) {
+            this.optionCollection = optionCollection;
+            return self();
+        }
+
+        /**
+         * Sets the Option that the UIOption will be generated from.
+         * @param option to build the UIOption from.
+         * @return this
+         */
+        public B option(final Option option) {
+            this.option = option;
+            return self();
+        }
+
+        /**
+         * Executes the final build.  Implemetation should use the builder to construct an instance of {@link <></>}
+         * @return An instance of the UIOption.
+         * @throws IllegalArgumentException if values are not set correctly.
+         */
+        protected abstract T doBuild() throws IllegalArgumentException;
+
+        /**
+         * Builds the UIOption.
+         * @return the UIOption.
+         * @throws IllegalArgumentException if values are not set correctly.
+         */
+        public final T build() throws IllegalArgumentException {
+            Objects.requireNonNull(optionCollection, "OptionCollection may not be null");
+            Objects.requireNonNull(option, "Option may not be null");
+            CasedString name = name();
+            if (name == null || name.isNull()) {
+                throw new IllegalArgumentException("name may not be null or contain a null value");
+            }
+            return doBuild();
+        }
     }
 }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/ui/UIOptionCollection.java b/apache-rat-core/src/main/java/org/apache/rat/ui/UIOptionCollection.java
index e88f8440..9d1a3e80 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/ui/UIOptionCollection.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/ui/UIOptionCollection.java
@@ -26,9 +26,10 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.TreeMap;
-import java.util.function.BiFunction;
+import java.util.function.Supplier;
 import java.util.stream.Stream;
 
+import org.apache.commons.cli.AlreadySelectedException;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
 import org.apache.rat.Defaults;
@@ -37,7 +38,7 @@ import org.apache.rat.utils.Log;
 
 /**
  * A collection of options supported by the UI. This includes RAT options and UI specific options.
- * @param <T> the AbstractOption implementation.
+ * @param <T> the UIOption implementation.
  */
 public class UIOptionCollection<T extends UIOption<T>> {
     /** map of ARG to the associated UpdatableOptionGroup */
@@ -52,19 +53,13 @@ public class UIOptionCollection<T extends UIOption<T>> {
      */
     private final Map <Option, String> defaultValues;
 
-    /**
-     * The function to generate a concrete BaseOption instance.
-     */
-    private final BiFunction<UIOptionCollection<T>, Option, T> mapper;
-
     /**
      * Construct the UIOptionCollection from the builder.
-     * @param builder the builder to build from.
+     * @param builder the Builder to build from.
      */
     protected UIOptionCollection(final Builder<T, ?> builder) {
-        Objects.requireNonNull(builder.mapper, "Builder.mapper");
+        Objects.requireNonNull(builder.uiOptionBuilderSupplier, "Builder.mapper");
         argMap = new TreeMap<>();
-        mapper = builder.mapper;
         supportedRatOptions = new UpdatableOptionGroupCollection();
 
         for (Arg arg : Arg.values()) {
@@ -75,15 +70,28 @@ public class UIOptionCollection<T extends UIOption<T>> {
             supportedRatOptions.findGroups(opt).forEach(group -> group.disableOption(opt));
         }
         uiOptions = new HashMap<>();
+        UIOption.Builder<T, ?> optBuilder = builder.uiOptionBuilderSupplier.get();
+        optBuilder.optionCollection(this);
         supportedRatOptions.options().getOptions()
-                .forEach(option -> uiOptions.put(option, mapper.apply(this, option)));
+                .forEach(option -> uiOptions.put(option, optBuilder.option(option).build()));
         builder.uiOptions.stream().filter(option -> !uiOptions.containsKey(option))
-                .forEach(option -> uiOptions.put(option, mapper.apply(this, option)));
+                .forEach(option -> uiOptions.put(option, optBuilder.option(option).build()));
         defaultValues = new HashMap<>(builder.defaultValues);
     }
 
     /**
-     * Checks if an Arg is selected.
+     * Extracts the UI based name string for an option.
+     * The default implementation return the string returned by {@link ArgumentTracker#extractKey(Option)}
+     *
+     * @param option the option to create the name for.
+     * @return the name for the option in the UICollection name, may be the same as the option name.
+     */
+    public String rename(final Option option) {
+        return ArgumentTracker.extractKey(option);
+    }
+
+    /**
+     * Checks if an {@link Arg} is selected.
      * @param arg the Arg to check.
      * @return {@code true} if the arg is selected.
      */
@@ -93,8 +101,8 @@ public class UIOptionCollection<T extends UIOption<T>> {
     }
 
     /**
-     * Gets the selected Option for the arg.
-     * @param arg the arg to check.
+     * Gets the selected Option for an Arg.
+     * @param arg the Arg to get the Option for..
      * @return an Optional containing the selected option, or an empty Optional if none was selected.
      */
     public final Optional<Option> getSelected(final Arg arg) {
@@ -110,43 +118,57 @@ public class UIOptionCollection<T extends UIOption<T>> {
         return Optional.empty();
     }
 
+    /**
+     * Resets the groups in the Args so that they are unused and ready to detect the next set of arguments.
+     */
+    public void resetSelected() {
+        argMap.values().forEach(uog -> {
+            try {
+                uog.setSelected(null);
+            } catch (AlreadySelectedException e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
+
     /**
      * Gets the collection of unsupported Options.
-     * @return the Options comprised for the unsupported options.
+     * @return the Options comprising the unsupported options.
      */
     public final Options getUnsupportedOptions() {
         return supportedRatOptions.unsupportedOptions();
     }
 
     /**
-     * Gets the UiOption instance for the Option.
+     * Gets the UIOption instance for the Option.
      * @param option the option to find the instance of.
-     * @return a UIOption instance that wraps the option.
+     * @return an Optional containing the UIOption for the option, or an empty Optional if the option is not in the UI.
      */
     public final Optional<T> getMappedOption(final Option option) {
         return Optional.ofNullable(uiOptions.get(option));
     }
 
     /**
-     * Gets an Options that contains the RAT Arg defined Option instances that are understood by this collection.
+     * Gets an Options that contains the Options and OptionGroups are understood by this collection.
      * OptionGroups are registered in the resulting Options object.
-     * @return an Options that contains the RAT Arg defined Option instances that are understood by this collection.
+     * @return an Options that contains the {@link Arg} defined Option instances that are understood by this collection as well as
+     * any additional custom Options.
      */
     public final Options getOptions() {
         return supportedRatOptions.options().addOptions(additionalOptions());
     }
 
     /**
-     * Gets the Stream of AbstractOption implementations understood by this collection.
-     * @return the Stream of AbstractOption implementations understood by this collection.
+     * Gets the Stream of UIOption implementations understood by this collection.
+     * @return the Stream of UIOption implementations understood by this collection.
      */
     public final Stream<T> getMappedOptions() {
         return uiOptions.values().stream();
     }
 
     /**
-     * Gets a map client option name to specified AbstractOption implementation.
-     * @return a map client option name to specified AbstractOption implementation
+     * Gets a map client option name to the specified UIOption implementation.
+     * @return a map client option name to thge specified UIOption implementation.
      */
     public final Map<String, T> getOptionMap() {
         Map<String, T> result = new TreeMap<>();
@@ -155,8 +177,8 @@ public class UIOptionCollection<T extends UIOption<T>> {
     }
 
     /**
-     * Gets the additional options understood by this collection.
-     * @return the additional options understood by this collection.
+     * Gets the additional Options understood by this collection.
+     * @return the additional Options understood by this collection.
      */
     public final Options additionalOptions() {
         Options options = new Options();
@@ -176,11 +198,11 @@ public class UIOptionCollection<T extends UIOption<T>> {
     }
 
     /**
-     * Builder for a BaseOptionCollection.
-     * @param <T> the concreate type of the BaseOption.
-     * @param <S> the concrete type being built.
+     * Builder for a UIOptionCollection.
+     * @param <T> the concreate type of the UIOption.
+     * @param <B> the concrete type the Builder.
      */
-    protected static class Builder<T extends UIOption<T>, S extends Builder<T, S>> {
+    protected static class Builder<T extends UIOption<T>, B extends Builder<T, B>> {
         /** set of additional UI specific options */
         private final List<Option> uiOptions;
         /**
@@ -191,13 +213,13 @@ public class UIOptionCollection<T extends UIOption<T>> {
         /** The list of unsupported RAT options. */
         protected final List<Option> unsupportedRatOptions;
         /** The function to convert an option into a UIOption. */
-        private final BiFunction<UIOptionCollection<T>, Option, T> mapper;
+        private final Supplier<UIOption.Builder<T, ?>> uiOptionBuilderSupplier;
 
         /**
-         * Constructor for the builder.
+         * Constructor for the UI option collection builder.
          */
-        protected Builder(final BiFunction<UIOptionCollection<T>, Option, T> mapper) {
-            this.mapper = mapper;
+        protected Builder(final Supplier<UIOption.Builder<T, ?>> uiOptionBiulderSupplier) {
+            this.uiOptionBuilderSupplier = uiOptionBiulderSupplier;
             uiOptions = new ArrayList<>();
             defaultValues = new HashMap<>();
             unsupportedRatOptions = new ArrayList<>();
@@ -209,59 +231,51 @@ public class UIOptionCollection<T extends UIOption<T>> {
         }
 
         /**
-         * Build the UIOptionCollection.
-         * @return the UIOptionCollection.
-         */
-        public UIOptionCollection<T> build() {
-            return new UIOptionCollection<>(this);
-        }
-
-        /**
-         * Returns this cast to {@code <S>} class.
-         * @return this as {@code <S>} class.
+         * Returns this cast to {@code <B>} class.
+         * @return this as {@code <B>} class.
          */
-        protected final S self() {
-            return (S) this;
+        protected final B self() {
+            return (B) this;
         }
 
         /**
-         * Add a UI option to the collection.
-         * @param uiOption the UI Option to add.
+         * Add an Option to the collection as a UIOption.
+         * @param option the Option to add.
          * @return this
          */
-        public S uiOption(final Option uiOption) {
-            uiOptions.add(uiOption);
+        public B uiOption(final Option option) {
+            uiOptions.add(option);
             return self();
         }
 
         /**
-         * Add a UI options to the collection.
-         * @param uiOption the UIOptions ({@code <T>} objects) to add.
+         * Add multiple Option instances to the collection as UIOptions.
+         * @param options the Option instances to add.
          * @return this
          */
-        public S uiOptions(final Option... uiOption) {
-            uiOptions.addAll(Arrays.asList(uiOption));
+        public B uiOptions(final Option... options) {
+            uiOptions.addAll(Arrays.asList(options));
             return self();
         }
 
         /**
-         * Register an option as unsupported.
-         * @param option the option that is not be supported. This should be an option in the
+         * Register an Option as unsupported.
+         * @param option the Option that is not be supported. This should be an option in the
          * {@link Arg} collection.
          * @return this
          */
-        public S unsupported(final Option option) {
+        public B unsupported(final Option option) {
             unsupportedRatOptions.add(option);
             return self();
         }
 
         /**
-         * Register multiple options as unsupported.
+         * Register multiple Options as unsupported.
          * Will ignore all the options associated with the specified Arg.
          * @param arg The Arg to ignore.
          * @return this
          */
-        public S unsupported(final Arg arg) {
+        public B unsupported(final Arg arg) {
             unsupportedRatOptions.addAll(arg.group().getOptions());
             return self();
         }
@@ -272,7 +286,7 @@ public class UIOptionCollection<T extends UIOption<T>> {
          * @param value the value for the option.
          * @return this
          */
-        public S defaultValue(final Option option, final String value) {
+        public B defaultValue(final Option option, final String value) {
             defaultValues.put(option, value);
             return self();
         }
@@ -283,7 +297,7 @@ public class UIOptionCollection<T extends UIOption<T>> {
          * @param value the value for the option.
          * @return this
          */
-        public S defaultValue(final Arg arg, final String value) {
+        public B defaultValue(final Arg arg, final String value) {
             return defaultValue(arg.option(), value);
         }
     }
diff --git a/apache-rat-core/src/main/java/org/apache/rat/ui/UpdatableOptionGroup.java b/apache-rat-core/src/main/java/org/apache/rat/ui/UpdatableOptionGroup.java
index 42181e99..7e6948c1 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/ui/UpdatableOptionGroup.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/ui/UpdatableOptionGroup.java
@@ -65,6 +65,7 @@ public final class UpdatableOptionGroup extends OptionGroup {
     public Stream<Option> getDisableOptions() {
         return disabledOptions.stream();
     }
+
     /**
      * Reset the group so that all disabled options are re-enabled.
      */
diff --git a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java
index 72478bdf..cdd2f134 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java
@@ -62,7 +62,7 @@ public final class CasedString {
      * @param segments the segments of the string.
      */
     public CasedString(final StringCase stringCase, final String[] segments) {
-        this.segments = segments;
+        this.segments = segments.length == 0 ? StringCase.NULL_SEGMENT : segments;
         this.stringCase = stringCase;
     }
 
@@ -72,7 +72,15 @@ public final class CasedString {
      * @return the new CasedString.
      */
     public CasedString as(final StringCase stringCase) {
-        return stringCase.name.equals(this.stringCase.name) ? this : new CasedString(stringCase, Arrays.copyOf(this.segments, this.segments.length));
+        return stringCase.name.equals(this.stringCase.name) ? this : new CasedString(stringCase, copySegments());
+    }
+
+    private String[] copySegments() {
+        return this.segments.length == 0 ? StringCase.NULL_SEGMENT : Arrays.copyOf(this.segments, this.segments.length);
+    }
+
+    public boolean isNull() {
+        return segments.length == 0;
     }
 
     /**
@@ -103,7 +111,7 @@ public final class CasedString {
             return false;
         }
         CasedString that = (CasedString) o;
-        return Objects.deepEquals(getSegments(), that.getSegments()) && Objects.equals(stringCase, that.stringCase);
+        return Objects.equals(stringCase, that.stringCase) && Objects.deepEquals(getSegments(), that.getSegments());
     }
 
     @Override
diff --git a/apache-rat-core/src/test/java/org/apache/rat/CLIOptionTest.java b/apache-rat-core/src/test/java/org/apache/rat/CLIOptionTest.java
index 028c25fd..de02cee6 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/CLIOptionTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/CLIOptionTest.java
@@ -26,10 +26,12 @@ import java.io.File;
 import static org.assertj.core.api.Assertions.assertThat;
 
 class CLIOptionTest {
-    final CLIOption optionA = new CLIOption(CLIOptionCollection.INSTANCE, new Option("a", false, "short key"));
-    final CLIOption optionB = new CLIOption(CLIOptionCollection.INSTANCE, Option.builder("b").longOpt("bee").hasArg().desc("two key").build());
-    final CLIOption optionC = new CLIOption(CLIOptionCollection.INSTANCE, Option.builder().longOpt("sea").hasArgs().type(File.class).desc("long key").build());
-    final CLIOption optionD = new CLIOption(CLIOptionCollection.INSTANCE, Option.builder().longOpt("dee").hasArgs().argName("dede").desc("long key").build());
+    final CLIOptionCollection cliOptionCollection = new CLIOptionCollection();
+    final CLIOption.CLIBuilder builder = new CLIOption.CLIBuilder().optionCollection(cliOptionCollection);
+    final CLIOption optionA = builder.option(new Option("a", false, "short key")).build();
+    final CLIOption optionB = builder.option(Option.builder("b").longOpt("bee").hasArg().desc("two key").build()).build();
+    final CLIOption optionC = builder.option(Option.builder().longOpt("sea").hasArgs().type(File.class).desc("long key").build()).build();
+    final CLIOption optionD = builder.option(Option.builder().longOpt("dee").hasArgs().argName("dede").desc("long key").build()).build();
 
     @Test
     void getText() {
diff --git a/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionParserTest.java b/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionParserTest.java
index f18ee2ab..3fb8f4ff 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionParserTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionParserTest.java
@@ -19,19 +19,26 @@
 package org.apache.rat;
 
 import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
+import org.apache.rat.api.RatException;
 import org.apache.rat.commandline.ArgumentContext;
+import org.apache.rat.testhelpers.TestingLog;
+import org.apache.rat.ui.ArgumentTracker;
 import org.apache.rat.ui.UIOption;
 import org.apache.rat.ui.UIOptionCollection;
 import org.apache.rat.utils.CasedString;
+import org.apache.rat.utils.DefaultLog;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.CleanupMode;
 import org.junit.jupiter.api.io.TempDir;
 
 import java.io.IOException;
 import java.nio.file.Path;
+import java.util.function.Function;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 class OptionCollectionParserTest {
 
@@ -39,10 +46,10 @@ class OptionCollectionParserTest {
     static Path testPath;
 
     private final TestOptionCollection optionCollection = new TestOptionCollection();
-    private final OptionCollectionParser underTest = new OptionCollectionParser(optionCollection);
+    private final OptionCollectionParser<TestOption> underTest = new OptionCollectionParser<>(optionCollection);
 
     @Test
-    void parseCommands() throws IOException, ParseException {
+    void parseCommands() throws RatException {
         String[] args = {"arg1", "arg2"};
         ArgumentContext ctxt = underTest.parseCommands(testPath.toFile(), args);
         assertThat(ctxt.getCommandLine().getArgList()).containsExactly(args);
@@ -55,6 +62,35 @@ class OptionCollectionParserTest {
         assertThat(ctxt.getCommandLine().getArgList()).containsExactly(args);
     }
 
+    @Test
+    void parseCommandLineParseExceptionTest() {
+        Options options = new Options();
+        options.addOption(Option.builder("req").required().build());
+        TestingLog testingLog = new TestingLog();
+        try {
+            DefaultLog.setInstance(testingLog);
+            assertThatThrownBy(() -> underTest.parseCommandLine(options, new String[0]))
+                    .isInstanceOf(ParseException.class);
+        } finally {
+            DefaultLog.setInstance(null);
+        }
+        assertThat(testingLog.getCaptured()).containsOnlyOnce("Please use the \"--help\" option to see a list of valid commands and options.");
+    }
+
+    @Test
+    void printHelpExceptionTest() throws ParseException {
+        Options options = new Options();
+        ReportConfiguration cfg = new ReportConfiguration();
+        ArgumentContext ctxt = new ArgumentContext(testPath.toFile(), cfg, options, new String[0]);
+        cfg.setOut(new ReportConfiguration.IODescriptor("Bad Supplier", () -> { throw new IOException("Bad Supplier");}));
+        assertThatThrownBy(() -> underTest.printHelp(ctxt))
+                .isInstanceOf(RatException.class)
+                .hasMessageContaining("Unable to print help: Bad Supplier");
+    }
+
+    /**
+     * A UIOption implementation to support testing.
+     */
     static class TestOption extends UIOption<TestOption> {
 
         /**
@@ -63,8 +99,8 @@ class OptionCollectionParserTest {
          * @param optionCollection the collection the UIOption belongs to.
          * @param option           The CLI option
          */
-        protected <C extends UIOptionCollection<TestOption>> TestOption(C optionCollection, Option option) {
-            super(optionCollection, option, new CasedString(CasedString.StringCase.CAMEL, option.getKey()));
+        protected <C extends UIOptionCollection<TestOption>> TestOption(TestOptionBuilder builder) {
+            super(builder);
         }
 
         @Override
@@ -81,8 +117,24 @@ class OptionCollectionParserTest {
         public String getText() {
             return "text for " + option.toString();
         }
+
+        public static class TestOptionBuilder extends UIOption.Builder<TestOption, TestOptionBuilder> {
+
+            @Override
+            protected Function<Option, CasedString> getNameFactory() {
+                return ArgumentTracker::extractName;
+            }
+
+            @Override
+            protected TestOption doBuild() {
+                return new TestOption(this);
+            }
+        }
     }
 
+    /**
+     * A UIOptionCollection implementation for testing.  Contains TestOptions.
+     */
     static class TestOptionCollection extends UIOptionCollection<TestOption> {
         /**
          * Construct the UIOptionCollection from the builder.
@@ -93,7 +145,7 @@ class OptionCollectionParserTest {
 
         static class TestCollectionBuilder extends UIOptionCollection.Builder<TestOption, TestCollectionBuilder> {
             TestCollectionBuilder() {
-                super(TestOption::new);
+                super(TestOption.TestOptionBuilder::new);
             }
         }
     }
diff --git a/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java b/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java
index 070484f2..ccc7e3bb 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java
@@ -30,8 +30,6 @@ import java.nio.file.Path;
 import java.util.Locale;
 import java.util.Map;
 import java.util.TreeMap;
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.DefaultParser;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.ParseException;
 import org.apache.commons.lang3.tuple.Pair;
@@ -220,8 +218,7 @@ public class OptionCollectionTest {
     @Test
     public void testDefaultConfiguration() throws ParseException {
         String[] empty = {};
-        CommandLine cl = new DefaultParser().parse(OptionCollection.buildOptions(), empty);
-        ArgumentContext context = new ArgumentContext(new File("."), cl);
+        ArgumentContext context = new ArgumentContext(new File("."), OptionCollection.buildOptions(), empty);
         ReportConfiguration config = OptionCollection.createConfiguration(context);
         ReportConfigurationTest.validateDefault(config);
     }
diff --git a/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java b/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java
index c0f2da00..306fb809 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java
@@ -23,16 +23,23 @@ import static org.assertj.core.api.Fail.fail;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.IOException;
 import java.io.PrintStream;
+import java.lang.reflect.Method;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
 
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.TreeMap;
+import java.util.UUID;
+import java.util.regex.Pattern;
 import javax.xml.XMLConstants;
 import javax.xml.transform.Source;
 import javax.xml.transform.TransformerException;
@@ -45,9 +52,6 @@ import javax.xml.xpath.XPath;
 import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathFactory;
 
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.DefaultParser;
-import org.apache.commons.cli.ParseException;
 import org.apache.commons.io.FileUtils;
 import org.apache.rat.api.Document.Type;
 import org.apache.rat.api.RatException;
@@ -58,12 +62,16 @@ import org.apache.rat.license.ILicenseFamily;
 import org.apache.rat.report.claim.ClaimStatistic;
 import org.apache.rat.report.claim.ClaimStatisticTest;
 import org.apache.rat.test.utils.Resources;
-import org.apache.rat.testhelpers.TextUtils;
+import org.apache.rat.testhelpers.BaseOption;
+import org.apache.rat.testhelpers.BaseOptionCollection;
 import org.apache.rat.testhelpers.XmlUtils;
 import org.apache.rat.utils.StandardXmlFactory;
 import org.apache.rat.walker.DirectoryWalker;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.api.TestInfo;
 import org.w3c.dom.Document;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
@@ -72,22 +80,60 @@ import org.xml.sax.SAXException;
  * Tests the output of the Reporter.
  */
 public class ReporterTest {
-    @TempDir
-    File tempDirectory;
+    /**
+     * temporary file.  Not using tempDir because it does not
+     * always work.
+     */
+    private static Path tempPath;
+
+    /**
+     * The directory for the test.
+     */
+    private Path testPath;
+
+    /**
+     * Directory for the test data.
+     */
     final String basedir;
 
+    private final OptionCollectionParser<BaseOption> collectionParser;
+
     ReporterTest() throws URISyntaxException {
         basedir = Resources.getExampleResource("exampleData").getPath();
+        collectionParser = new OptionCollectionParser<>(new BaseOptionCollection());
     }
 
-    @Test
-    void testExecute() throws RatException, ParseException {
-        File output = new File(tempDirectory, "testExecute");
+    @BeforeAll
+    static void setUp() throws IOException {
+        tempPath = Files.createTempDirectory("ReporterTest");
+    }
 
-        CommandLine cl = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{"--output-style", "xml", "--output-file", output.getPath(), basedir});
-        ArgumentContext context = new ArgumentContext(new File("."), cl);
-        ReportConfiguration config = OptionCollection.createConfiguration(context);
-        ClaimStatistic statistic = new Reporter(config).execute().getStatistic();
+    @AfterAll
+    static void cleanup() throws IOException {
+        FileUtils.deleteDirectory(tempPath.toFile());
+    }
+
+    @BeforeEach
+    void setUpTest(TestInfo testInfo) {
+        Optional<Method> testMethod = testInfo.getTestMethod();
+        this.testPath = tempPath.resolve(testMethod.map(Method::getName).orElseGet(() -> UUID.randomUUID().toString()));
+        File file = testPath.toFile();
+        if (!file.exists()) {
+            if (!file.mkdirs()) {
+                throw new RuntimeException("Unable to create temp directory: " + file.getName());
+            }
+        } else {
+            if (!file.isDirectory()) {
+                throw new RuntimeException(file.getName() + " is NOT a directory.");
+            }
+        }
+    }
+
+    @Test
+    void testExecute() throws RatException {
+        File output = testPath.resolve("output.xml").toFile();
+        ArgumentContext ctxt = collectionParser.parseCommands(new File("."), new String[]{"--output-style", "xml", "--output-file", output.getPath(), basedir});
+        ClaimStatistic statistic = new Reporter(ctxt.getConfiguration()).execute().getStatistic();
 
         assertThat(statistic.getCounter(Type.ARCHIVE)).isEqualTo(1);
         assertThat(statistic.getCounter(Type.BINARY)).isEqualTo(2);
@@ -136,11 +182,9 @@ public class ReporterTest {
     }
 
     @Test
-    void testExecuteNoSource() throws ParseException, RatException, TransformerException {
-        File output = new File(tempDirectory, "testExecuteNoSource");
-
-        CommandLine cl = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{"--output-style", "xml", "--output-file", output.getPath()});
-        ArgumentContext context = new ArgumentContext(new File("."), cl);
+    void testExecuteNoSource() throws RatException, TransformerException {
+        File output = testPath.resolve("testExecuteNoSource").toFile();
+        ArgumentContext context = collectionParser.parseCommands(new File("."), new String[]{"--output-style", "xml", "--output-file", output.getPath()});
         ReportConfiguration config = OptionCollection.createConfiguration(context);
         Reporter.Output result = new Reporter(config).execute();
         assertThat(StandardXmlFactory.serializeDocument(result.getDocument())).isEmpty();
@@ -149,43 +193,34 @@ public class ReporterTest {
 
     @Test
     void testOutputOption() throws Exception {
-        File output = new File(tempDirectory, "test");
-        CommandLine commandLine = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{"-o", output.getCanonicalPath(), basedir});
-        ArgumentContext context = new ArgumentContext(new File("."), commandLine);
-
-        ReportConfiguration config = OptionCollection.createConfiguration(context);
-        new Reporter(config).execute().format(config);
+        File output = testPath.resolve("testOutputOption.txt").toFile();
+        ArgumentContext ctxt = collectionParser.parseCommands(new File("."), new String[]{"--output-file", output.getCanonicalPath(), basedir});
+        new Reporter(ctxt.getConfiguration()).execute().format(ctxt.getConfiguration());
         assertThat(output.exists()).isTrue();
         String content = FileUtils.readFileToString(output, StandardCharsets.UTF_8);
-        TextUtils.assertPatternInTarget("^! Unapproved:\\s*2 ", content);
-        assertThat(content).contains("/Source.java");
-        assertThat(content).contains("/sub/Empty.txt");
+        assertThat(content).containsPattern(Pattern.compile("^! Unapproved:\\s*2 ", Pattern.MULTILINE))
+                .contains("/Source.java")
+                .contains("/sub/Empty.txt");
     }
 
     @Test
     void testGetOutputMethod() throws Exception {
-        File output = new File(tempDirectory, "test");
-        CommandLine commandLine = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{"-o", output.getCanonicalPath(), basedir});
-        ArgumentContext context = new ArgumentContext(new File("."), commandLine);
-
-        ReportConfiguration config = OptionCollection.createConfiguration(context);
-        Reporter reporter = new Reporter(config);
+        File output = testPath.resolve("testGetOutputMethod.txt").toFile();
+        ArgumentContext context = collectionParser.parseCommands(new File("."), new String[]{"-o", output.getCanonicalPath(), basedir});
+        Reporter reporter = new Reporter(context.getConfiguration());
         Reporter.Output expected = reporter.execute();
         assertThat(reporter.getOutput()).isEqualTo(expected);
     }
 
     @Test
     void testDefaultOutput() throws Exception {
-        File output = new File(tempDirectory, "testDefaultOutput");
+        File output = testPath.resolve("captured.txt").toFile();
 
         PrintStream origin = System.out;
         try (PrintStream out = new PrintStream(output)) {
             System.setOut(out);
-            CommandLine commandLine = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{basedir});
-            ArgumentContext context = new ArgumentContext(new File("."), commandLine);
-
-            ReportConfiguration config = OptionCollection.createConfiguration(context);
-            new Reporter(config).execute().format(config);
+            ArgumentContext ctxt = collectionParser.parseCommands(new File("."), new String[]{basedir});
+            new Reporter(ctxt.getConfiguration()).execute().format(ctxt.getConfiguration());
         } finally {
             System.setOut(origin);
         }
@@ -233,13 +268,10 @@ public class ReporterTest {
         expected.put("/tri.txt", mapOf("encoding", "ISO-8859-1", "mediaType", "text/plain",
                 "type", "STANDARD"));
 
-        File output = new File(tempDirectory, ".rat/testXMLOutput");
-        output.getParentFile().mkdir();
-        CommandLine commandLine = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{"--output-style", "xml", "--output-file", output.getPath(), basedir});
-        ArgumentContext context = new ArgumentContext(tempDirectory, commandLine);
-
-        ReportConfiguration config = OptionCollection.createConfiguration(context);
-        new Reporter(config).execute().format(config);
+        File output = testPath.resolve(".rat/testXMLOutput").toFile();
+        output.getParentFile().mkdirs();
+        ArgumentContext ctxt = collectionParser.parseCommands(testPath.toFile(), new String[]{"--output-style", "xml", "--output-file", output.getPath(), basedir});
+        new Reporter(ctxt.getConfiguration()).execute().format(ctxt.getConfiguration());
 
         assertThat(output).exists();
         Document doc = XmlUtils.toDom(java.nio.file.Files.newInputStream(output.toPath()));
@@ -368,56 +400,49 @@ public class ReporterTest {
     }
 
     private void verifyStandardContent(final String document) {
-        TextUtils.assertPatternInTarget("^  Notices:\\s*2 ", document);
-        TextUtils.assertPatternInTarget("^  Binaries:\\s*2 ", document);
-        TextUtils.assertPatternInTarget("^  Archives:\\s*1 ", document);
-        TextUtils.assertPatternInTarget("^  Standards:\\s*8 ", document);
-        TextUtils.assertPatternInTarget("^  Ignored:\\s*2 ", document);
-        TextUtils.assertPatternInTarget("^! Unapproved:\\s*2 ", document);
-        TextUtils.assertPatternInTarget("^  Unknown:\\s*2 ", document);
-
-        TextUtils.assertPatternInTarget("^Apache License 2.0: 5 ", document);
-        TextUtils.assertPatternInTarget("^BSD 3 clause: 1 ", document);
-        TextUtils.assertPatternInTarget("^The MIT License: 1 ", document);
-        TextUtils.assertPatternInTarget("^The Telemanagement Forum License: 1 ", document);
-        TextUtils.assertPatternInTarget("^Unknown license: 2 ", document);
-
-        TextUtils.assertPatternInTarget("^\\Q?????\\E: 2 ", document);
-        TextUtils.assertPatternInTarget("^AL   : 5 ", document);
-        TextUtils.assertPatternInTarget("^BSD-3: 2 ", document);
-        TextUtils.assertPatternInTarget("^MIT  : 1 ", document);
-
-        TextUtils.assertPatternInTarget(
-                "^Files with unapproved licenses\\s+\\*+\\s+" //
+        assertThat(document)
+                .containsPattern(Pattern.compile("^  Notices:\\s*2 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^  Binaries:\\s*2 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^  Archives:\\s*1 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^  Standards:\\s*8 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^  Ignored:\\s*2 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^! Unapproved:\\s*2 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^  Unknown:\\s*2 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^Apache License 2.0: 5 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^BSD 3 clause: 1 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^The MIT License: 1 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^The Telemanagement Forum License: 1 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^Unknown license: 2 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^\\Q?????\\E: 2 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^AL   : 5 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^BSD-3: 2 ", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("^MIT  : 1 ", Pattern.MULTILINE))
+                .containsPattern(
+                        Pattern.compile("^Files with unapproved licenses\\s+\\*+\\s+" //
                         + "\\Q/Source.java\\E\\s+" //
-                        + "\\Q/sub/Empty.txt\\E\\s",
-                document);
-        TextUtils.assertPatternInTarget(ReporterTestUtils.documentOut(true, Type.ARCHIVE, "/dummy.jar"),
-                document);
-        TextUtils.assertPatternInTarget(
-                ReporterTestUtils.documentOut(true, Type.STANDARD, "/ILoggerFactory.java")
-                        + ReporterTestUtils.licenseOut("MIT", "The MIT License"),
-                document);
-        TextUtils.assertPatternInTarget(ReporterTestUtils.documentOut(true, Type.BINARY, "/Image.png"),
-                document);
-        TextUtils.assertPatternInTarget(ReporterTestUtils.documentOut(true, Type.NOTICE, "/LICENSE"),
-                document);
-        TextUtils.assertPatternInTarget(ReporterTestUtils.documentOut(true, Type.NOTICE, "/NOTICE"), document);
-        TextUtils.assertPatternInTarget(ReporterTestUtils.documentOut(false, Type.STANDARD, "/Source.java")
-                + ReporterTestUtils.UNKNOWN_LICENSE, document);
-        TextUtils.assertPatternInTarget(ReporterTestUtils.documentOut(true, Type.STANDARD, "/Text.txt")
-                + ReporterTestUtils.APACHE_LICENSE, document);
-        TextUtils.assertPatternInTarget(ReporterTestUtils.documentOut(true, Type.STANDARD, "/Xml.xml")
-                + ReporterTestUtils.APACHE_LICENSE, document);
-        TextUtils.assertPatternInTarget(ReporterTestUtils.documentOut(true, Type.STANDARD, "/buildr.rb")
-                + ReporterTestUtils.APACHE_LICENSE, document);
-        TextUtils.assertPatternInTarget(ReporterTestUtils.documentOut(true, Type.STANDARD, "/TextHttps.txt")
-                + ReporterTestUtils.APACHE_LICENSE, document);
-        TextUtils.assertPatternInTarget(ReporterTestUtils.documentOut(true, Type.STANDARD, "/tri.txt")
+                                + "\\Q/sub/Empty.txt\\E\\s", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.ARCHIVE, "/dummy.jar"), Pattern.MULTILINE))
+                .containsPattern(
+                        Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/ILoggerFactory.java")
+                                + ReporterTestUtils.licenseOut("MIT", "The MIT License"), Pattern.MULTILINE))
+                .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.BINARY, "/Image.png"), Pattern.MULTILINE))
+                .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.NOTICE, "/LICENSE"), Pattern.MULTILINE))
+                .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.NOTICE, "/NOTICE"), Pattern.MULTILINE))
+                .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(false, Type.STANDARD, "/Source.java")
+                        + ReporterTestUtils.UNKNOWN_LICENSE, Pattern.MULTILINE))
+                .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/Text.txt")
+                        + ReporterTestUtils.APACHE_LICENSE, Pattern.MULTILINE))
+                .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/Xml.xml")
+                        + ReporterTestUtils.APACHE_LICENSE, Pattern.MULTILINE))
+                .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/buildr.rb")
+                        + ReporterTestUtils.APACHE_LICENSE, Pattern.MULTILINE))
+                .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/TextHttps.txt")
+                        + ReporterTestUtils.APACHE_LICENSE, Pattern.MULTILINE))
+                .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/tri.txt")
                 + ReporterTestUtils.APACHE_LICENSE + ReporterTestUtils.licenseOut("BSD-3", "BSD 3 clause")
-                + ReporterTestUtils.licenseOut("BSD-3", "TMF", "The Telemanagement Forum License"), document);
-        TextUtils.assertPatternInTarget(ReporterTestUtils.documentOut(false, Type.STANDARD, "/sub/Empty.txt")
-                + ReporterTestUtils.UNKNOWN_LICENSE, document);
+                        + ReporterTestUtils.licenseOut("BSD-3", "TMF", "The Telemanagement Forum License"), Pattern.MULTILINE))
+                .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(false, Type.STANDARD, "/sub/Empty.txt")
+                        + ReporterTestUtils.UNKNOWN_LICENSE, Pattern.MULTILINE));
     }
 
     private Validator initValidator() throws SAXException {
@@ -481,8 +506,9 @@ public class ReporterTest {
 
         String document = out.toString();
 
-        TextUtils.assertNotContains("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", document);
-        assertThat(document).as(() -> "'Generated at' is not present in \n" + document).startsWith(HEADER);
+        assertThat(document).as(() -> "'Generated at' is not present in \n" + document)
+                .doesNotContainPattern("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
+                .startsWith(HEADER);
 
         verifyStandardContent(document);
     }
@@ -497,9 +523,9 @@ public class ReporterTest {
 
         String document = out.toString();
 
-        TextUtils.assertContains("Generated at: ", document );
-        TextUtils.assertPatternInTarget("\\Q/Source.java\\E$", document);
-        TextUtils.assertPatternInTarget("\\Q/sub/Empty.txt\\E", document);
+        assertThat(document).containsOnlyOnce("Generated at: ")
+                .containsPattern(Pattern.compile("\\Q/Source.java\\E$", Pattern.MULTILINE))
+                .containsPattern(Pattern.compile("\\Q/sub/Empty.txt\\E", Pattern.MULTILINE));
     }
 
     @Test
diff --git a/apache-rat-core/src/test/java/org/apache/rat/commandline/ArgTests.java b/apache-rat-core/src/test/java/org/apache/rat/commandline/ArgTests.java
index 742632c5..873d47c5 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/commandline/ArgTests.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/commandline/ArgTests.java
@@ -18,13 +18,8 @@
  */
 package org.apache.rat.commandline;
 
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.DefaultParser;
-import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
 import org.apache.rat.CLIOptionCollection;
-import org.apache.rat.DeprecationReporter;
-import org.apache.rat.OptionCollection;
 import org.apache.rat.ReportConfiguration;
 import org.apache.rat.document.DocumentName;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -35,17 +30,12 @@ import java.nio.file.Path;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
-public class ArgTests {
-
-    private CommandLine createCommandLine(String[] args) throws ParseException {
-        Options opts = OptionCollection.buildOptions();
-        return DefaultParser.builder().setDeprecatedHandler(DeprecationReporter.getLogReporter())
-                .setAllowPartialMatching(true).build().parse(opts, args);
-    }
+class ArgTests {
+    final private CLIOptionCollection cliOptionCollection = new CLIOptionCollection();
 
     @ParameterizedTest(name = "{0}")
     @ValueSource(strings = { "rat.txt", "./rat.txt", "/rat.txt", "target/rat.test" })
-    public void outputFileNameNoDirectoryTest(String name) throws ParseException {
+    void outputFileNameNoDirectoryTest(String name) throws ParseException {
         class OutputFileConfig extends ReportConfiguration {
             private File actual = null;
 
@@ -62,10 +52,9 @@ public class ArgTests {
         Path workingPath = localFile.getAbsoluteFile().toPath();
         String expected = fsInfo.normalize(workingPath.resolve("./" + fileName).toString());
 
-        CommandLine commandLine = createCommandLine(new String[]{"--output-file", fileName});
         OutputFileConfig configuration = new OutputFileConfig();
-        ArgumentContext ctxt = new ArgumentContext(localFile, configuration, commandLine);
-        Arg.processArgs(ctxt, CLIOptionCollection.INSTANCE);
+        ArgumentContext ctxt = new ArgumentContext(localFile, configuration, cliOptionCollection.getOptions(), new String[]{"--output-file", fileName});
+        Arg.processArgs(ctxt, cliOptionCollection);
         if (name.equals("/rat.txt")) {
             assertThat(fsInfo.normalize(configuration.actual.getAbsolutePath())).isEqualTo(localFileName.getRoot() + "rat.txt");
         } else {
diff --git a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOption.java b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOption.java
new file mode 100644
index 00000000..1a7faff2
--- /dev/null
+++ b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOption.java
@@ -0,0 +1,61 @@
+/*
+ * 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   *
+ *                                                              *
+ *   https://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 org.apache.rat.testhelpers;
+
+import org.apache.commons.cli.Option;
+import org.apache.rat.ui.ArgumentTracker;
+import org.apache.rat.ui.UIOption;
+import org.apache.rat.utils.CasedString;
+
+import java.util.function.Function;
+
+public final class BaseOption extends UIOption<BaseOption> {
+    BaseOption(BaseOptionBuilder builder) {
+        super(builder);
+    }
+
+    public Builder builder() {
+        return new BaseOptionBuilder();
+    }
+
+    protected String cleanupName(Option option) {
+        return ArgumentTracker.extractKey(option);
+    }
+
+    public String getExample() {
+        return "";
+    }
+
+    public String getText() {
+        return "";
+    }
+
+    public static class BaseOptionBuilder extends UIOption.Builder<BaseOption, BaseOptionBuilder> {
+
+        @Override
+        protected Function<Option, CasedString> getNameFactory() {
+            return ArgumentTracker::extractName;
+        }
+
+        @Override
+        protected BaseOption doBuild() {
+            return new BaseOption(this);
+        }
+    }
+}
diff --git a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOptionCollection.java b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOptionCollection.java
new file mode 100644
index 00000000..262ae733
--- /dev/null
+++ b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOptionCollection.java
@@ -0,0 +1,36 @@
+/*
+ * 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   *
+ *                                                              *
+ *   https://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 org.apache.rat.testhelpers;
+
+import org.apache.rat.ui.UIOptionCollection;
+
+public final class BaseOptionCollection extends UIOptionCollection<BaseOption> {
+    public BaseOptionCollection() {
+        super(new Builder());
+    }
+
+    public BaseOptionCollection(Builder builder) {
+        super(builder);
+    }
+    public static final class Builder extends UIOptionCollection.Builder<BaseOption, Builder> {
+        public Builder() {
+            super(BaseOption.BaseOptionBuilder::new);
+        }
+    }
+}
diff --git a/apache-rat-core/src/test/java/org/apache/rat/ui/ArgumentTrackerTest.java b/apache-rat-core/src/test/java/org/apache/rat/ui/ArgumentTrackerTest.java
index 74fd2810..4c269c03 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/ui/ArgumentTrackerTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/ui/ArgumentTrackerTest.java
@@ -113,7 +113,7 @@ class ArgumentTrackerTest {
     @Test
     void invalidAbstractOption() {
         Option option = Option.builder().longOpt("notAValidOption").build();
-        TestingUIOption invalidOption = new TestingUIOption(testingUIOptionCollection, option);
+        TestingUIOption invalidOption = new TestingUIOption.TestingUIOptionBuilder().option(option).optionCollection(testingUIOptionCollection).build();
         underTest.addArg(invalidOption, "foo");
         assertThat(underTest.getArg(invalidOption.keyValue())).isEmpty();
     }
diff --git a/apache-rat-core/src/test/java/org/apache/rat/ui/UIOptionCollectionTest.java b/apache-rat-core/src/test/java/org/apache/rat/ui/UIOptionCollectionTest.java
index b1a4e6b7..5b1c4861 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/ui/UIOptionCollectionTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/ui/UIOptionCollectionTest.java
@@ -21,6 +21,8 @@ package org.apache.rat.ui;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
+import java.util.function.Function;
+
 import org.apache.commons.cli.AlreadySelectedException;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.OptionGroup;
@@ -41,9 +43,10 @@ public class UIOptionCollectionTest {
             super(new Builder());
 
         }
+
         private static class Builder extends UIOptionCollection.Builder<TestingUIOption, Builder> {
             Builder() {
-                super(TestingUIOption::new);
+                super(TestingUIOption.TestingUIOptionBuilder::new);
                         uiOption(UI_OPTION)
                         .uiOption(DEPRECATED_UI_OPTION)
                         .unsupported(Arg.COUNTER_MAX)
@@ -54,10 +57,10 @@ public class UIOptionCollectionTest {
         }
     }
 
-    static class TestingUIOption extends UIOption<TestingUIOption> {
+    public static class TestingUIOption extends UIOption<TestingUIOption> {
 
-        TestingUIOption(final UIOptionCollection<TestingUIOption> collection, final Option option) {
-            super(collection, option, ArgumentTracker.extractName(option).as(CasedString.StringCase.DOT));
+        private TestingUIOption(final TestingUIOptionBuilder builder) {
+            super(builder);
         }
 
         @Override
@@ -74,6 +77,19 @@ public class UIOptionCollectionTest {
         public String getText() {
             return "Short and long options for " + cleanupName(option);
         }
+
+        public static class TestingUIOptionBuilder extends UIOption.Builder<TestingUIOption, TestingUIOptionBuilder> {
+
+            @Override
+            protected Function<Option, CasedString> getNameFactory() {
+                return o -> ArgumentTracker.extractName(option()).as(CasedString.StringCase.DOT);
+            }
+
+            @Override
+            protected TestingUIOption doBuild() {
+                return new TestingUIOption(this);
+            }
+        }
     }
 
     private final TestingUIOptionCollection underTest = new TestingUIOptionCollection();
diff --git a/apache-rat-core/src/test/java/org/apache/rat/ui/UIOptionTest.java b/apache-rat-core/src/test/java/org/apache/rat/ui/UIOptionTest.java
index 2b59fa7a..0c8759ac 100644
--- a/apache-rat-core/src/test/java/org/apache/rat/ui/UIOptionTest.java
+++ b/apache-rat-core/src/test/java/org/apache/rat/ui/UIOptionTest.java
@@ -30,7 +30,8 @@ class UIOptionTest {
     @Test
     void cleanup() {
         optionCollection = new UIOptionCollectionTest.TestingUIOptionCollection();
-        underTest = new UIOptionCollectionTest.TestingUIOption(optionCollection, new Option("a", false, "An option"));
+        underTest = new UIOptionCollectionTest.TestingUIOption.TestingUIOptionBuilder().option(new Option("a", false, "An option"))
+                .optionCollection(optionCollection).build();
         String s = underTest.cleanup("The name is --output-licenses because I said so");
         assertThat(s).isEqualTo("The name is output.licenses because I said so");
 
diff --git a/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/Help.java b/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/Help.java
index 56e08e1b..7979ef2d 100644
--- a/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/Help.java
+++ b/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/Help.java
@@ -179,7 +179,7 @@ public class Help extends BaseAntTask {
             int max = optionTitle.length();
             int maxExample = exampleTitle.length();
             final List<AntOption> optList = new ArrayList<>();
-            AntOptionCollection.INSTANCE.getMappedOptions().forEach(optList::add);
+            new AntOptionCollection().getMappedOptions().forEach(optList::add);
             optList.sort(Comparator.comparing(UIOption::getName));
             List<String> exampleList = new ArrayList<>();
             for (final AntOption option : optList) {
diff --git a/apache-rat-tasks/src/test/java/org/apache/rat/anttasks/GeneratedReportTest.java b/apache-rat-tasks/src/test/java/org/apache/rat/anttasks/GeneratedReportTest.java
index 0f590cba..8e5cf981 100644
--- a/apache-rat-tasks/src/test/java/org/apache/rat/anttasks/GeneratedReportTest.java
+++ b/apache-rat-tasks/src/test/java/org/apache/rat/anttasks/GeneratedReportTest.java
@@ -185,7 +185,7 @@ public class GeneratedReportTest {
      */
     static Stream<Arguments> generatedData() {
 
-        List<AntOption> options = AntOptionCollection.INSTANCE.getMappedOptions().toList();
+        List<AntOption> options = new AntOptionCollection().getMappedOptions().toList();
 
         List<Arguments> lst = new ArrayList<>();
 
@@ -228,11 +228,7 @@ public class GeneratedReportTest {
             if (body == null) {
                 xml.append(format("      <%1$s>%2$s</%1$s>%n", actualOption.getName(), getData(option)));
             } else {
-//                if (actualOption.argCount() == 1) {
-//                    xml.append(format("      <%s %s=\"%s\" />%n", actualOption.getName(), createAttribute(option), getData(option)));
-//                } else {
                 xml.append(format("      <%1$s>%2$s</%1$s>%n", actualOption.getName(), body));
-//                }
             }
         }
 
diff --git a/apache-rat-tasks/src/test/java/org/apache/rat/anttasks/ReportOptionTest.java b/apache-rat-tasks/src/test/java/org/apache/rat/anttasks/ReportOptionTest.java
index ea2172a6..b3cdc4e2 100644
--- a/apache-rat-tasks/src/test/java/org/apache/rat/anttasks/ReportOptionTest.java
+++ b/apache-rat-tasks/src/test/java/org/apache/rat/anttasks/ReportOptionTest.java
@@ -96,6 +96,7 @@ public class ReportOptionTest  {
     }
 
     final static class AntOptionsProvider extends AbstractConfigurationOptionsProvider implements ArgumentsProvider {
+        private AntOptionCollection antOptionCollection = new AntOptionCollection();
 
         public AntOptionsProvider() {
             super(BaseAntTask.unsupportedArgs(), testPath.toFile());
@@ -138,7 +139,7 @@ public class ReportOptionTest  {
             final String name;
 
             BuildTask(Option option) {
-                this(AntOptionCollection.INSTANCE.getMappedOption(option).get().getName());
+                this(antOptionCollection.getMappedOption(option).get().getName());
             }
 
             BuildTask() {
@@ -154,7 +155,7 @@ public class ReportOptionTest  {
                 Map<String, String> attributes = new HashMap<>();
                 if (args.get(0).getKey() != null) {
                     for (Pair<Option, String[]> pair : args) {
-                        AntOption argOption = AntOptionCollection.INSTANCE.getMappedOption(pair.getKey()).get();
+                        AntOption argOption = antOptionCollection.getMappedOption(pair.getKey()).get();
                         if (argOption.isAttribute()) {
                             String value = pair.getValue() == null ? "true" : pair.getValue()[0];
                             attributes.put(argOption.getName(), value);
diff --git a/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/AntOption.java b/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/AntOption.java
index e3cdc580..7d27bbf6 100644
--- a/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/AntOption.java
+++ b/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/AntOption.java
@@ -22,25 +22,27 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.function.Function;
 
 import org.apache.commons.cli.Option;
 import org.apache.rat.ui.UIOption;
 import org.apache.rat.ui.UIOptionCollection;
+import org.apache.rat.utils.CasedString;
 
 import static java.lang.String.format;
 
 /**
  * A class that wraps the CLI option and provides Ant specific values.
  */
-public class AntOption extends UIOption<AntOption> {
+public final class AntOption extends UIOption<AntOption> {
 
     /**
      * Constructor.
      *
-     * @param option the option to wrap.
+     * @param builder the AntOptionBuilder
      */
-    public AntOption(final UIOptionCollection<AntOption> collection, final Option option) {
-        super(collection, option, AntOptionCollection.createName(option));
+    private AntOption(final AntOptionBuilder builder) {
+        super(builder);
     }
 
     /**
@@ -76,7 +78,8 @@ public class AntOption extends UIOption<AntOption> {
     }
 
     /**
-     * Gets the set of options that are mapped to this option.
+     * Gets the set of options that are mapped to this option.  This is used to allow one implementation to answer for
+     * multiple options.
      * @return the set of options that are mapped to this option.
      */
     public Set<AntOption> convertedFrom() {
@@ -104,15 +107,28 @@ public class AntOption extends UIOption<AntOption> {
         return antOption.cleanupName();
     }
 
+    /**
+     * Cleans up the name of this AntOption.  Used in documentation.
+     * Returns either {@code <name>} or {@code name attribute}.
+     * @return the cleaned up name.
+     */
     public String cleanupName() {
         String fmt = isAttribute() ? "%s attribute" : "<%s>";
         return format(fmt, name);
     }
 
+    /**
+     * Gets the AntOptionCollection this AntOption is associated with.
+     * @return the AntOptionCollection this AntOption is associated with.
+     */
     AntOptionCollection getAntCollection() {
         return getOptionCollection();
     }
 
+    /**
+     * Gets the Ant build type for this option.
+     * @return the Ant build type for this option.
+     */
     public AntOptionCollection.BuildType buildType() {
         return getAntCollection().buildType(this.getArgType());
     }
@@ -194,4 +210,54 @@ public class AntOption extends UIOption<AntOption> {
             return result.toString();
         }
     }
+
+    /**
+     * The Builder for AntOptions.
+     */
+    public static class AntOptionBuilder extends UIOption.Builder<AntOption, AntOptionBuilder> {
+
+        public AntOptionBuilder() {
+        }
+
+        /**
+         * returns the function to convert an Option to its Ant based cased name.
+         * @return the function to convert an Option to its Ant based cased name.
+         */
+        protected Function<Option, CasedString> getNameFactory() {
+            return this::createName;
+        }
+
+        @Override
+        public AntOptionBuilder optionCollection(final UIOptionCollection<AntOption> optionCollection) {
+            if (optionCollection instanceof AntOptionCollection) {
+                return super.optionCollection(optionCollection);
+            }
+            throw new IllegalArgumentException("Option collection must be instance of AntOptionCollection");
+        }
+
+        @Override
+        protected AntOption doBuild() {
+            return new AntOption(this);
+        }
+
+        /**
+         * Creates the name for the option based on rules for conversion of Option names.
+         * Ant names are in PASCAL format.
+         * @param option the standard option.
+         * @return a new CasedString comprising the name of the AntOption.
+         * @see CasedString.StringCase#PASCAL
+         */
+        CasedString createName(final Option option) {
+            List<String> pluralEndings = List.of("approved", "denied");
+            String name = optionCollection().rename(option);
+            CasedString casedName = new CasedString(CasedString.StringCase.KEBAB, name);
+            String[] segments = casedName.getSegments();
+            String lastSegment = segments[segments.length - 1];
+            if (option.hasArgs() && !lastSegment.endsWith("s") && !pluralEndings.contains(lastSegment)) {
+                segments[segments.length - 1] += "s";
+                casedName = new CasedString(CasedString.StringCase.KEBAB, segments);
+            }
+            return casedName.as(CasedString.StringCase.PASCAL);
+        }
+    }
 }
diff --git a/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/AntOptionCollection.java b/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/AntOptionCollection.java
index 88f38c0c..b429d615 100644
--- a/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/AntOptionCollection.java
+++ b/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/AntOptionCollection.java
@@ -35,30 +35,24 @@ import org.apache.commons.text.WordUtils;
 import org.apache.rat.OptionCollection;
 import org.apache.rat.commandline.Arg;
 import org.apache.rat.document.DocumentName;
-import org.apache.rat.ui.ArgumentTracker;
 import org.apache.rat.ui.UIOptionCollection;
-import org.apache.rat.utils.CasedString;
 
 import static java.lang.String.format;
 
 /**
- * The collection of AntOptions equivalent to the CLI options
- * with any unsupported options removed.
+ * The collection of AntOptions.
  */
 public final class AntOptionCollection extends UIOptionCollection<AntOption> {
     /** mapping of standard name to non-conflicting name. */
-    private static final Map<String, String> RENAME_MAP;
-
+    static final Map<String, String> RENAME_MAP;
     /**
      * The format for an XML element.
      */
     private static final String DEFAULT_XML = "<%1$s>%%s</%1$s>%n";
-
     /** Attributes that are required for example data. */
     private static final Map<String, Map<String, String>> REQUIRED_ATTRIBUTES = new HashMap<>();
     /** The list of data types that are specified as XML attributes in Ant build.xml documents */
     private static final List<Class<?>> ATTRIBUTE_TYPES = new ArrayList<>();
-
     /** The map of option name conversions. */
     private final Map<Option, Option> conversionMap;
 
@@ -82,21 +76,38 @@ public final class AntOptionCollection extends UIOptionCollection<AntOption> {
         ATTRIBUTE_TYPES.add(DocumentName.class);
     }
 
+    /**
+     * Gets the map of standard Option names that are renamed to fit the Ant naming process.
+     * @return the map of renamed Options.
+     */
     public static Map<String, String> getRenameMap() {
         return new TreeMap<>(RENAME_MAP);
     }
 
-    /** The instance of the AntOptionCollection */
-    public static final AntOptionCollection INSTANCE = new Builder().build();
-
+    /** Creates an instance of the AntOption Collection.
+     */
+    public AntOptionCollection() {
+        this(new Builder());
+    }
     /**
      * Create an instance.
      */
     private AntOptionCollection(final Builder builder) {
-        super(builder);
+        super(new Builder());
         conversionMap = builder.conversions;
     }
 
+    @Override
+    public String rename(final Option option) {
+        String key = super.rename(option);
+        return StringUtils.defaultIfEmpty(RENAME_MAP.get(key), key);
+    }
+
+    /**
+     * Returns the set of Antoptions that the argument was converted From.
+     * @param antOption the AntOption to check.
+     * @return returns a Set of AntOptoins that were converted to the argument. May be an empty set.
+     */
     public Set<AntOption> convertedFrom(final AntOption antOption) {
         return conversionMap.entrySet().stream().filter(e -> e.getValue().equals(antOption.getOption()))
                 .map(e -> getMappedOption(e.getKey()))
@@ -115,16 +126,31 @@ public final class AntOptionCollection extends UIOptionCollection<AntOption> {
         return opt == null ? antOption : getMappedOption(opt).orElse(antOption);
     }
 
+    /**
+     * Returns {@code true} if this argument is an attribute to the ANT RAT call.
+     * @param antOption the AntOption to check.
+     * @return {@code true} if this argument is an attribute to the ANT RAT call.
+     */
     public boolean isAttribute(final AntOption antOption) {
         Option opt = antOption.getOption();
         return (!opt.hasArg() || opt.getArgs() == 1) && convertedFrom(antOption).isEmpty() &&
                 ATTRIBUTE_TYPES.contains(opt.getType());
     }
 
+    /**
+     * Gets a map of attributes that are required for the example data.
+     * @param name the attribute name to lookup.
+     * @return the map of attributes that are required for the example data.
+     */
     public Map<String, String> getRequiredAttributes(final String name) {
         return REQUIRED_ATTRIBUTES.get(name);
     }
 
+    /**
+     * Gets the ANT build type for the ArgumentType
+     * @param type ArgumentType to get the ANT build type for.
+     * @return the ANT build type for the ArgumentType
+     */
     BuildType buildType(final OptionCollection.ArgumentType type) {
         return switch (type) {
             case FILE, DIRORARCHIVE -> new BuildType("filename") {
@@ -147,37 +173,6 @@ public final class AntOptionCollection extends UIOptionCollection<AntOption> {
         };
     }
 
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    /**
-     * Provides a new name for an option if it is renamed in the collection.
-     * @param name the option name.
-     * @return the collection name, may be the same as the option name.
-     */
-    static String rename(final String name) {
-        return StringUtils.defaultIfEmpty(RENAME_MAP.get(name), name);
-    }
-
-    /**
-     * Creates the name for the option based on rules for conversion of CLI option names.
-     * @param option the standard option.
-     * @return the new Option name as a CasedString.
-     */
-    static CasedString createName(final Option option) {
-        List<String> pluralEndings = List.of("approved", "denied");
-        String name = rename(ArgumentTracker.extractKey(option));
-        CasedString casedName = new CasedString(CasedString.StringCase.KEBAB, name);
-        String[] segments = casedName.getSegments();
-        String lastSegment = segments[segments.length - 1];
-        if (option.hasArgs() && !lastSegment.endsWith("s") && !pluralEndings.contains(lastSegment)) {
-            segments[segments.length - 1] += "s";
-            casedName = new CasedString(CasedString.StringCase.KEBAB, segments);
-        }
-        return casedName.as(CasedString.StringCase.PASCAL);
-    }
-
     /**
      * The Builder for the AntOptionCollection.
      */
@@ -186,7 +181,7 @@ public final class AntOptionCollection extends UIOptionCollection<AntOption> {
         private final Map<Option, Option> conversions = new HashMap<>();
 
         private Builder() {
-            super(AntOption::new);
+            super(AntOption.AntOptionBuilder::new);
             Arg.getOptions().getOptions()
                     .stream().filter(o -> Objects.isNull(o.getLongOpt()))
                     .forEach(this::unsupported);
@@ -205,11 +200,12 @@ public final class AntOptionCollection extends UIOptionCollection<AntOption> {
                     .convert(Arg.EXCLUDE_STD, Arg.EXCLUDE);
         }
 
-        @Override
-        public AntOptionCollection build() {
-            return new AntOptionCollection(this);
-        }
-
+        /**
+         * Converts one Arg type to another.
+         * @param from the Arg to convert from
+         * @param to the Arg to convert to.
+         * @return this
+         */
         public Builder convert(final Arg from, final Arg to) {
             Option mapTo = to.option();
             for (Option option : from.group().getOptions()) {
@@ -269,6 +265,12 @@ public final class AntOptionCollection extends UIOptionCollection<AntOption> {
             return format("<%1$s>%2$s</%1$s>%n", delegateOption.getName(), inner);
         }
 
+        /**
+         * Gets a string comprising the Ant XML pattern for the specified AntOption.
+         * @param antOption the ant option to get XML example for
+         * @param data the data for the option.
+         * @return an ANT XML formatted option.
+         */
         public String getXml(final AntOption antOption, final String data) {
             AntOption delegateOption = antOption.getActualAntOption();
             if (delegateOption.isAttribute()) {
@@ -278,6 +280,11 @@ public final class AntOptionCollection extends UIOptionCollection<AntOption> {
             }
         }
 
+        /**
+         * Gets a test name for the AntOption.
+         * @param antOption the Ant Option being tested
+         * @return the test name for this type.
+         */
         public String testName(final AntOption antOption) {
             return addExt ? format("%s_%s", antOption.getName(), antOption.getArgName()) : antOption.getName();
         }
diff --git a/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/MavenOption.java b/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/MavenOption.java
index 6b7397bb..cc4143a7 100644
--- a/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/MavenOption.java
+++ b/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/MavenOption.java
@@ -18,17 +18,19 @@
  */
 package org.apache.rat.documentation.options;
 
+import java.util.function.Function;
+
 import org.apache.commons.cli.Option;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.text.WordUtils;
+import org.apache.rat.ConfigurationException;
 import org.apache.rat.ui.UIOption;
-import org.apache.rat.ui.UIOptionCollection;
 import org.apache.rat.utils.CasedString;
 
 import static java.lang.String.format;
 
 /**
- * A representation of a Maven option based on a CLI option.
+ * A representation of a Maven option based on an Option.
  */
 public final class MavenOption extends UIOption<MavenOption> {
 
@@ -38,10 +40,10 @@ public final class MavenOption extends UIOption<MavenOption> {
     /**
      * Constructor.
      *
-     * @param option The CLI option
+     * @param builder The MavenOption builder.
      */
-    MavenOption(final UIOptionCollection<MavenOption> collection, final Option option) {
-        super(collection, option, MavenOptionCollection.createName(option));
+    private MavenOption(final MavenOptionBuilder builder) {
+        super(builder);
     }
 
     @Override
@@ -60,7 +62,8 @@ public final class MavenOption extends UIOption<MavenOption> {
     @Override
     protected String cleanupName(final Option option) {
         // only parse the option if we need to.
-        return option == this.option ? format(XML_FMT, this.name) : format(XML_FMT, MavenOptionCollection.createName(option));
+        return option == this.option ? format(XML_FMT, this.name) : format(XML_FMT,
+                MavenOptionBuilder.createName(optionCollection.rename(option)));
     }
 
     @Override
@@ -102,29 +105,43 @@ public final class MavenOption extends UIOption<MavenOption> {
         return sb.toString();
     }
 
+    /**
+     * Gets the Maven Mojo method signature for this Option.
+     * @param indent the number of spaces to indent the text.
+     * @param multiple {@code true} if this option takes multiple arguments.
+     * @return the method signature for this Option.
+     */
     public String getMethodSignature(final String indent, final boolean multiple) {
         StringBuilder sb = new StringBuilder();
         if (isDeprecated()) {
             sb.append(format("%s@Deprecated%n", indent));
         }
-        String fname = name.toCase(CasedString.StringCase.CAMEL);
+        // the camel case name for this option.
+        String camelName = name.toCase(CasedString.StringCase.CAMEL);
+        if (camelName == null) {
+            throw new ConfigurationException("Name can not be null");
+        }
         String args = option.hasArg() ? "String" : "boolean";
         if (multiple) {
-            if (!(fname.endsWith("s") || fname.endsWith("Approved") || fname.endsWith("Denied"))) {
-                fname = fname + "s";
+            if (!(camelName.endsWith("s") || camelName.endsWith("Approved") || camelName.endsWith("Denied"))) {
+                camelName = camelName + "s";
             }
             args = args + "[]";
         }
 
         return sb.append(format("%1$s%5$s%n%1$spublic void set%3$s(%4$s %2$s)",
-                        indent, name, fname, args, getPropertyAnnotation(fname)))
+                        indent, name, camelName, args, getParameterAnnotation(camelName)))
                 .toString();
     }
 
-
-    public String getPropertyAnnotation(final String fname) {
+    /**
+     * Creates the {@code @Parameter} annotation for this option.
+     * @param camelName The camel cased name for this option.
+     * @return the string that is the parameter annotation.
+     */
+    public String getParameterAnnotation(final String camelName) {
         StringBuilder sb = new StringBuilder("@Parameter");
-        String property = option.hasArgs() ? null : format("property = \"rat.%s\"", fname);
+        String property = option.hasArgs() ? null : format("property = \"rat.%s\"", camelName);
         String defaultValue = option.isDeprecated() ? null : getDefaultValue();
         if (property != null || defaultValue != null) {
             sb.append("(");
@@ -138,4 +155,30 @@ public final class MavenOption extends UIOption<MavenOption> {
         }
         return sb.toString();
     }
+
+    /**
+     * The builder for the MavenOptions.
+     */
+    public static class MavenOptionBuilder extends UIOption.Builder<MavenOption, MavenOptionBuilder> {
+
+        @Override
+        protected Function<Option, CasedString> getNameFactory() {
+            return o -> createName(optionCollection().rename(o));
+        }
+
+        @Override
+        protected MavenOption doBuild() {
+            return new MavenOption(this);
+        }
+
+        /**
+         * Create the cased name
+         * @param key the renamed key from the collection.
+         * @return the name for the key.
+         * @see MavenOptionCollection#rename(Option)
+         */
+        static CasedString createName(final String key) {
+            return new CasedString(CasedString.StringCase.KEBAB, key).as(CasedString.StringCase.PASCAL);
+        }
+    }
 }
diff --git a/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/MavenOptionCollection.java b/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/MavenOptionCollection.java
index 4b856ca9..8f3b5fda 100644
--- a/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/MavenOptionCollection.java
+++ b/apache-rat-tools/src/main/java/org/apache/rat/documentation/options/MavenOptionCollection.java
@@ -26,9 +26,7 @@ import java.util.TreeMap;
 import org.apache.commons.cli.Option;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.rat.commandline.Arg;
-import org.apache.rat.ui.ArgumentTracker;
 import org.apache.rat.ui.UIOptionCollection;
-import org.apache.rat.utils.CasedString;
 
 /**
  * The collection of MavenOptions equivalent to the CLI options
@@ -47,10 +45,9 @@ public final class MavenOptionCollection extends UIOptionCollection<MavenOption>
     }
 
     /**
-     * The instance of the MavenOptionCollection.
+     * Gets the Map of renamed Options indexed by original option name.
+     * @return the map of renamed Options indexed by original option name.
      */
-    public static final MavenOptionCollection INSTANCE = new Builder().build();
-
     public static Map<String, String> getRenameMap() {
         return new TreeMap<>(RENAME_MAP);
     }
@@ -58,33 +55,14 @@ public final class MavenOptionCollection extends UIOptionCollection<MavenOption>
     /**
      * Create an instance.
      */
-    private MavenOptionCollection(final Builder builder) {
-        super(builder);
-    }
-
-    public static Builder builder() {
-        return new Builder();
+    public MavenOptionCollection() {
+        super(new Builder());
     }
 
-    /**
-     * Provides a new name for an option if it is renamed in the collection.
-     *
-     * @param name the option name.
-     * @return the collection name, may be the same as the option name.
-     */
-    static String rename(final String name) {
-        return StringUtils.defaultIfEmpty(RENAME_MAP.get(name), name);
-    }
-
-    /**
-     * Creates the name for the option based on rules for conversion of CLI option names.
-     *
-     * @param option the standard option.
-     * @return the new Option name as a CasedString.
-     */
-    static CasedString createName(final Option option) {
-        String name = rename(ArgumentTracker.extractKey(option));
-        return new CasedString(CasedString.StringCase.KEBAB, name).as(CasedString.StringCase.PASCAL);
+    @Override
+    public String rename(final Option option) {
+        String key = super.rename(option);
+        return StringUtils.defaultIfEmpty(RENAME_MAP.get(key), key);
     }
 
     /**
@@ -92,16 +70,11 @@ public final class MavenOptionCollection extends UIOptionCollection<MavenOption>
      */
     public static final class Builder extends UIOptionCollection.Builder<MavenOption, Builder> {
         private Builder() {
-            super(MavenOption::new);
+            super(MavenOption.MavenOptionBuilder::new);
             Arg.getOptions().getOptions()
                     .stream().filter(o -> Objects.isNull(o.getLongOpt()))
                     .forEach(this::unsupported);
             unsupported(Arg.DIR).unsupported(Arg.LOG_LEVEL);
         }
-
-        @Override
-        public MavenOptionCollection build() {
-            return new MavenOptionCollection(this);
-        }
     }
 }
diff --git a/apache-rat-tools/src/main/java/org/apache/rat/documentation/velocity/RatTool.java b/apache-rat-tools/src/main/java/org/apache/rat/documentation/velocity/RatTool.java
index ca1adadf..7faf6ffb 100644
--- a/apache-rat-tools/src/main/java/org/apache/rat/documentation/velocity/RatTool.java
+++ b/apache-rat-tools/src/main/java/org/apache/rat/documentation/velocity/RatTool.java
@@ -48,6 +48,7 @@ import org.apache.rat.documentation.options.MavenOptionCollection;
 import org.apache.rat.help.AbstractHelp;
 import org.apache.rat.license.ILicense;
 import org.apache.rat.license.LicenseSetFactory;
+import org.apache.rat.ui.UIOption;
 import org.apache.velocity.tools.config.DefaultKey;
 import org.apache.velocity.tools.config.ValidScope;
 
@@ -86,6 +87,22 @@ public class RatTool {
     /** The license factory this tool uses. */
     private final LicenseSetFactory licenseSetFactory;
 
+    /**
+     * The Client option instance
+     */
+    // visible for testing
+    private final CLIOptionCollection cliOptions = new CLIOptionCollection();
+
+    /**
+     * The ANT option instance.
+     */
+    private final AntOptionCollection antOptions = new AntOptionCollection();
+
+    /**
+     * The Maven option instance
+     */
+    private final MavenOptionCollection mavenOptions = new MavenOptionCollection();
+
     /**
      * Constructor.
      */
@@ -99,8 +116,8 @@ public class RatTool {
      * @return the list of command line options.
      */
     public List<Option> options() {
-        return CLIOptionCollection.INSTANCE.getMappedOptions()
-        .map(CLIOption::getOption).toList();
+        return cliOptions.getMappedOptions()
+        .map(UIOption::getOption).toList();
     }
 
     /**
@@ -108,7 +125,7 @@ public class RatTool {
      * @return a map client option name to Ant Option.
      */
     public Map<String, AntOption> antOptions() {
-        return AntOptionCollection.INSTANCE.getOptionMap();
+        return antOptions.getOptionMap();
     }
 
     /**
@@ -116,7 +133,7 @@ public class RatTool {
      * @return a map client option name to CLI Option.
      */
     public Map<String, CLIOption> cliOptions() {
-        return CLIOptionCollection.INSTANCE.getOptionMap();
+        return cliOptions.getOptionMap();
     }
 
     /**
@@ -124,7 +141,7 @@ public class RatTool {
      * @return a map client option name to Maven Option.
      */
     public Map<String, MavenOption> mvnOptions() {
-        return MavenOptionCollection.INSTANCE.getOptionMap();
+        return mavenOptions.getOptionMap();
     }
 
     /**
diff --git a/apache-rat-tools/src/main/java/org/apache/rat/tools/AntDocumentation.java b/apache-rat-tools/src/main/java/org/apache/rat/tools/AntDocumentation.java
index 5511ab0e..eb59ae62 100644
--- a/apache-rat-tools/src/main/java/org/apache/rat/tools/AntDocumentation.java
+++ b/apache-rat-tools/src/main/java/org/apache/rat/tools/AntDocumentation.java
@@ -48,6 +48,11 @@ public final class AntDocumentation {
     /** The directory to write to. */
     private final File outputDir;
 
+    /**
+     * THe Ant Option collection instance.
+     */
+    private final AntOptionCollection antOptions = new AntOptionCollection();
+
     /**
      * Creates APT documentation files for Ant.
      * Requires 1 argument:
@@ -83,7 +88,7 @@ public final class AntDocumentation {
     }
 
     public void execute() {
-        List<AntOption> options = AntOptionCollection.INSTANCE.getMappedOptions().toList();
+        List<AntOption> options = antOptions.getMappedOptions().toList();
         writeAttributes(options);
         writeElements(options);
         printValueTypes();
@@ -151,7 +156,7 @@ public final class AntDocumentation {
         List<List<String>> table = new ArrayList<>();
         table.add(Arrays.asList("Value Type", "Description"));
 
-        AntOptionCollection.INSTANCE.getMappedOptions()
+        antOptions.getMappedOptions()
                 .map(antOption -> Arrays.asList(antOption.getName(), antOption.getDescription()))
                 .forEach(table::add);
 
diff --git a/apache-rat-tools/src/main/java/org/apache/rat/tools/AntGenerator.java b/apache-rat-tools/src/main/java/org/apache/rat/tools/AntGenerator.java
index d748dbe1..725cd876 100644
--- a/apache-rat-tools/src/main/java/org/apache/rat/tools/AntGenerator.java
+++ b/apache-rat-tools/src/main/java/org/apache/rat/tools/AntGenerator.java
@@ -124,12 +124,13 @@ public final class AntGenerator {
             System.err.println("At least three arguments are required: package, simple class name, target directory.");
             return;
         }
+        final AntOptionCollection antOptions = new AntOptionCollection();
 
         String packageName = args[0];
         String className = args[1];
         String destDir = args[2];
 
-        List<AntOption> options = AntOptionCollection.INSTANCE.getMappedOptions().toList();
+        List<AntOption> options = antOptions.getMappedOptions().toList();
 
         String pkgName = String.join(File.separator, new CasedString(StringCase.DOT, packageName).getSegments());
         File file = new File(new File(new File(destDir), pkgName), className + ".java");
@@ -150,12 +151,12 @@ public final class AntGenerator {
                             writer.append(format("        xlateName.put(\"%s\", \"%s\");%n", entry.getKey(), entry.getValue()));
                         }
 
-                        for (Option option : AntOptionCollection.INSTANCE.getUnsupportedOptions()
+                        for (Option option : antOptions.getUnsupportedOptions()
                                 .getOptions()) {
                             writer.append(format("        unsupportedArgs.add(\"%s\");%n", argsKey(option)));
                         }
 
-                        for (AntOption option : AntOptionCollection.INSTANCE.getMappedOptions().filter(AntOption::isDeprecated).toList()) {
+                        for (AntOption option : antOptions.getMappedOptions().filter(AntOption::isDeprecated).toList()) {
                             writer.append(format("        deprecatedArgs.put(\"%s\", \"%s\");%n", argsKey(option.getOption()),
                                     format("Use of deprecated option '%s'. %s", option.getName(), option.getDeprecated())));
                         }
diff --git a/apache-rat-tools/src/main/java/org/apache/rat/tools/MavenGenerator.java b/apache-rat-tools/src/main/java/org/apache/rat/tools/MavenGenerator.java
index 6d12c7bf..e05cbead 100644
--- a/apache-rat-tools/src/main/java/org/apache/rat/tools/MavenGenerator.java
+++ b/apache-rat-tools/src/main/java/org/apache/rat/tools/MavenGenerator.java
@@ -74,6 +74,9 @@ public final class MavenGenerator {
         String packageName = args[0];
         String className = args[1];
         String destDir = args[2];
+
+        final MavenOptionCollection mavenOptions = new MavenOptionCollection();
+
         String pkgName = String.join(File.separator, new CasedString(StringCase.DOT, packageName).getSegments());
         File file = new File(new File(new File(destDir), pkgName), className + ".java");
         System.out.println("Creating " + file);
@@ -91,16 +94,16 @@ public final class MavenGenerator {
                         for (Map.Entry<?, ?> entry : MavenOptionCollection.getRenameMap().entrySet()) {
                             writer.append(format("        xlateName.put(\"%s\", \"%s\");%n", entry.getKey(), entry.getValue()));
                         }
-                        for (Option option : MavenOptionCollection.INSTANCE.getUnsupportedOptions().getOptions()) {
+                        for (Option option : mavenOptions.getUnsupportedOptions().getOptions()) {
                             writer.append(format("        unsupportedArgs.add(\"%s\");%n", argsKey(option)));
                         }
-                        for (MavenOption option : MavenOptionCollection.INSTANCE.getMappedOptions().filter(MavenOption::isDeprecated).toList()) {
+                        for (MavenOption option : mavenOptions.getMappedOptions().filter(MavenOption::isDeprecated).toList()) {
                             writer.append(format("        deprecatedArgs.put(\"%s\", \"%s\");%n", argsKey(option.getOption()),
                                     format("Use of deprecated option '%s'. %s", option.getName(), option.getDeprecated())));
                         }
                         break;
                     case "${methods}":
-                        writeMethods(writer);
+                        writeMethods(mavenOptions, writer);
                         break;
                     case "${package}":
                         writer.append(format("package %s;%n", packageName));
@@ -162,8 +165,8 @@ public final class MavenGenerator {
         return sb.append(format("     */%n")).toString();
     }
 
-    private static void writeMethods(final FileWriter writer) throws IOException {
-        for (MavenOption option : MavenOptionCollection.INSTANCE.getMappedOptions().toList()) {
+    private static void writeMethods(final MavenOptionCollection mavenOptions, final FileWriter writer) throws IOException {
+        for (MavenOption option : mavenOptions.getMappedOptions().toList()) {
             writer.append(getComment(option))
                     .append(option.getMethodSignature("    ", option.hasArgs())).append(" {").append(System.lineSeparator())
                     .append(getBody(option))
diff --git a/apache-rat-tools/src/main/java/org/apache/rat/tools/Naming.java b/apache-rat-tools/src/main/java/org/apache/rat/tools/Naming.java
index acd15383..ea8b1b2a 100644
--- a/apache-rat-tools/src/main/java/org/apache/rat/tools/Naming.java
+++ b/apache-rat-tools/src/main/java/org/apache/rat/tools/Naming.java
@@ -81,10 +81,28 @@ public final class Naming {
             .addOption(INCLUDE_DEPRECATED)
             .addOption(WIDTH);
 
-    /**
-     * No instantiation of utility class.
-     */
-    private Naming() { }
+    /** The porsed command line */
+    private final CommandLine cl;
+    /** The width of the output */
+    private final int width;
+    /** The Maven options */
+    private final MavenOptionCollection mavenCollection = new MavenOptionCollection();
+    /** The Ant options */
+    private final AntOptionCollection antCollection = new AntOptionCollection();
+    /** The filter to include only non-deprecated long options */
+    private final Predicate<Option> filter;
+    /** The columns to display */
+    private final List<String> columns;
+    /** if {@code true} then show Maven options */
+    private final boolean showMaven;
+    /** if {@code true} then show Ant options */
+    private final boolean showAnt;
+    /** if {@code true} then show CLI options */
+    private final boolean addCLI;
+    /** if {@code true} then include deprecated options in output. */
+    private final boolean includeDeprecated;
+    /** The function to display the description of the option */
+    private final Function<Option, String> descriptionFunction;
 
     /**
      * Creates the CSV file.
@@ -101,43 +119,43 @@ public final class Naming {
             System.err.println("At least one argument is required: path to file is missing.");
             return;
         }
-        CommandLine cl = DefaultParser.builder().build().parse(OPTIONS, args);
-        int width = Math.max(cl.getParsedOptionValue(WIDTH, AbstractHelp.HELP_WIDTH), AbstractHelp.HELP_WIDTH);
-
-        boolean showMaven = cl.hasOption(MAVEN);
-        MavenOptionCollection mavenCollection = MavenOptionCollection.INSTANCE;
-
-        boolean showAnt = cl.hasOption(ANT);
-        AntOptionCollection antCollection = AntOptionCollection.INSTANCE;
-
-        boolean includeDeprecated = cl.hasOption(INCLUDE_DEPRECATED);
-        Predicate<Option> filter = o -> o.hasLongOpt() && (!o.isDeprecated() || includeDeprecated);
+        Naming naming = new Naming(args);
+        naming.write();
+    }
+    /**
+     * No instantiation of utility class.
+     */
+    private Naming(final String[] args) throws ParseException {
+        cl = DefaultParser.builder().build().parse(OPTIONS, args);
 
-        List<String> columns = new ArrayList<>();
+        width = Math.max(cl.getParsedOptionValue(WIDTH, AbstractHelp.HELP_WIDTH), AbstractHelp.HELP_WIDTH);
+        showMaven = cl.hasOption(MAVEN);
+        showAnt = cl.hasOption(ANT);
+        addCLI = cl.hasOption(CLI);
+        includeDeprecated = cl.hasOption(INCLUDE_DEPRECATED);
+        filter = o -> o.hasLongOpt() && (!o.isDeprecated() || includeDeprecated);
 
-        if (cl.hasOption(CLI)) {
-            columns.add("CLI");
+        List<String> columnsBuilder = new ArrayList<>();
+        if (addCLI) {
+            columnsBuilder.add("CLI");
         }
-
         if (showAnt) {
-            columns.add("Ant");
+            columnsBuilder.add("Ant");
         }
-
         if (showMaven) {
-            columns.add("Maven");
+            columnsBuilder.add("Maven");
         }
-        columns.add("Description");
-        columns.add("Argument Type");
+        columnsBuilder.add("Description");
+        columnsBuilder.add("Argument Type");
+        columns = columnsBuilder;
 
-        Function<Option, String> descriptionFunction;
-
-        if (cl.hasOption(CLI) || !showAnt && !showMaven) {
+        if (addCLI || !showAnt && !showMaven) {
             descriptionFunction = o -> {
                 StringBuilder desc = new StringBuilder();
-            if (o.isDeprecated()) {
-                desc.append("[").append(o.getDeprecated().toString()).append("] ");
-            }
-            return desc.append(StringUtils.defaultIfEmpty(o.getDescription(), "")).toString();
+                if (o.isDeprecated()) {
+                    desc.append("[").append(o.getDeprecated().toString()).append("] ");
+                }
+                return desc.append(StringUtils.defaultIfEmpty(o.getDescription(), "")).toString();
             };
         } else if (showAnt) {
             descriptionFunction = o -> {
@@ -163,61 +181,56 @@ public final class Naming {
                 return desc.toString();
             };
         }
+    }
 
+    private void write() throws IOException {
         try (Writer underWriter = cl.getArgs().length != 0 ?
                 new FileWriter(cl.getArgs()[0], StandardCharsets.UTF_8) : new OutputStreamWriter(System.out, StandardCharsets.UTF_8)) {
             if (cl.hasOption(CSV)) {
-                printCSV(columns, filter, cl.hasOption(CLI), showMaven, showAnt, descriptionFunction, underWriter);
-            }
-            else {
-                printText(columns, filter, cl.hasOption(CLI), showMaven, showAnt, descriptionFunction, underWriter, width);
+                printCSV(underWriter);
+            } else {
+                printText(underWriter);
             }
         }
     }
 
-    private static List<String> fillColumns(final List<String> columns, final Option option, final boolean addCLI, final boolean showMaven,
-                                            final boolean showAnt, final Function<Option, String> descriptionFunction) {
-        AntOptionCollection antCollection = AntOptionCollection.INSTANCE;
-        MavenOptionCollection mavenCollection = MavenOptionCollection.INSTANCE;
 
+    private List<String> fillColumns(final Option option) {
+        List<String> columnsBuilder = new ArrayList<>();
         if (addCLI) {
             if (option.hasLongOpt()) {
-                columns.add("--" + option.getLongOpt());
+                columnsBuilder.add("--" + option.getLongOpt());
             } else {
-                columns.add("-" + option.getOpt());
+                columnsBuilder.add("-" + option.getOpt());
             }
         }
         if (showAnt) {
-            antCollection.getMappedOption(option).ifPresentOrElse(antOption -> columns.add(antOption.getExample()),
-                    () -> columns.add(" "));
+            antCollection.getMappedOption(option).ifPresentOrElse(antOption -> columnsBuilder.add(antOption.getExample()),
+                    () -> columnsBuilder.add(" "));
         }
         if (showMaven) {
-            mavenCollection.getMappedOption(option).ifPresentOrElse(mavenOption -> columns.add(mavenOption.getExample()),
-                    () -> columns.add(" "));
+            mavenCollection.getMappedOption(option).ifPresentOrElse(mavenOption -> columnsBuilder.add(mavenOption.getExample()),
+                    () -> columnsBuilder.add(" "));
         }
 
-        columns.add(descriptionFunction.apply(option));
-        columns.add(option.hasArgName() ? option.getArgName() : option.hasArgs() ? "Strings" : option.hasArg() ? "String" : "-- none --");
-        columns.add(option.hasArgName() ? option.getArgName() : option.hasArgs() ? "Strings" : option.hasArg() ? "String" : "-- none --");
-        columns.add(option.hasArgName() ? option.getArgName() : option.hasArgs() ? "Strings" : option.hasArg() ? "String" : "-- none --");
-        return columns;
+        columnsBuilder.add(descriptionFunction.apply(option));
+        columnsBuilder.add(option.hasArgName() ? option.getArgName() : option.hasArgs() ? "Strings" : option.hasArg() ? "String" : "-- none --");
+        return columnsBuilder;
     }
 
-    private static void printCSV(final List<String> columns, final Predicate<Option> filter, final boolean addCLI, final boolean showMaven,
-                                 final boolean showAnt, final Function<Option, String> descriptionFunction,
-                                 final Writer underWriter) throws IOException {
+    private void printCSV(final Appendable underWriter) throws IOException {
         try (CSVPrinter printer = new CSVPrinter(underWriter, CSVFormat.DEFAULT.builder().setQuoteMode(QuoteMode.ALL).get())) {
             printer.printRecord(columns);
             for (Option option : OptionCollection.buildOptions().getOptions()) {
                 if (filter.test(option)) {
                     columns.clear();
-                    printer.printRecord(fillColumns(columns, option, addCLI, showMaven, showAnt, descriptionFunction));
+                    printer.printRecord(fillColumns(option));
                 }
             }
         }
     }
 
-    private static int[] calculateColumnWidth(final int width, final int columnCount, final List<List<String>> page) {
+    private int[] calculateColumnWidth(final int width, final int columnCount, final List<List<String>> page) {
         int[] columnWidth = new int[columnCount];
         for (List<String> row : page) {
             for (int i = 0; i < columnCount; i++) {
@@ -246,9 +259,7 @@ public final class Naming {
         return columnWidth;
     }
 
-    private static void printText(final List<String> columns, final Predicate<Option> filter, final boolean addCLI,
-                                  final boolean showMaven, final boolean showAnt,
-                                  final Function<Option, String> descriptionFunction, final Writer underWriter, final int width) throws IOException {
+    private void printText(final Appendable underWriter) throws IOException {
         List<List<String>> page = new ArrayList<>();
 
         int columnCount = columns.size();
@@ -256,7 +267,7 @@ public final class Naming {
 
         for (Option option : OptionCollection.buildOptions().getOptions()) {
             if (filter.test(option)) {
-                page.add(fillColumns(new ArrayList<>(), option, addCLI, showMaven, showAnt, descriptionFunction));
+                page.add(fillColumns(option));
             }
         }
         int[] columnWidth = calculateColumnWidth(width, columnCount, page);
diff --git a/apache-rat-tools/src/test/java/org/apache/rat/documentation/options/MavenOptionTest.java b/apache-rat-tools/src/test/java/org/apache/rat/documentation/options/MavenOptionTest.java
index 38c65802..736e4360 100644
--- a/apache-rat-tools/src/test/java/org/apache/rat/documentation/options/MavenOptionTest.java
+++ b/apache-rat-tools/src/test/java/org/apache/rat/documentation/options/MavenOptionTest.java
@@ -27,9 +27,10 @@ import org.junit.jupiter.api.Test;
 public class MavenOptionTest {
     @Test
     void getDeprecatedTest() {
+        MavenOptionCollection mavenOptionCollection = new MavenOptionCollection();
         for (Option option : Arg.getOptions().getOptions()) {
             if (option.isDeprecated()) {
-                MavenOptionCollection.INSTANCE.getMappedOption(option).ifPresent( mavenOption -> //
+                mavenOptionCollection.getMappedOption(option).ifPresent( mavenOption -> //
                         TextUtils.assertPatternNotInTarget("\\-\\- ", mavenOption.getDeprecated()));
             }
         }
diff --git a/apache-rat-tools/src/test/java/org/apache/rat/documentation/velocity/RatToolTest.java b/apache-rat-tools/src/test/java/org/apache/rat/documentation/velocity/RatToolTest.java
index 52d5d571..7e2a6a6a 100644
--- a/apache-rat-tools/src/test/java/org/apache/rat/documentation/velocity/RatToolTest.java
+++ b/apache-rat-tools/src/test/java/org/apache/rat/documentation/velocity/RatToolTest.java
@@ -42,6 +42,9 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 public class RatToolTest {
     final RatTool underTest = new RatTool();
+    final CLIOptionCollection cliOptionCollection = new CLIOptionCollection();
+    final AntOptionCollection antOptionCollection = new AntOptionCollection();
+    final MavenOptionCollection mavenOptionCollection = new MavenOptionCollection();
 
     @Test
     void environmentVariables() {
@@ -57,13 +60,13 @@ public class RatToolTest {
     void antOptions() {
         Map<String, AntOption> options = underTest.antOptions();
         assertThat(options).isNotEmpty();
-        assertThat(options.values()).containsExactlyInAnyOrderElementsOf(AntOptionCollection.INSTANCE.getMappedOptions().toList());
+        assertThat(options.values()).containsExactlyInAnyOrderElementsOf(antOptionCollection.getMappedOptions().toList());
     }
 
     @Test
     void options() {
         List<Option> options = underTest.options();
-        assertThat(options).isNotEmpty().containsExactlyInAnyOrderElementsOf(CLIOptionCollection.INSTANCE.getMappedOptions()
+        assertThat(options).isNotEmpty().containsExactlyInAnyOrderElementsOf(cliOptionCollection.getMappedOptions()
                     .map(CLIOption::getOption).toList());
     }
 
@@ -71,14 +74,14 @@ public class RatToolTest {
     void cliOptions() {
         Map<String, CLIOption> options = underTest.cliOptions();
         assertThat(options).isNotEmpty();
-        assertThat(options.values()).containsExactlyInAnyOrderElementsOf(CLIOptionCollection.INSTANCE.getMappedOptions().toList());
+        assertThat(options.values()).containsExactlyInAnyOrderElementsOf(cliOptionCollection.getMappedOptions().toList());
     }
 
     @Test
     void mvnOptions() {
         Map<String, MavenOption> options = underTest.mvnOptions();
         assertThat(options).isNotEmpty();
-        assertThat(options.values()).containsExactlyInAnyOrderElementsOf(MavenOptionCollection.INSTANCE.getMappedOptions().toList());
+        assertThat(options.values()).containsExactlyInAnyOrderElementsOf(mavenOptionCollection.getMappedOptions().toList());
     }
 
     @Test
diff --git a/apache-rat-tools/src/test/java/org/apache/rat/tools/AntDocumentationTest.java b/apache-rat-tools/src/test/java/org/apache/rat/tools/AntDocumentationTest.java
index 07d1e12c..2200d200 100644
--- a/apache-rat-tools/src/test/java/org/apache/rat/tools/AntDocumentationTest.java
+++ b/apache-rat-tools/src/test/java/org/apache/rat/tools/AntDocumentationTest.java
@@ -45,14 +45,15 @@ class AntDocumentationTest {
 
     @BeforeEach
     void setup(@TempDir(cleanup = CleanupMode.NEVER) Path path) {
-        optionCollection = AntOptionCollection.INSTANCE;
+        optionCollection = new AntOptionCollection();
         testPath = path;
         underTest = new AntDocumentation(path.toFile());
     }
 
     @Test
     void writeAttributes() throws IOException {
-        List<AntOption> antOptions = options.getOptions().stream().map(opt -> new AntOption(optionCollection, opt)).toList();
+        AntOption.AntOptionBuilder builder = new AntOption.AntOptionBuilder().optionCollection(optionCollection);
+        List<AntOption> antOptions = options.getOptions().stream().map(opt -> builder.option(opt).build()).toList();
         underTest.writeAttributes(antOptions);
         File result = new File(testPath.toFile(), "report_attributes.txt");
         assertThat(result).exists();
@@ -63,7 +64,8 @@ class AntDocumentationTest {
 
     @Test
     void writeElements() throws IOException {
-        List<AntOption> antOptions = options.getOptions().stream().map(opt -> new AntOption(optionCollection, opt)).toList();
+        AntOption.AntOptionBuilder builder = new AntOption.AntOptionBuilder().optionCollection(optionCollection);
+        List<AntOption> antOptions = options.getOptions().stream().map(opt -> builder.option(opt).build()).toList();
         underTest.writeElements(antOptions);
         File result = new File(testPath.toFile(), "report_elements.txt");
         assertThat(result).exists();