View Javadoc
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 }