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 <[email protected]> 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();
