1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.rat.documentation.options;
20
21 import java.io.File;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Map;
29 import java.util.Objects;
30 import java.util.Set;
31 import java.util.function.Predicate;
32 import java.util.function.Supplier;
33 import java.util.stream.Collectors;
34
35 import org.apache.commons.cli.Option;
36 import org.apache.commons.lang3.StringUtils;
37 import org.apache.commons.text.StringEscapeUtils;
38 import org.apache.commons.text.WordUtils;
39 import org.apache.rat.OptionCollection;
40 import org.apache.rat.commandline.Arg;
41 import org.apache.rat.utils.CasedString;
42
43 import static java.lang.String.format;
44
45
46
47
48 public class AntOption extends AbstractOption {
49
50
51
52
53 private static final Predicate<Option> ANT_FILTER;
54
55
56
57
58 private static final List<Option> UNSUPPORTED_LIST = new ArrayList<>();
59
60
61
62
63 private static final Map<Option, Option> ANT_CONVERSION_MAP = new HashMap<>();
64
65
66
67
68 private static final Map<OptionCollection.ArgumentType, BuildType> BUILD_TYPE_MAP = new HashMap<>();
69
70
71 private static final List<Class<?>> ATTRIBUTE_TYPES = new ArrayList<>();
72
73 private static final Map<String, String> RENAME_MAP = new HashMap<>();
74
75
76 private static final Map<String, Map<String, String>> REQUIRED_ATTRIBUTES = new HashMap<>();
77
78
79
80
81
82
83 private static void updateConversionMap(final Arg arg, final Arg actualArg) {
84 Option mapTo = actualArg.option();
85 for (Option option : arg.group().getOptions()) {
86 if (!option.equals(mapTo) && !option.isDeprecated()) {
87 ANT_CONVERSION_MAP.put(option, mapTo);
88 }
89 }
90 }
91
92 static {
93 RENAME_MAP.put("addLicense", "add-license");
94 ATTRIBUTE_TYPES.add(String.class);
95 ATTRIBUTE_TYPES.add(String[].class);
96 ATTRIBUTE_TYPES.add(Integer.class);
97 ATTRIBUTE_TYPES.add(Long.class);
98 ATTRIBUTE_TYPES.add(File.class);
99 Arg.getOptions().getOptions().stream().filter(o -> Objects.isNull(o.getLongOpt())).forEach(UNSUPPORTED_LIST::add);
100 UNSUPPORTED_LIST.addAll(Arg.LOG_LEVEL.group().getOptions());
101 UNSUPPORTED_LIST.addAll(Arg.DIR.group().getOptions());
102 UNSUPPORTED_LIST.add(OptionCollection.HELP);
103 UNSUPPORTED_LIST.addAll(Arg.SOURCE.group().getOptions());
104 updateConversionMap(Arg.LICENSES_APPROVED_FILE, Arg.LICENSES_APPROVED);
105 updateConversionMap(Arg.LICENSES_DENIED_FILE, Arg.LICENSES_DENIED);
106 updateConversionMap(Arg.FAMILIES_APPROVED_FILE, Arg.FAMILIES_APPROVED);
107 updateConversionMap(Arg.FAMILIES_DENIED_FILE, Arg.FAMILIES_DENIED);
108 updateConversionMap(Arg.INCLUDE_FILE, Arg.INCLUDE);
109 updateConversionMap(Arg.INCLUDE_STD, Arg.INCLUDE);
110 updateConversionMap(Arg.EXCLUDE_FILE, Arg.EXCLUDE);
111 updateConversionMap(Arg.EXCLUDE_STD, Arg.EXCLUDE);
112
113
114
115
116 BuildType buildType;
117 for (OptionCollection.ArgumentType type : OptionCollection.ArgumentType.values()) {
118 switch (type) {
119 case FILE:
120 case DIRORARCHIVE:
121 buildType = new BuildType(type, "filename") {
122 @Override
123 protected String getMultipleFormat(final AntOption antOption) {
124 return " <fileset file='%s' />\n";
125 }
126 };
127 BUILD_TYPE_MAP.put(type, buildType);
128 break;
129 case NONE:
130 buildType = new BuildType(type, "");
131 BUILD_TYPE_MAP.put(type, buildType);
132 break;
133 case COUNTERPATTERN:
134 buildType = new BuildType(type, "cntr");
135 BUILD_TYPE_MAP.put(type, buildType);
136 break;
137 case EXPRESSION:
138 buildType = new BuildType(type, "expr");
139 BUILD_TYPE_MAP.put(type, buildType);
140 break;
141 case STANDARDCOLLECTION:
142 buildType = new BuildType(type, "std");
143 BUILD_TYPE_MAP.put(type, buildType);
144 break;
145 case LICENSEID:
146 case FAMILYID:
147 buildType = new BuildType(type, "lst");
148 BUILD_TYPE_MAP.put(type, buildType);
149 break;
150 default:
151 buildType = new BuildType(type, type.getDisplayName()) {
152 protected String getMethodFormat(final AntOption antOption) {
153 return String.format("<%1$s>%%s</%1$s>\n", WordUtils.uncapitalize(antOption.getArgName()));
154 }
155 };
156 BUILD_TYPE_MAP.put(type, buildType);
157 }
158 }
159 Set<Option> filteredOptions = getFilteredOptions();
160 ANT_FILTER = option -> !(filteredOptions.contains(option) || option.getLongOpt() == null);
161
162 Map<String, String> attributes = new HashMap<>();
163 attributes.put("editLicense", "true");
164 REQUIRED_ATTRIBUTES.put("copyright", attributes);
165 REQUIRED_ATTRIBUTES.put("editCopyright", attributes);
166 REQUIRED_ATTRIBUTES.put("force", attributes);
167 REQUIRED_ATTRIBUTES.put("editOverwrite", attributes);
168 }
169
170
171
172
173
174 public static List<AntOption> getAntOptions() {
175 return Arg.getOptions().getOptions().stream().filter(ANT_FILTER).map(AntOption::new)
176 .collect(Collectors.toList());
177 }
178
179
180
181
182 public static List<Option> getUnsupportedOptions() {
183 return Collections.unmodifiableList(UNSUPPORTED_LIST);
184 }
185
186
187
188
189
190
191 public static Set<Option> getFilteredOptions() {
192 HashSet<Option> filteredOptions = new HashSet<>(UNSUPPORTED_LIST);
193 filteredOptions.addAll(ANT_CONVERSION_MAP.keySet());
194 return filteredOptions;
195 }
196
197 public static Map<String, String> getRenameMap() {
198 return Collections.unmodifiableMap(RENAME_MAP);
199 }
200
201
202
203
204
205
206 public AntOption(final Option option) {
207 super(option, createName(option));
208 }
209
210 public static String createName(final Option option) {
211 String name = option.getLongOpt();
212 name = StringUtils.defaultIfEmpty(RENAME_MAP.get(name), name).toLowerCase(Locale.ROOT);
213 return new CasedString(CasedString.StringCase.KEBAB, name).toCase(CasedString.StringCase.CAMEL);
214 }
215
216
217
218
219
220
221 public boolean isAttribute() {
222 return (!option.hasArg() || option.getArgs() == 1) && convertedFrom().isEmpty()
223 && ATTRIBUTE_TYPES.contains(option.getType());
224 }
225
226
227
228
229
230
231 public boolean isElement() {
232 return !isAttribute();
233 }
234
235
236
237
238
239
240 public AntOption getActualAntOption() {
241 Option opt = ANT_CONVERSION_MAP.get(this.option);
242 return opt == null ? this : new AntOption(opt);
243 }
244
245
246
247
248
249 public Set<Option> convertedFrom() {
250 return ANT_CONVERSION_MAP.entrySet().stream().filter(e -> e.getValue().equals(option))
251 .map(Map.Entry::getKey)
252 .collect(Collectors.toSet());
253 }
254
255 @Override
256 public String getText() {
257 return cleanupName(option);
258 }
259
260 protected String cleanupName(final Option option) {
261 AntOption antOption = new AntOption(option);
262 String fmt = antOption.isAttribute() ? "%s attribute" : "<%s>";
263 return format(fmt, createName(option));
264 }
265
266
267
268
269
270
271
272 public String getComment(final boolean addParam) {
273 StringBuilder sb = new StringBuilder();
274 String desc = getDescription();
275 if (desc == null) {
276 throw new IllegalStateException(format("Description for %s may not be null", getName()));
277 }
278 if (!desc.contains(".")) {
279 throw new IllegalStateException(format("First sentence of description for %s must end with a '.'", getName()));
280 }
281 if (addParam) {
282 String arg;
283 if (option.hasArg()) {
284 arg = desc.substring(desc.indexOf(" ") + 1, desc.indexOf(".") + 1);
285 arg = WordUtils.capitalize(arg.substring(0, 1)) + arg.substring(1);
286 } else {
287 arg = "The state";
288 }
289 if (option.getArgName() != null) {
290 Supplier<String> sup = OptionCollection.getArgumentTypes().get(option.getArgName());
291 if (sup == null) {
292 throw new IllegalStateException(format("Argument type %s must be in OptionCollection.ARGUMENT_TYPES", option.getArgName()));
293 }
294 desc = format("%s Argument%s should be %s%s. (See Argument Types for clarification)", desc, option.hasArgs() ? "s" : "",
295 option.hasArgs() ? "" : "a ", option.getArgName());
296 }
297 sb.append(format(" /**%n * %s%n * @param %s %s%n", StringEscapeUtils.escapeHtml4(desc), getName(),
298 StringEscapeUtils.escapeHtml4(arg)));
299 } else {
300 sb.append(format(" /**%n * %s%n", StringEscapeUtils.escapeHtml4(desc)));
301 }
302 if (option.isDeprecated()) {
303 sb.append(format(" * @deprecated %s%n", StringEscapeUtils.escapeHtml4(getDeprecated())));
304 }
305 return sb.append(format(" */%n")).toString();
306 }
307
308
309
310
311
312
313 public String getAttributeFunctionName() {
314 return "set" +
315 WordUtils.capitalize(name) +
316 (option.hasArg() ? "(String " : "(boolean ") +
317 name +
318 ")";
319 }
320
321 @Override
322 public String getExample() {
323 return new ExampleGenerator().getExample();
324 }
325
326
327
328
329 private static class BuildType {
330
331 private final OptionCollection.ArgumentType type;
332
333 private final String tag;
334
335
336
337
338
339
340 BuildType(final OptionCollection.ArgumentType type, final String tag) {
341 this.type = type;
342 this.tag = tag;
343 }
344
345
346
347
348
349
350 protected String getMultipleFormat(final AntOption antOption) {
351 return String.format("<%1$s>%%s</%1$s>\n", tag);
352 }
353
354
355
356
357
358
359 protected String getMethodFormat(final AntOption antOption) {
360 return antOption.hasArgs() ? getMultipleFormat(antOption) : String.format("<%1$s>%%s</%1$s>\n", tag);
361 }
362
363
364
365
366
367
368
369
370 public String getPattern(final AntOption delegateOption, final AntOption antOption, final String data) {
371 String fmt = getMethodFormat(antOption);
372 String value = data == null ? WordUtils.uncapitalize(antOption.getArgName()) : data;
373 String inner = format(fmt, value);
374 return format("<%1$s>%2$s</%1$s>%n", delegateOption.getName(), inner);
375 }
376 }
377
378
379
380
381 public class ExampleGenerator {
382
383
384
385
386 public ExampleGenerator() {
387 }
388
389
390
391
392
393 String getExample() {
394 return getExample("data", REQUIRED_ATTRIBUTES.get(getName()), null);
395 }
396
397
398
399
400
401
402
403
404 public String getExample(final String data, final Map<String, String> attributes, final List<String> childElements) {
405 if (UNSUPPORTED_LIST.contains(option)) {
406 return "-- not supported --";
407 }
408 return "<rat:report" +
409 getExampleAttributes(data, attributes) +
410 "> \n" +
411 getChildElements(data, childElements) +
412 "</rat:report>\n";
413 }
414
415
416
417
418
419
420
421 public String getExampleAttributes(final String data, final Map<String, String> attributes) {
422 AntOption actualOption = getActualAntOption();
423 StringBuilder result = new StringBuilder();
424 if (attributes != null) {
425 attributes.forEach((k, v) -> result.append(format(" %s=\"%s\"", k, v)));
426 }
427 if (actualOption.isAttribute()) {
428 result.append(format(" %s=\"%s\"", actualOption.getName(), actualOption.hasArg() ? data : "true"));
429 }
430 return result.toString();
431 }
432
433
434
435
436
437
438
439 public String getChildElements(final String data, final List<String> childElements) {
440 AntOption baseOption = AntOption.this;
441 AntOption actualOption = getActualAntOption();
442 StringBuilder result = new StringBuilder();
443 if (!actualOption.isAttribute()) {
444 String inner = BUILD_TYPE_MAP.get(getArgType()).getPattern(actualOption, baseOption, data);
445 result.append(inner);
446 }
447 if (childElements != null) {
448 childElements.forEach(x -> result.append(x).append("\n"));
449 }
450 return result.toString();
451 }
452 }
453 }