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

claude pushed a commit to branch add-ui-package
in repository https://gitbox.apache.org/repos/asf/creadur-rat.git

commit d1ac2509d118eba44c354cd2ab9cc2b4337dc212
Author: Claude Warren <[email protected]>
AuthorDate: Tue Mar 10 17:07:08 2026 +0000

    initial code
---
 apache-rat-core/pom.xml                            |   4 +
 .../org/apache/rat/OptionCollectionParser.java     | 327 +++++++++++++++++++++
 .../rat/commandline/UpdatableOptionGroup.java      |  60 ++++
 .../org/apache/rat/ui/AbstractCodeGenerator.java   | 143 +++++++++
 .../java/org/apache/rat/ui/AbstractOption.java     | 232 +++++++++++++++
 .../apache/rat/ui/AbstractOptionCollection.java    | 162 ++++++++++
 .../java/org/apache/rat/ui/ArgumentTracker.java    | 211 +++++++++++++
 .../src/main/java/org/apache/rat/ui/UI.java        |  36 +++
 .../main/java/org/apache/rat/ui/package-info.java  |  22 ++
 .../java/org/apache/rat/ui/spi/UIProvider.java     |  25 ++
 .../java/org/apache/rat/ui/spi/package-info.java   |  22 ++
 pom.xml                                            |   5 +
 12 files changed, 1249 insertions(+)

diff --git a/apache-rat-core/pom.xml b/apache-rat-core/pom.xml
index 4819f23b..d4bf3b5d 100644
--- a/apache-rat-core/pom.xml
+++ b/apache-rat-core/pom.xml
@@ -176,6 +176,10 @@
     </plugins>
   </build>
   <dependencies>
