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 * http://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.HashMap;
22 import java.util.Locale;
23 import java.util.Map;
24 import java.util.Optional;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27
28 import org.apache.commons.cli.Option;
29 import org.apache.commons.lang3.StringUtils;
30 import org.apache.rat.OptionCollection;
31 import org.apache.rat.utils.CasedString;
32
33 import static java.lang.String.format;
34
35 /**
36 * Abstract class that provides the framework for UI-specific RAT options.
37 * In this context UI option means an option expressed in the specific UI.
38 * @param <T> the concrete implementation of AbstractOption.
39 */
40 public abstract class UIOption<T extends UIOption<T>> {
41 /** The pattern to match CLI options in text */
42 protected static final Pattern PATTERN = Pattern.compile("-?-([A-Za-z0-9]+-?)+"); // NOSONAR
43 /** The actual UI-specific name for the option */
44 protected final Option option;
45 /** The name for the option */
46 protected final CasedString name;
47 /** The argument type for this option */
48 protected final OptionCollection.ArgumentType argumentType;
49 /** The AbstractOptionCollection associated with this AbstractOption */
50 protected final UIOptionCollection<T> optionCollection;
51
52 /**
53 * Constructor.
54 *
55 * @param option The CLI option
56 * @param name the UI-specific name for the option
57 */
58 protected <C extends UIOptionCollection<T>> UIOption(final C optionCollection, final Option option, final CasedString name) {
59 this.optionCollection = optionCollection;
60 this.option = option;
61 this.name = name;
62 OptionCollection.ArgumentType argType;
63 if (option.hasArg()) {
64 if (option.getArgName() == null) {
65 argType = OptionCollection.ArgumentType.ARG;
66 } else {
67 // extract the name of the argument type.
68 try {
69 argType = OptionCollection.ArgumentType.valueOf(option.getArgName().toUpperCase(Locale.ROOT));
70 } catch (IllegalArgumentException e) {
71 argType = OptionCollection.ArgumentType.ARG;
72 }
73 }
74 } else {
75 argType = OptionCollection.ArgumentType.NONE;
76 }
77 this.argumentType = argType;
78 }
79
80 /**
81 * Gets the AbstractOptionCollection that this option is a member of.
82 * @return the AbstractOptionCollection that this option is a member of.
83 */
84 public final <X extends UIOptionCollection<T>> X getOptionCollection() {
85 return (X) optionCollection;
86 }
87
88 /**
89 * Gets the option this abstract option is wrapping.
90 * @return the original Option.
91 */
92 public final Option getOption() {
93 return option;
94 }
95
96 /**
97 * Return default value.
98 * @return default value or {@code null} if no argument given.
99 */
100 public final String getDefaultValue() {
101 return optionCollection.defaultValue(option);
102 }
103
104 /**
105 * Provide means to wrap the given option depending on the UI-specific option implementation.
106 * @param option The CLI option
107 * @return the cleaned up option name.
108 */
109 protected abstract String cleanupName(Option option);
110
111 /**
112 * Gets an example of how to use this option in the native UI.
113 * @return An example of how to use this option in the native UI.
114 */
115 public abstract String getExample();
116
117 /**
118 * Replaces CLI pattern options with implementation specific pattern options.
119 * @param str the string to clean.
120 * @return the string with CLI names replaced with implementation specific names.
121 */
122 public String cleanup(final String str) {
123 String workingStr = str;
124 if (StringUtils.isNotBlank(workingStr)) {
125 Map<String, String> maps = new HashMap<>();
126 Matcher matcher = PATTERN.matcher(workingStr);
127 while (matcher.find()) {
128 String key = matcher.group();
129 String optKey = (1 == key.indexOf('-', 1)) ? key.substring(2) : key.substring(1);
130 Optional<Option> maybeResult = getOptionCollection().getOptions().getOptions().stream()
131 .filter(o -> optKey.equals(o.getOpt()) || optKey.equals(o.getLongOpt())).findFirst();
132 maybeResult.ifPresent(value -> maps.put(key, cleanupName(value)));
133 }
134 for (Map.Entry<String, String> entry : maps.entrySet()) {
135 workingStr = workingStr.replaceAll(Pattern.quote(format("%s", entry.getKey())), entry.getValue());
136 }
137 }
138 return workingStr;
139 }
140
141 /**
142 * Gets the implementation specific name for the native UI.
143 * @return The implementation specific name for the native UI.
144 */
145 public final String getName() {
146 return name.toString();
147 }
148
149 /**
150 * Gets the CasedString version of the name for the native UI.
151 * @return the CasedString version of the name for the native UI.
152 */
153 public final CasedString getCasedName() {
154 return name;
155 }
156
157 /**
158 * return a string showing long and short options if they are available. Will return
159 * a string.
160 * @return A string showing long and short options if they are available. Never {@code null}.
161 */
162 public abstract String getText();
163
164 /**
165 * Gets the description in implementation specific format.
166 *
167 * @return the description or an empty string.
168 */
169 public final String getDescription() {
170 return cleanup(option.getDescription());
171 }
172
173 /**
174 * Gets the simple class name for the data type for this option.
175 * Normally "String".
176 * @return the simple class name for the type.
177 */
178 public final Class<?> getType() {
179 return option.hasArg() ? ((Class<?>) option.getType()) : boolean.class;
180 }
181
182 /**
183 * Gets the argument name if there is one.
184 * @return the Argument name
185 */
186 public final String getArgName() {
187 return argumentType.getDisplayName();
188 }
189
190 /**
191 * Gets the argument type if there is one.
192 * @return the Argument name
193 */
194 public final OptionCollection.ArgumentType getArgType() {
195 return argumentType;
196 }
197
198 /**
199 * Determines if the option is deprecated.
200 * @return {@code true} if the option is deprecated
201 */
202 public final boolean isDeprecated() {
203 return option.isDeprecated();
204 }
205
206 /**
207 * Determines if the option is required.
208 * @return {@code true} if the option is required.
209 */
210 public final boolean isRequired() {
211 return option.isRequired();
212 }
213
214 /**
215 * Determine if the enclosed option expects an argument.
216 * @return {@code true} if the enclosed option expects at least one argument.
217 */
218 public final boolean hasArg() {
219 return option.hasArg();
220 }
221
222 /**
223 * Returns {@code true} if the option has multiple arguments.
224 * @return {@code true} if the option has multiple arguments.
225 */
226 public final boolean hasArgs() {
227 return option.hasArgs();
228 }
229
230 /**
231 * The key value for the option.
232 * @return the key value for the CLI argument map.
233 */
234 public final String keyValue() {
235 return StringUtils.defaultIfEmpty(option.getLongOpt(), option.getOpt());
236 }
237
238 /**
239 * Gets the deprecated string if the option is deprecated, or an empty string otherwise.
240 * @return the deprecated string if the option is deprecated, or an empty string otherwise.
241 */
242 public final String getDeprecated() {
243 return option.isDeprecated() ? cleanup(StringUtils.defaultIfEmpty(option.getDeprecated().toString(), StringUtils.EMPTY)) : StringUtils.EMPTY;
244 }
245 }