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