+    <dependency>
+      <groupId>com.github.spotbugs</groupId>
+      <artifactId>spotbugs-annotations</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.rat</groupId>
       <artifactId>apache-rat-testdata</artifactId>
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
new file mode 100644
index 00000000..21c34367
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/OptionCollectionParser.java
@@ -0,0 +1,327 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Serial;
+import java.io.Serializable;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Comparator;
+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;
+import org.apache.rat.api.Document;
+import org.apache.rat.commandline.Arg;
+import org.apache.rat.commandline.ArgumentContext;
+import org.apache.rat.commandline.StyleSheets;
+import org.apache.rat.config.exclusion.StandardCollection;
+import org.apache.rat.document.DocumentName;
+import org.apache.rat.document.DocumentNameMatcher;
+import org.apache.rat.document.FileDocument;
+import org.apache.rat.help.Licenses;
+import org.apache.rat.license.LicenseSetFactory;
+import org.apache.rat.report.IReportable;
+import org.apache.rat.report.claim.ClaimStatistic;
+import org.apache.rat.ui.AbstractOptionCollection;
+import org.apache.rat.utils.DefaultLog;
+import org.apache.rat.utils.Log.Level;
+import org.apache.rat.walker.ArchiveWalker;
+import org.apache.rat.walker.DirectoryWalker;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import static java.lang.String.format;
+
+/**
+ * Uses the AbstractOptionCollection to parse the command line options.
+ * contains utility methods to ReportConfiguration from the options and an 
array of arguments.
+ */
+@SuppressFBWarnings("EI_EXPOSE_REP2")
+public final class OptionCollectionParser {
+    /** The OptionCollection that we are working with */
+    private final AbstractOptionCollection<?> optionCollection;
+
+    public OptionCollectionParser(final AbstractOptionCollection<?> 
optionCollection) {
+        this.optionCollection = optionCollection;
+    }
+
+    /** The Option comparator to sort the help */
+    public static final Comparator<Option> OPTION_COMPARATOR = new 
OptionComparator();
+
+    /**
+     * Join a collection of objects together as a comma separated list of 
their string values.
+     * @param args the objects to join together.
+     * @return the comma separated string.
+     */
+    private static String asString(final Object[] args) {
+        return 
Arrays.stream(args).map(Object::toString).collect(Collectors.joining(", "));
+    }
+
+    /**
+     * Parses the standard options to create a ReportConfiguration.
+     *
+     * @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.
+     */
+    public ArgumentContext parseCommands(final File workingDirectory, final 
String[] args)
+            throws IOException, ParseException {
+        return parseCommands(workingDirectory, args, 
optionCollection.getOptions());
+    }
+
+    /**
+     * Parse the options into the command line.
+     * @param opts the option definitions.
+     * @param args the argument to apply the definitions to.
+     * @return the CommandLine
+     * @throws ParseException on option parsing error.
+     */
+    //@VisibleForTesting
+    CommandLine parseCommandLine(final Options opts, final String[] args) 
throws ParseException {
+        try {
+            return 
DefaultParser.builder().setDeprecatedHandler(DeprecationReporter.getLogReporter())
+                    .setAllowPartialMatching(true).build().parse(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);
+            throw e;
+        }
+    }
+
+    /**
+     * Parses the standard options to create a ReportConfiguration.
+     *
+     * @param workingDirectory The directory to resolve relative file names 
against.
+     * @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.
+     */
+    private ArgumentContext parseCommands(final File workingDirectory, final 
String[] args,
+                                                                       final 
Options options) throws IOException, ParseException {
+
+        CommandLine commandLine = parseCommandLine(options, args);
+
+        Arg.processLogLevel(commandLine);
+
+        ArgumentContext argumentContext = new 
ArgumentContext(workingDirectory, commandLine);
+        populateConfiguration(argumentContext);
+        if (commandLine.hasOption(Arg.HELP_LICENSES.option())) {
+            new Licenses(argumentContext.getConfiguration(),
+                    new 
PrintWriter(argumentContext.getConfiguration().getOutput().get(),
+                            false, StandardCharsets.UTF_8)).printHelp();
+        }
+
+        return argumentContext;
+    }
+
+    /**
+     * Create the report configuration.
+     * Note: this method is package private for testing.
+     * You probably want one of the {@code ParseCommands} methods.
+     * @param argumentContext The context to execute in.
+     * @return a ReportConfiguration
+     */
+    ReportConfiguration populateConfiguration(final ArgumentContext 
argumentContext) {
+        argumentContext.processArgs();
+        final ReportConfiguration configuration = 
argumentContext.getConfiguration();
+        final CommandLine commandLine = argumentContext.getCommandLine();
+        if (!configuration.hasSource()) {
+            for (String s : commandLine.getArgs()) {
+                IReportable reportable = getReportable(new File(s), 
configuration);
+                if (reportable != null) {
+                    configuration.addSource(reportable);
+                }
+            }
+        }
+        return configuration;
+    }
+
+    /**
+     * Creates an IReportable object from the directory name and 
ReportConfiguration
+     * object.
+     *
+     * @param base the directory that contains the files to report on.
+     * @param config the ReportConfiguration.
+     * @return the IReportable instance containing the files.
+     */
+    IReportable getReportable(final File base, final ReportConfiguration 
config) {
+        File absBase = base.getAbsoluteFile();
+        DocumentName documentName = DocumentName.builder(absBase).build();
+        if (!absBase.exists()) {
+            DefaultLog.getInstance().error("Directory '" + documentName + "' 
does not exist.");
+            return null;
+        }
+        DocumentNameMatcher documentExcluder = 
config.getDocumentExcluder(documentName);
+
+        Document doc = new FileDocument(documentName, absBase, 
documentExcluder);
+        if (!documentExcluder.matches(doc.getName())) {
+            DefaultLog.getInstance().error("Directory '" + documentName + "' 
is in excluded list.");
+            return null;
+        }
+
+        if (absBase.isDirectory()) {
+            return new DirectoryWalker(doc);
+        }
+
+        return new ArchiveWalker(doc);
+    }
+
+    /**
+     * This class implements the {@code Comparator} interface for comparing 
Options.
+     */
+    private static final class OptionComparator implements Comparator<Option>, 
Serializable {
+        /** The serial version UID.  */
+        @Serial
+        private static final long serialVersionUID = 5305467873966684014L;
+
+        private String getKey(final Option opt) {
+            String key = opt.getOpt();
+            key = key == null ? opt.getLongOpt() : key;
+            return key;
+        }
+
+        /**
+         * Compares its two arguments for order. Returns a negative integer, 
zero, or a
+         * positive integer as the first argument is less than, equal to, or 
greater
+         * than the second.
+         *
+         * @param opt1 The first Option to be compared.
+         * @param opt2 The second Option to be compared.
+         * @return a negative integer, zero, or a positive integer as the 
first argument
+         * is less than, equal to, or greater than the second.
+         */
+        @Override
+        public int compare(final Option opt1, final Option opt2) {
+            return getKey(opt1).compareToIgnoreCase(getKey(opt2));
+        }
+    }
+
+    public enum ArgumentType {
+        /**
+         * A plain file.
+         */
+        FILE("File", () -> "A file name."),
+        /**
+         * An Integer.
+         */
+        INTEGER("Integer", () -> "An integer value."),
+        /**
+         * A directory or archive.
+         */
+        DIRORARCHIVE("DirOrArchive", () -> "A directory or archive file to 
scan."),
+        /**
+         * A matching expression.
+         */
+        EXPRESSION("Expression", () -> "A file matching pattern usually of the 
form used in Ant build files and " +
+                "'.gitignore' files (see 
https://ant.apache.org/manual/dirtasks.html#patterns for examples). " +
+                "Regular expression patterns may be specified by surrounding 
the pattern with '%regex[' and ']'. " +
+                "For example '%regex[[A-Z].*]' would match files and 
directories that start with uppercase latin letters."),
+        /**
+         * A license filter.
+         */
+        LICENSEFILTER("LicenseFilter", () -> format("A defined filter for the 
licenses to include. Valid values: %s.",
+                asString(LicenseSetFactory.LicenseFilter.values()))),
+        /**
+         * A log level.
+         */
+        LOGLEVEL("LogLevel", () -> format("The log level to use. Valid values 
%s.", asString(Level.values()))),
+        /**
+         * A processing type.
+         */
+        PROCESSINGTYPE("ProcessingType", () -> format("Specifies how to 
process file types. Valid values are: %s%n",
+                Arrays.stream(ReportConfiguration.Processing.values())
+                        .map(v -> format("\t%s: %s", v.name(), v.desc()))
+                        .collect(Collectors.joining(System.lineSeparator())))),
+        /**
+         * A style sheet.
+         */
+        STYLESHEET("StyleSheet", () -> format("Either an external XSLT file or 
one of the internal named sheets. Internal sheets are: %n%s",
+                Arrays.stream(StyleSheets.values())
+                        .map(v -> format("\t%s: %s%n", v.arg(), v.desc()))
+                        .collect(Collectors.joining(System.lineSeparator())))),
+        /**
+         * A license id.
+         */
+        LICENSEID("LicenseID", () -> "The ID for a license."),
+        /**
+         * A license family id.
+         */
+        FAMILYID("FamilyID", () -> "The ID for a license family."),
+        /**
+         * A standard collection name.
+         */
+        STANDARDCOLLECTION("StandardCollection", () -> format("Defines 
standard expression patterns (see above). Valid values are: %n%s%n",
+                Arrays.stream(StandardCollection.values())
+                        .map(v -> format("\t%s: %s%n", v.name(), v.desc()))
+                        .collect(Collectors.joining(System.lineSeparator())))),
+        /**
+         * A Counter pattern name
+         */
+        COUNTERPATTERN("CounterPattern", () -> format("A pattern comprising 
one of the following prefixes followed by " +
+                        "a colon and a count (e.g. %s:5).  Prefixes are 
%n%s.", ClaimStatistic.Counter.UNAPPROVED,
+                Arrays.stream(ClaimStatistic.Counter.values())
+                        .map(v -> format("\t%s: %s Default range [%s, %s]%n", 
v.name(), v.getDescription(),
+                                v.getDefaultMinValue(),
+                                v.getDefaultMaxValue() == -1 ? "unlimited" : 
v.getDefaultMaxValue()))
+                        .collect(Collectors.joining(System.lineSeparator())))),
+        /**
+         * A generic argument.
+         */
+        ARG("Arg", () -> "A string"),
+        /**
+         * No argument.
+         */
+        NONE("", () -> "");
+
+        /**
+         * The display name
+         */
+        private final String displayName;
+        /**
+         * A supplier of the description
+         */
+        private final Supplier<String> description;
+
+        ArgumentType(final String name,
+                     final Supplier<String> description) {
+            this.displayName = name;
+            this.description = description;
+        }
+
+        public String getDisplayName() {
+            return displayName;
+        }
+
+        public Supplier<String> description() {
+            return description;
+        }
+    }
+}
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/commandline/UpdatableOptionGroup.java
 
b/apache-rat-core/src/main/java/org/apache/rat/commandline/UpdatableOptionGroup.java
new file mode 100644
index 00000000..96e08743
--- /dev/null
+++ 
b/apache-rat-core/src/main/java/org/apache/rat/commandline/UpdatableOptionGroup.java
@@ -0,0 +1,60 @@
+/*
+ * 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.commandline;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionGroup;
+
+/**
+ * An implementation of Apache Commons CLI OptionGroup that allows options to 
be removed (disabled).
+ */
+public class UpdatableOptionGroup extends OptionGroup {
+    /** The set of options to remove */
+    private final Set<Option> removedOptions = new HashSet<>();
+
+    /**
+     * Disable an option in the group.
+     * @param option The option to disable.
+     */
+    public final void disableOption(final Option option) {
+        removedOptions.add(option);
+    }
+
+    /**
+     * Reset the group so that all disabled options are re-enabled.
+     */
+    public void reset() {
+        removedOptions.clear();
+    }
+
+    @Override
+    public Collection<Option> getOptions() {
+        return super.getOptions().stream().filter(opt -> 
!removedOptions.contains(opt)).toList();
+    }
+
+    @Override
+    public UpdatableOptionGroup addOption(final Option option) {
+        super.addOption(option);
+        return this;
+    }
+}
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/ui/AbstractCodeGenerator.java 
b/apache-rat-core/src/main/java/org/apache/rat/ui/AbstractCodeGenerator.java
new file mode 100644
index 00000000..b9f76c1f
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/ui/AbstractCodeGenerator.java
@@ -0,0 +1,143 @@
+/*
+ * 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.ui;
+
+import java.io.IOException;
+import java.util.function.Function;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.text.WordUtils;
+import org.apache.rat.DeprecationReporter;
+
+import static java.lang.String.format;
+import static org.apache.rat.OptionCollectionParser.ArgumentType.NONE;
+
+/**
+ * Generates the ${code org.apache.rat.maven.AbstractMaven} source code.
+ * @param <T> The concrete implementation of the AbstractOption.
+ */
+public abstract class AbstractCodeGenerator<T extends AbstractOption<?>> {
+    /** The base source directory */
+    protected final String baseDirectory;
+    /**
+     * private constructor.
+     * @param baseDirectory The base source directory.
+     */
+    protected AbstractCodeGenerator(final String baseDirectory) {
+        this.baseDirectory = baseDirectory;
+    }
+
+    /**
+     * Gets the options for the command line.
+     * @return the command line options.
+     */
+    private static Options getOptions() {
+        return new Options()
+                .addOption(Option.builder("h").longOpt("help").desc("Print 
this message").build())
+                
.addOption(Option.builder("p").longOpt("path").required().hasArg().desc("The 
path to the base of the generated java code directory").build());
+    }
+
+
+    /**
+     * Executable entry point.
+     * @param args the arguments for the executable
+     * @throws IOException on IO error.
+     */
+    protected static void processArgs(final String syntax,
+                                   final Function<String, 
AbstractCodeGenerator<?>> instance,
+                                   final String[] args)
+            throws IOException {
+        CommandLine commandLine = null;
+        try {
+            commandLine = 
DefaultParser.builder().setDeprecatedHandler(DeprecationReporter.getLogReporter())
+                    .build().parse(getOptions(), args);
+        } catch (ParseException pe) {
+            HelpFormatter formatter = new HelpFormatter();
+            formatter.printHelp(syntax, pe.getMessage(), getOptions(), "");
+            System.exit(1);
+        }
+
+        if (commandLine.hasOption("h")) {
+            HelpFormatter formatter = new HelpFormatter();
+            formatter.printHelp(syntax, getOptions());
+            System.exit(0);
+        }
+        AbstractCodeGenerator<?> codeGenerator = 
instance.apply(commandLine.getOptionValue("p"));
+        codeGenerator.execute();
+    }
+
+    /**
+     * Executes the code generation.
+     * @throws IOException on IO error
+     */
+    protected abstract void execute() throws IOException;
+
+    /**
+     * Creates the description for a method.
+     * @param abstractOption the option generating the method.
+     * @return the description for the method in {@code AbstractMaven.java}.
+     */
+    protected final String createDesc(final T abstractOption) {
+        String desc = abstractOption.getDescription();
+        if (desc == null) {
+            throw new IllegalStateException(format("Description for %s may not 
be null", abstractOption.getName()));
+        }
+        if (!desc.contains(".")) {
+            throw new IllegalStateException(format("First sentence of 
description for %s must end with a '.'", abstractOption.getName()));
+        }
+        if (abstractOption.getArgType() != NONE) {
+            desc = format("%s Argument%s should be %s%s. (See Argument Types 
for clarification)", desc, abstractOption.hasArgs() ? "s" : "",
+                    abstractOption.hasArgs() ? "" : "a ", 
abstractOption.getArgName());
+        }
+        return desc;
+    }
+
+    /**
+     * Gets the argument description for the method returned from ${link 
createMethodName}.
+     * @param abstractOption the maven option generating the method.
+     * @param desc the description of the argument.
+     * @return the argument description for the method in {@code 
AbstractMaven.java}.
+     */
+    protected String createArgDesc(final T abstractOption, final String desc) {
+        if (abstractOption.hasArg()) {
+            String argDesc = desc.substring(desc.indexOf(" ") + 1, 
desc.indexOf(".") + 1);
+            return WordUtils.capitalize(argDesc.substring(0, 1)) + 
argDesc.substring(1);
+        } else {
+            return "The state";
+        }
+    }
+
+    /**
+     * Gets method name for the option.
+     * @param abstractOption the maven option generating the method.
+     * @return the method name description for the method in {@code 
AbstractMaven.java}.
+     */
+    protected abstract String createMethodName(T abstractOption);
+
+    /**
+     * Gathers all method definitions into a single string.
+     * @return the definition of all the methods.
+     */
+    protected abstract String gatherMethods();
+}
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/ui/AbstractOption.java 
b/apache-rat-core/src/main/java/org/apache/rat/ui/AbstractOption.java
new file mode 100644
index 00000000..42957067
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/ui/AbstractOption.java
@@ -0,0 +1,232 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rat.ui;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.cli.Option;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.rat.OptionCollectionParser;
+
+import static java.lang.String.format;
+
+/**
+ * Abstract class that provides the framework for UI-specific RAT options.
+ * In this context UI option means an option expressed in the specific UI.
+ * @param <T> the concrete implementation of AbstractOption.
+ */
+public abstract class AbstractOption<T extends AbstractOption<T>> {
+    /** The pattern to match CLI options in text */
+    protected static final Pattern PATTERN = Pattern.compile("-(-[a-z0-9]+)+");
+    /** The actual UI-specific name for the option */
+    protected final Option option;
+    /** The name for the option */
+    protected final String name;
+    /** The argument type for this option */
+    protected final OptionCollectionParser.ArgumentType argumentType;
+    /** The AbstractOptionCollection associated with this AbstractOption */
+    protected final AbstractOptionCollection<T> optionCollection;
+
+    /**
+     * Constructor.
+     *
+     * @param option The CLI option
+     * @param name the UI-specific name for the option.
+     */
+    protected <C extends AbstractOptionCollection<T>> AbstractOption(final C 
optionCollection, final Option option, final String name) {
+        this.optionCollection = optionCollection;
+        this.option = option;
+        this.name = name;
+        argumentType = option.hasArg() ?
+                option.getArgName() == null ? 
OptionCollectionParser.ArgumentType.ARG :
+                
OptionCollectionParser.ArgumentType.valueOf(option.getArgName().toUpperCase(Locale.ROOT))
 :
+                OptionCollectionParser.ArgumentType.NONE;
+    }
+
+    /**
+     * Gets the AbstractOptionCollection that this option is a member of.
+     * @return the AbstractOptionCollection that this option is a member of.
+     */
+    public final AbstractOptionCollection<T> getOptionCollection() {
+        return optionCollection;
+    }
+
+    /**
+     * Gets the option this abstract option is wrapping.
+     * @return the original Option.
+     */
+    public final Option getOption() {
+        return option;
+    }
+
+    /**
+     * Return default value.
+     * @return default value or {@code null} if no argument given.
+     */
+    public final String getDefaultValue() {
+        return optionCollection.getDefaultValue(option);
+    }
+
+    /**
+     * Provide means to wrap the given option depending on the UI-specific 
option implementation.
+     * @param option The CLI option
+     * @return the cleaned up option name.
+     */
+    protected abstract String cleanupName(Option option);
+
+    /**
+     * Gets an example of how to use this option in the native UI.
+     * @return An example of how to use this option in the native UI.
+     */
+    public abstract String getExample();
+
+    /**
+     * Replaces CLI pattern options with implementation specific pattern 
options.
+     * @param str the string to clean.
+     * @return the string with CLI names replaced with implementation specific 
names.
+     */
+    public String cleanup(final String str) {
+        String workingStr = str;
+        if (StringUtils.isNotBlank(workingStr)) {
+            Map<String, String> maps = new HashMap<>();
+            Matcher matcher = PATTERN.matcher(workingStr);
+            while (matcher.find()) {
+                String key = matcher.group();
+                String optKey = key.substring(2);
+                Optional<Option> maybeResult = 
getOptionCollection().getOptions().getOptions().stream()
+                                .filter(o -> optKey.equals(o.getOpt()) || 
optKey.equals(o.getLongOpt())).findFirst();
+                maybeResult.ifPresent(value -> maps.put(key, 
cleanupName(value)));
+            }
+            for (Map.Entry<String, String> entry : maps.entrySet()) {
+                workingStr = workingStr.replaceAll(Pattern.quote(format("%s", 
entry.getKey())), entry.getValue());
+            }
+        }
+        return workingStr;
+    }
+
+    /**
+     * Gets the implementation specific name for the CLI option.
+     * @return The implementation specific name for the CLI option.
+     */
+    public final String getName() {
+        return name;
+    }
+
+    /**
+     * return a string showing long and short options if they are available. 
Will return
+     * a string.
+     * @return A string showing long and short options if they are available. 
Never {@code null}.
+     */
+    public abstract String getText();
+
+    /**
+     * Gets the description in implementation specific format.
+     *
+     * @return the description or an empty string.
+     */
+    public final String getDescription() {
+        return cleanup(option.getDescription());
+    }
+
+    /**
+     * Gets the simple class name for the data type for this option.
+     * Normally "String".
+     * @return the simple class name for the type.
+     */
+    public final Class<?> getType() {
+        return option.hasArg() ? ((Class<?>) option.getType()) : boolean.class;
+    }
+
+    /**
+     * Gets the argument name if there is one.
+     * @return the Argument name
+     */
+    public final String getArgName() {
+        return argumentType.getDisplayName();
+    }
+
+    /**
+     * Gets the argument type if there is one.
+     * @return the Argument name
+     */
+    public final OptionCollectionParser.ArgumentType getArgType() {
+        return argumentType;
+    }
+
+    /**
+     * Determines if the option is deprecated.
+     * @return {@code true} if the option is deprecated
+     */
+    public final boolean isDeprecated() {
+        return option.isDeprecated();
+    }
+
+    /**
+     * Determines if the option is required.
+     * @return {@code true} if the option is required.
+     */
+    public final boolean isRequired() {
+        return option.isRequired();
+    }
+
+    /**
+     * Determine if the enclosed option expects an argument.
+     * @return {@code true} if the enclosed option expects at least one 
argument.
+     */
+    public final boolean hasArg() {
+        return option.hasArg();
+    }
+
+    /**
+     * Returns {@code true} if the option has multiple arguments.
+     * @return {@code true} if the option has multiple arguments.
+     */
+    public final boolean hasArgs() {
+        return option.hasArgs();
+    }
+
+    /**
+     * Returns the number of arguments.
+     * @return The number of arguments.
+     */
+    public final int argCount() {
+        return option.getArgs();
+    }
+
+    /**
+     * The key value for the option.
+     * @return the key value for the CLI argument map.
+     */
+    public final String keyValue() {
+        return StringUtils.defaultIfEmpty(option.getLongOpt(), 
option.getOpt());
+    }
+
+    /**
+     * Gets the deprecated string if the option is deprecated, or an empty 
string otherwise.
+     * @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;
+    }
+}
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/ui/AbstractOptionCollection.java 
b/apache-rat-core/src/main/java/org/apache/rat/ui/AbstractOptionCollection.java
new file mode 100644
index 00000000..70b4f1e2
--- /dev/null
+++ 
b/apache-rat-core/src/main/java/org/apache/rat/ui/AbstractOptionCollection.java
@@ -0,0 +1,162 @@
+/*
+ * 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.ui;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.rat.commandline.Arg;
+import org.apache.rat.commandline.UpdatableOptionGroup;
+
+/**
+ *
+ * @param <T> the AbstractOption implementation.
+ */
+public abstract class AbstractOptionCollection<T extends AbstractOption<T>> {
+
+    /** The collection of unsupported options */
+    protected final Collection<Option> unsupportedOptions;
+    /** The additional options for the specific UI.  Used for documentation 
generation */
+    protected final Options additionalOptions;
+    /**
+     * Create an Instance.
+     * @param unsupportedOptions the collection of options that are not 
supported.
+     */
+    protected AbstractOptionCollection(final Collection<Option> 
unsupportedOptions, final Options additionalOptions) {
+        this.unsupportedOptions = unsupportedOptions;
+        this.additionalOptions = additionalOptions;
+    }
+
+    /**
+     * Gets the collection of unsupported Options.
+     * @return the Options comprised for the unsupported options.
+     */
+    public final Options getUnsupportedOptions() {
+        Options options = new Options();
+        unsupportedOptions.forEach(options::addOption);
+        return options;
+    }
+
+    /**
+     * Get a mapping function from an Apache Commons cli Option to the 
AbstractOption implementation.
+     * @return a mapping function from an Apache Commons cli Option to the 
AbstractOption implementation.
+     */
+    protected abstract Function<Option, T> getMapper();
+
+    /**
+     * Creates an AbstractOption instance ({@code T}) from an Apache Commons 
cli Option.
+     * @param option the option to build the instance from.
+     * @return an AbstractOption instance from an Apache Commons cli Option.
+     */
+    public final T getMappedOption(final Option option) {
+        return getMapper().apply(option);
+    }
+
+    /**
+     * Gets an Apache Commons cli Options that contains all the Apache Commons 
cli Options that are understood by this collection.
+     * @return an Apache Commons cli Options that contains all the Apache 
Commons cli Options that are understood by this collection.
+     */
+    public final Options getOptions() {
+        Options result = new Options();
+        Options argOptions = Arg.getOptions();
+        Set<UpdatableOptionGroup> optionGroups = new HashSet<>();
+        for (Option option : argOptions.getOptions()) {
+            optionGroups.add((UpdatableOptionGroup) 
argOptions.getOptionGroup(option));
+        }
+        for (Option option : unsupportedOptions) {
+            UpdatableOptionGroup group = (UpdatableOptionGroup) 
argOptions.getOptionGroup(option);
+            if (group != null) {
+                group.disableOption(option);
+            }
+        }
+        optionGroups.forEach(result::addOptionGroup);
+        return result.addOptions(additionalOptions());
+    }
+
+    /**
+     * Gets the Stream of AbstractOption implementations  understood by this 
collection.
+     * @return the Stream of AbstractOption implementations understood by this 
collection.
+     */
+    public final Stream<T> getMappedOptions() {
+        return getOptions().getOptions().stream().map(getMapper());
+    }
+
+    /**
+     * Gets a map client option name to specified AbstractOption 
implementation.
+     * @return a map client option name to specified AbstractOption 
implementation
+     */
+    public Map<String, T> getOptionMap() {
+        Map<String, T> result = new TreeMap<>();
+        getMappedOptions().forEach(mappedOption -> 
result.put(ArgumentTracker.extractKey(mappedOption.getOption()), mappedOption));
+        return result;
+    }
+
+    /**
+     * Gets the additional options understood by this collection.
+     * @return the additional options understood by this collection.
+     */
+    public final Options additionalOptions() {
+        return additionalOptions;
+    }
+
+    /**
+     * Gets the map of default overrides.
+     * @return the default overrides.
+     */
+    protected abstract Map<Option, String> defaultOverrides();
+
+    /**
+     * Specifies the default value for the option.
+     * @param option the option to override.
+     * @param value the value to use as the default.
+     */
+    public abstract void addOverride(Option option, String value);
+
+    /**
+     * Adds overrides the default value for all options in the Arg.
+     * @param arg the arg to override.
+     * @param value the value to set as the default value.
+     */
+    public void addOverride(final Arg arg, final String value) {
+        arg.group().getOptions().forEach(option -> this.addOverride(option, 
value));
+    }
+
+    /**
+     * Gets the default value for the option.
+     * @param option the option to lookup.
+     * @return the default value or {@code null} if not set.
+     */
+    public final String getDefaultValue(final Option option) {
+        String override = defaultOverrides().get(option);
+        if (override == null) {
+            Arg arg = Arg.findArg(option);
+            if (arg != null) {
+                override = arg.defaultValue();
+            }
+        }
+        return override;
+    }
+}
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/ui/ArgumentTracker.java 
b/apache-rat-core/src/main/java/org/apache/rat/ui/ArgumentTracker.java
new file mode 100644
index 00000000..e905356f
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/ui/ArgumentTracker.java
@@ -0,0 +1,211 @@
+/*
+ * 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.ui;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+
+import org.apache.commons.cli.Option;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.rat.DeprecationReporter;
+import org.apache.rat.commandline.Arg;
+import org.apache.rat.utils.DefaultLog;
+import org.apache.rat.utils.Log;
+
+/**
+ * Tracks arguments that are set and their values for conversion from native 
UI to
+ * Apache Commons command line values.  Native values
+ */
+public final class ArgumentTracker {
+
+    /**
+     * List of deprecated arguments and their deprecation notice.
+     */
+    private final Map<String, String> deprecatedArgs = new HashMap<>();
+
+    /**
+     * A map of CLI-based arguments to values.
+     */
+    private final Map<String, List<String>> args = new HashMap<>();
+
+    /**
+     * The arguments set by the UI for the current report execution.
+     * @param uiOptionList the list of AbstractOption implementations for this 
UI.
+     */
+    public ArgumentTracker(final List<? extends AbstractOption<?>> 
uiOptionList) {
+        for (AbstractOption<?> abstractOption : uiOptionList) {
+            if (abstractOption.isDeprecated()) {
+                deprecatedArgs.put(abstractOption.getName(),
+                        String.format("Use of deprecated option '%s'. %s", 
abstractOption.getName(), abstractOption.getDeprecated()));
+            }
+        }
+    }
+
+    /**
+     * Extract the core name from the option.  This is the {@link 
Option#getLongOpt()} if defined, otherwise
+     * the {@link Option#getOpt()}.
+     * @param option the commons cli option.
+     * @return the common cli based name.
+     */
+    public static String extractKey(final Option option) {
+        return StringUtils.defaultIfBlank(option.getLongOpt(), 
option.getOpt());
+    }
+
+    /**
+     * Sets the deprecation report method.
+     */
+    private void setDeprecationReporter() {
+        DeprecationReporter.setLogReporter(opt -> {
+            String msg = deprecatedArgs.get(extractKey(opt));
+            if (msg == null) {
+                DeprecationReporter.getDefault().accept(opt);
+            } else {
+                DefaultLog.getInstance().warn(msg);
+            }
+        });
+    }
+
+    /**
+     * Gets the list of arguments prepared for the CLI code to parse.
+     * @return the List of arguments for the CLI command line.
+     */
+    public List<String> args() {
+        final List<String> result = new ArrayList<>();
+        for (Map.Entry<String, List<String>> entry : args.entrySet()) {
+            result.add("--" + entry.getKey());
+            
result.addAll(entry.getValue().stream().filter(Objects::nonNull).collect(Collectors.toList()));
+        }
+        return result;
+    }
+
+    /**
+     * Applies the consumer to each arg and list in turn.
+     */
+    public void apply(final BiConsumer<String, List<String>> consumer) {
+        args.forEach((key, value) -> consumer.accept(key, new 
ArrayList<>(value)));
+    }
+
+    private boolean validateSet(final String key) {
+        final Arg arg = Arg.findArg(key);
+        if (arg != null) {
+            final Option opt = arg.find(key);
+            final Option main = arg.option();
+            if (opt.isDeprecated()) {
+                args.remove(extractKey(main));
+                // deprecated options must be explicitly set so let it go.
+                return true;
+            }
+            // non-deprecated options may have default so ignore it if another 
option has already been set.
+            for (Option o : arg.group().getOptions()) {
+                if (!o.equals(main)) {
+                    if (args.containsKey(extractKey(o))) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Set a key and value into the argument list.
+     * Replaces any existing value.
+     * @param key the key for the map.
+     * @param value the value to set.
+     */
+    public void setArg(final String key, final String value) {
+        if (value == null || StringUtils.isNotBlank(value)) {
+            if (validateSet(key)) {
+                List<String> values = new ArrayList<>();
+                if (DefaultLog.getInstance().isEnabled(Log.Level.DEBUG)) {
+                    DefaultLog.getInstance().debug(String.format("Setting %s 
to '%s'", key, value));
+                }
+                values.add(value);
+                args.put(key, values);
+            }
+        }
+    }
+
+    /**
+     * Get the list of values for a key.
+     * @param key the key for the map.
+     * @return the list of values for the key or {@code null} if not set.
+     */
+    public Optional<List<String>> getArg(final String key) {
+        return Optional.ofNullable(args.get(key));
+    }
+
+    /**
+     * Add values to the key in the argument list.
+     * empty values are ignored. If no non-empty values are present no change 
is made.
+     * If the key does not exist, adds it.
+     * @param key the key for the map.
+     * @param value the array of values to set.
+     */
+    public void addArg(final String key, final String[] value) {
+        List<String> newValues = 
Arrays.stream(value).filter(StringUtils::isNotBlank).collect(Collectors.toList());
+        if (!newValues.isEmpty()) {
+            if (validateSet(key)) {
+                if (DefaultLog.getInstance().isEnabled(Log.Level.DEBUG)) {
+                    DefaultLog.getInstance().debug(String.format("Adding [%s] 
to %s", String.join(", ", Arrays.asList(value)), key));
+                }
+                List<String> values = args.computeIfAbsent(key, k -> new 
ArrayList<>());
+                values.addAll(newValues);
+            }
+        }
+    }
+
+    /**
+     * Add a value to the key in the argument list.
+     * If the key does not exist, adds it.
+     * @param key the key for the map.
+     * @param value the value to set.
+     */
+    public void addArg(final String key, final String value) {
+        if (StringUtils.isNotBlank(value)) {
+            if (validateSet(key)) {
+                List<String> values = args.get(key);
+                if (DefaultLog.getInstance().isEnabled(Log.Level.DEBUG)) {
+                    DefaultLog.getInstance().debug(String.format("Adding [%s] 
to %s", String.join(", ", Arrays.asList(value)), key));
+                }
+                if (values == null) {
+                    values = new ArrayList<>();
+                    args.put(key, values);
+                }
+                values.add(value);
+            }
+        }
+    }
+
+    /**
+     * Remove a key from the argument list.
+     * @param key the key to remove from the map.
+     */
+    public void removeArg(final String key) {
+        args.remove(key);
+    }
+}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/ui/UI.java 
b/apache-rat-core/src/main/java/org/apache/rat/ui/UI.java
new file mode 100644
index 00000000..7e8afa8e
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/ui/UI.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.ui;
+
+public interface UI<T extends AbstractOption<T>> {
+    /**
+     * Gets the common name of this UI.
+     * @return the common name of this UI.
+     */
+    default String name() {
+        return getClass().getSimpleName();
+    }
+
+    /**
+     * Gets the OptionFactory configuration for this UI.
+     *
+     * @return the OptionFactory configuration for this UI.
+     */
+    AbstractOptionCollection<T> getOptionCollection();
+}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/ui/package-info.java 
b/apache-rat-core/src/main/java/org/apache/rat/ui/package-info.java
new file mode 100644
index 00000000..0244aa7a
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/ui/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+/**
+ * Classes to generate UIs
+ */
+package org.apache.rat.ui;
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/ui/spi/UIProvider.java 
b/apache-rat-core/src/main/java/org/apache/rat/ui/spi/UIProvider.java
new file mode 100644
index 00000000..02609943
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/ui/spi/UIProvider.java
@@ -0,0 +1,25 @@
+/*
+ * 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.ui.spi;
+
+import org.apache.rat.ui.UI;
+
+public interface UIProvider {
+    UI<?> create();
+}
diff --git 
a/apache-rat-core/src/main/java/org/apache/rat/ui/spi/package-info.java 
b/apache-rat-core/src/main/java/org/apache/rat/ui/spi/package-info.java
new file mode 100644
index 00000000..982ca301
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/ui/spi/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+/**
+ * The SPI implementation for the UIs
+ */
+package org.apache.rat.ui.spi;
diff --git a/pom.xml b/pom.xml
index 7ee69c2e..8e5759bd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,6 +85,11 @@ agnostic home for software distribution comprehension and 
audit tools.
   </distributionManagement>
   <dependencyManagement>
     <dependencies>
+      <dependency>
+        <groupId>com.github.spotbugs</groupId>
+        <artifactId>spotbugs-annotations</artifactId>
+        <version>4.9.8</version>
+      </dependency>
       <!-- used to render the site and make skin updates more transparent -->
       <dependency>
         <groupId>org.apache.maven.skins</groupId>

Reply via email to