1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one *
3 * or more contributor license agreements. See the NOTICE file *
4 * distributed with this work for additional information *
5 * regarding copyright ownership. The ASF licenses this file *
6 * to you under the Apache License, Version 2.0 (the *
7 * "License"); you may not use this file except in compliance *
8 * with the License. You may obtain a copy of the License at *
9 * *
10 * https://www.apache.org/licenses/LICENSE-2.0 *
11 * *
12 * Unless required by applicable law or agreed to in writing, *
13 * software distributed under the License is distributed on an *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15 * KIND, either express or implied. See the License for the *
16 * specific language governing permissions and limitations *
17 * under the License. *
18 */
19 package org.apache.rat.ui;
20
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.Optional;
28 import java.util.TreeMap;
29 import java.util.function.Supplier;
30 import java.util.stream.Stream;
31
32 import org.apache.commons.cli.AlreadySelectedException;
33 import org.apache.commons.cli.Option;
34 import org.apache.commons.cli.Options;
35 import org.apache.commons.lang3.StringUtils;
36 import org.apache.rat.Defaults;
37 import org.apache.rat.commandline.Arg;
38 import org.apache.rat.utils.Log;
39
40 /**
41 * A collection of options supported by the UI. This includes RAT options and UI specific options.
42 * @param <T> the UIOption implementation.
43 */
44 public class UIOptionCollection<T extends UIOption<T>> {
45 /** map of ARG to the associated UpdatableOptionGroup */
46 private final Map<Arg, UpdatableOptionGroup> argMap;
47 /** set of RAT OptionGroups with unsupported options for this UI removed */
48 private final UpdatableOptionGroupCollection supportedRatOptions;
49 /** set of UI specific options */
50 private final Map<Option, T> uiOptions;
51 /**
52 * Map of option to overridden default value. Generally applies to supported RAT options but may be
53 * UI-specific options as well.
54 */
55 private final Map <Option, String> defaultValues;
56
57 /**
58 * Construct the UIOptionCollection from the builder.
59 * @param builder the Builder to build from.
60 */
61 protected UIOptionCollection(final Builder<T, ?> builder) {
62 Objects.requireNonNull(builder.uiOptionBuilderSupplier, "Builder.mapper");
63 argMap = new TreeMap<>();
64 supportedRatOptions = new UpdatableOptionGroupCollection();
65
66 for (Arg arg : Arg.values()) {
67 argMap.put(arg, supportedRatOptions.add(arg.group()));
68 }
69
70 for (Option opt : builder.unsupportedRatOptions) {
71 supportedRatOptions.findGroups(opt).forEach(group -> group.disableOption(opt));
72 }
73 uiOptions = new HashMap<>();
74 UIOption.Builder<T, ?> optBuilder = builder.uiOptionBuilderSupplier.get();
75 optBuilder.optionCollection(this);
76 supportedRatOptions.options().getOptions()
77 .forEach(option -> uiOptions.put(option, optBuilder.option(option).build()));
78 builder.uiOptions.stream().filter(option -> !uiOptions.containsKey(option))
79 .forEach(option -> uiOptions.put(option, optBuilder.option(option).build()));
80 defaultValues = new HashMap<>(builder.defaultValues);
81 }
82
83 /**
84 * Extracts the UI based name string for an option.
85 * The default implementation return the string returned by {@link ArgumentTracker#extractKey(Option)}
86 *
87 * @param option the option to create the name for.
88 * @return the name for the option in the UICollection name, may be the same as the option name.
89 */
90 public String rename(final Option option) {
91 return ArgumentTracker.extractKey(option);
92 }
93
94 /**
95 * Checks if an {@link Arg} is selected.
96 * @param arg the Arg to check.
97 * @return {@code true} if the arg is selected.
98 */
99 public final boolean isSelected(final Arg arg) {
100 UpdatableOptionGroup group = argMap.get(arg);
101 return group != null && group.getSelected() != null;
102 }
103
104 /**
105 * Gets the selected Option for an Arg.
106 * @param arg the Arg to get the Option for..
107 * @return an Optional containing the selected option, or an empty Optional if none was selected.
108 */
109 public final Optional<Option> getSelected(final Arg arg) {
110 UpdatableOptionGroup group = argMap.get(arg);
111 String s = group == null ? null : group.getSelected();
112 if (s != null) {
113 for (Option result : group.getOptions()) {
114 if (result.getKey().equals(s)) {
115 return Optional.of(result);
116 }
117 }
118 }
119 return Optional.empty();
120 }
121
122 /**
123 * Resets the groups in the Args so that they are unused and ready to detect the next set of arguments.
124 */
125 public void resetSelected() {
126 argMap.values().forEach(uog -> {
127 try {
128 uog.setSelected(null);
129 } catch (AlreadySelectedException e) {
130 throw new RuntimeException(e);
131 }
132 });
133 }
134
135 /**
136 * Gets the collection of unsupported Options.
137 * @return the Options comprising the unsupported options.
138 */
139 public final Options getUnsupportedOptions() {
140 return supportedRatOptions.unsupportedOptions();
141 }
142
143 /**
144 * Gets the UIOption instance for the Option.
145 * @param option the option to find the instance of.
146 * @return an Optional containing the UIOption for the option, or an empty Optional if the option is not in the UI.
147 */
148 public final Optional<T> getMappedOption(final Option option) {
149 return Optional.ofNullable(uiOptions.get(option));
150 }
151
152 /**
153 * Gets an Options that contains the Options and OptionGroups are understood by this collection.
154 * OptionGroups are registered in the resulting Options object.
155 * @return an Options that contains the {@link Arg} defined Option instances that are understood by this collection as well as
156 * any additional custom Options.
157 */
158 public final Options getOptions() {
159 return supportedRatOptions.options().addOptions(additionalOptions());
160 }
161
162 /**
163 * Gets the Stream of UIOption implementations understood by this collection.
164 * @return the Stream of UIOption implementations understood by this collection.
165 */
166 public final Stream<T> getMappedOptions() {
167 return uiOptions.values().stream();
168 }
169
170 /**
171 * Gets a map client option name to the specified UIOption implementation.
172 * @return a map client option name to the specified UIOption implementation.
173 */
174 public final Map<String, T> getOptionMap() {
175 Map<String, T> result = new TreeMap<>();
176 getMappedOptions().forEach(mappedOption -> result.put(ArgumentTracker.extractKey(mappedOption.getOption()), mappedOption));
177 return result;
178 }
179
180 /**
181 * Gets the additional Options understood by this collection.
182 * @return the additional Options understood by this collection.
183 */
184 public final Options additionalOptions() {
185 Options options = new Options();
186 uiOptions.keySet().stream()
187 .filter(option -> !supportedRatOptions.contains(option))
188 .forEach(options::addOption);
189 return options;
190 }
191
192 /**
193 * Gets the default value for the option.
194 * @param option the option to lookup.
195 * @return the default value or {@code null} if not set.
196 */
197 public final String defaultValue(final Option option) {
198 return defaultValues.get(option);
199 }
200
201 /**
202 * Gets the description for the Option.
203 * Default implementation is based on the description in the option itself as well as the deprecated state.
204 * @param option the option to get the description for.
205 * @return the description of the option.
206 */
207 public String getDescription(final Option option) {
208 StringBuilder desc = new StringBuilder();
209 getMappedOption(option).ifPresent(uiOption -> {
210 if (uiOption.isDeprecated()) {
211 desc.append("[").append(uiOption.getDeprecated()).append("] ");
212 }
213 desc.append(StringUtils.defaultIfEmpty(uiOption.getDescription(), ""));
214 });
215 return desc.toString();
216 }
217
218 /**
219 * Builder for a UIOptionCollection.
220 * @param <T> the concreate type of the UIOption.
221 * @param <B> the concrete type the Builder.
222 */
223 protected static class Builder<T extends UIOption<T>, B extends Builder<T, B>> {
224 /** Set of additional UI specific options. */
225 private final List<Option> uiOptions;
226 /**
227 * Map of option to overridden default value. Generally applies to supported RAT options but may be
228 * UI-specific options as well.
229 */
230 private final Map <Option, String> defaultValues;
231 /** The list of unsupported RAT options. */
232 protected final List<Option> unsupportedRatOptions;
233 /** The function to convert an option into a UIOption. */
234 private final Supplier<UIOption.Builder<T, ?>> uiOptionBuilderSupplier;
235
236 /**
237 * Constructor for the UI option collection builder.
238 */
239 protected Builder(final Supplier<UIOption.Builder<T, ?>> uiOptionBiulderSupplier) {
240 this.uiOptionBuilderSupplier = uiOptionBiulderSupplier;
241 uiOptions = new ArrayList<>();
242 defaultValues = new HashMap<>();
243 unsupportedRatOptions = new ArrayList<>();
244 defaultValue(Arg.LOG_LEVEL, Log.Level.WARN.name());
245 defaultValue(Arg.OUTPUT_ARCHIVE, Defaults.ARCHIVE_PROCESSING.name());
246 defaultValue(Arg.OUTPUT_STANDARD, Defaults.STANDARD_PROCESSING.name());
247 defaultValue(Arg.OUTPUT_LICENSES, Defaults.LIST_LICENSES.name());
248 defaultValue(Arg.OUTPUT_FAMILIES, Defaults.LIST_FAMILIES.name());
249 }
250
251 /**
252 * Returns this cast to {@code <B>} class.
253 * @return this as {@code <B>} class.
254 */
255 protected final B self() {
256 return (B) this;
257 }
258
259 /**
260 * Add an Option to the collection as a UIOption.
261 * @param option the Option to add.
262 * @return this
263 */
264 public B uiOption(final Option option) {
265 uiOptions.add(option);
266 return self();
267 }
268
269 /**
270 * Add multiple Option instances to the collection as UIOptions.
271 * @param options the Option instances to add.
272 * @return this
273 */
274 public B uiOptions(final Option... options) {
275 uiOptions.addAll(Arrays.asList(options));
276 return self();
277 }
278
279 /**
280 * Register an Option as unsupported.
281 * @param option the Option that is not be supported. This should be an option in the
282 * {@link Arg} collection.
283 * @return this
284 */
285 public B unsupported(final Option option) {
286 unsupportedRatOptions.add(option);
287 return self();
288 }
289
290 /**
291 * Register multiple Options as unsupported.
292 * Will ignore all the options associated with the specified Arg.
293 * @param arg The Arg to ignore.
294 * @return this
295 */
296 public B unsupported(final Arg arg) {
297 unsupportedRatOptions.addAll(arg.group().getOptions());
298 return self();
299 }
300
301 /**
302 * Specify the default values for an option.
303 * @param option the option to specify the default value for.
304 * @param value the value for the option.
305 * @return this
306 */
307 public B defaultValue(final Option option, final String value) {
308 defaultValues.put(option, value);
309 return self();
310 }
311
312 /**
313 * Specify the default values for an Arg.
314 * @param arg the Arg to specify the default value for.
315 * @param value the value for the option.
316 * @return this
317 */
318 public B defaultValue(final Arg arg, final String value) {
319 return defaultValue(arg.option(), value);
320 }
321 }
322 }