UIOptionCollection.java
/*
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* https://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
*/
package org.apache.rat.ui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
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.commons.lang3.StringUtils;
import org.apache.rat.Defaults;
import org.apache.rat.commandline.Arg;
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 UIOption implementation.
*/
public class UIOptionCollection<T extends UIOption<T>> {
/** map of ARG to the associated UpdatableOptionGroup */
private final Map<Arg, UpdatableOptionGroup> argMap;
/** set of RAT OptionGroups with unsupported options for this UI removed */
private final UpdatableOptionGroupCollection supportedRatOptions;
/** set of UI specific options */
private final Map<Option, T> uiOptions;
/**
* Map of option to overridden default value. Generally applies to supported RAT options but may be
* UI-specific options as well.
*/
private final Map <Option, String> defaultValues;
/**
* Construct the UIOptionCollection from the builder.
* @param builder the Builder to build from.
*/
protected UIOptionCollection(final Builder<T, ?> builder) {
Objects.requireNonNull(builder.uiOptionBuilderSupplier, "Builder.mapper");
argMap = new TreeMap<>();
supportedRatOptions = new UpdatableOptionGroupCollection();
for (Arg arg : Arg.values()) {
argMap.put(arg, supportedRatOptions.add(arg.group()));
}
for (Option opt : builder.unsupportedRatOptions) {
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, optBuilder.option(option).build()));
builder.uiOptions.stream().filter(option -> !uiOptions.containsKey(option))
.forEach(option -> uiOptions.put(option, optBuilder.option(option).build()));
defaultValues = new HashMap<>(builder.defaultValues);
}
/**
* 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.
*/
public final boolean isSelected(final Arg arg) {
UpdatableOptionGroup group = argMap.get(arg);
return group != null && group.getSelected() != null;
}
/**
* 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) {
UpdatableOptionGroup group = argMap.get(arg);
String s = group == null ? null : group.getSelected();
if (s != null) {
for (Option result : group.getOptions()) {
if (result.getKey().equals(s)) {
return Optional.of(result);
}
}
}
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 comprising the unsupported options.
*/
public final Options getUnsupportedOptions() {
return supportedRatOptions.unsupportedOptions();
}
/**
* Gets the UIOption instance for the Option.
* @param option the option to find the instance of.
* @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 Options and OptionGroups are understood by this collection.
* OptionGroups are registered in the resulting Options object.
* @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 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 the specified UIOption implementation.
* @return a map client option name to the specified UIOption implementation.
*/
public final Map<String, T> getOptionMap() {
Map<String, T> result = new TreeMap<>();
getMappedOptions().forEach(mappedOption -> result.put(ArgumentTracker.extractKey(mappedOption.getOption()), mappedOption));
return result;
}
/**
* Gets the additional Options understood by this collection.
* @return the additional Options understood by this collection.
*/
public final Options additionalOptions() {
Options options = new Options();
uiOptions.keySet().stream()
.filter(option -> !supportedRatOptions.contains(option))
.forEach(options::addOption);
return options;
}
/**
* Gets the default value for the option.
* @param option the option to lookup.
* @return the default value or {@code null} if not set.
*/
public final String defaultValue(final Option option) {
return defaultValues.get(option);
}
/**
* Gets the description for the Option.
* Default implementation is based on the description in the option itself as well as the deprecated state.
* @param option the option to get the description for.
* @return the description of the option.
*/
public String getDescription(final Option option) {
StringBuilder desc = new StringBuilder();
getMappedOption(option).ifPresent(uiOption -> {
if (uiOption.isDeprecated()) {
desc.append("[").append(uiOption.getDeprecated()).append("] ");
}
desc.append(StringUtils.defaultIfEmpty(uiOption.getDescription(), ""));
});
return desc.toString();
}
/**
* 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>, B extends Builder<T, B>> {
/** Set of additional UI specific options. */
private final List<Option> uiOptions;
/**
* Map of option to overridden default value. Generally applies to supported RAT options but may be
* UI-specific options as well.
*/
private final Map <Option, String> defaultValues;
/** The list of unsupported RAT options. */
protected final List<Option> unsupportedRatOptions;
/** The function to convert an option into a UIOption. */
private final Supplier<UIOption.Builder<T, ?>> uiOptionBuilderSupplier;
/**
* Constructor for the UI option collection builder.
*/
protected Builder(final Supplier<UIOption.Builder<T, ?>> uiOptionBiulderSupplier) {
this.uiOptionBuilderSupplier = uiOptionBiulderSupplier;
uiOptions = new ArrayList<>();
defaultValues = new HashMap<>();
unsupportedRatOptions = new ArrayList<>();
defaultValue(Arg.LOG_LEVEL, Log.Level.WARN.name());
defaultValue(Arg.OUTPUT_ARCHIVE, Defaults.ARCHIVE_PROCESSING.name());
defaultValue(Arg.OUTPUT_STANDARD, Defaults.STANDARD_PROCESSING.name());
defaultValue(Arg.OUTPUT_LICENSES, Defaults.LIST_LICENSES.name());
defaultValue(Arg.OUTPUT_FAMILIES, Defaults.LIST_FAMILIES.name());
}
/**
* Returns this cast to {@code <B>} class.
* @return this as {@code <B>} class.
*/
protected final B self() {
return (B) this;
}
/**
* Add an Option to the collection as a UIOption.
* @param option the Option to add.
* @return this
*/
public B uiOption(final Option option) {
uiOptions.add(option);
return self();
}
/**
* Add multiple Option instances to the collection as UIOptions.
* @param options the Option instances to add.
* @return this
*/
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
* {@link Arg} collection.
* @return this
*/
public B unsupported(final Option option) {
unsupportedRatOptions.add(option);
return self();
}
/**
* Register multiple Options as unsupported.
* Will ignore all the options associated with the specified Arg.
* @param arg The Arg to ignore.
* @return this
*/
public B unsupported(final Arg arg) {
unsupportedRatOptions.addAll(arg.group().getOptions());
return self();
}
/**
* Specify the default values for an option.
* @param option the option to specify the default value for.
* @param value the value for the option.
* @return this
*/
public B defaultValue(final Option option, final String value) {
defaultValues.put(option, value);
return self();
}
/**
* Specify the default values for an Arg.
* @param arg the Arg to specify the default value for.
* @param value the value for the option.
* @return this
*/
public B defaultValue(final Arg arg, final String value) {
return defaultValue(arg.option(), value);
}
}
}