1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.rat.commandline;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.lang.reflect.Array;
26 import java.nio.charset.StandardCharsets;
27 import java.nio.file.Files;
28 import java.util.Arrays;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.function.Predicate;
32
33 import org.apache.commons.cli.AlreadySelectedException;
34 import org.apache.commons.cli.CommandLine;
35 import org.apache.commons.cli.DeprecatedAttributes;
36 import org.apache.commons.cli.Option;
37 import org.apache.commons.cli.OptionGroup;
38 import org.apache.commons.cli.Options;
39 import org.apache.commons.cli.ParseException;
40 import org.apache.commons.io.IOUtils;
41 import org.apache.commons.io.function.IOSupplier;
42 import org.apache.commons.lang3.tuple.Pair;
43 import org.apache.rat.ConfigurationException;
44 import org.apache.rat.Defaults;
45 import org.apache.rat.ReportConfiguration;
46 import org.apache.rat.config.AddLicenseHeaders;
47 import org.apache.rat.config.exclusion.ExclusionUtils;
48 import org.apache.rat.config.exclusion.StandardCollection;
49 import org.apache.rat.document.DocumentName;
50 import org.apache.rat.document.DocumentNameMatcher;
51 import org.apache.rat.license.LicenseSetFactory;
52 import org.apache.rat.report.claim.ClaimStatistic.Counter;
53 import org.apache.rat.utils.DefaultLog;
54 import org.apache.rat.utils.Log;
55
56 import static java.lang.String.format;
57
58
59
60
61
62
63
64 public enum Arg {
65
66
67
68
69
70 EDIT_COPYRIGHT(new OptionGroup()
71 .addOption(Option.builder("c")
72 .longOpt("copyright").hasArg()
73 .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("0.17")
74 .setDescription(StdMsgs.useMsg("--edit-copyright")).get())
75 .desc("The copyright message to use in the license headers.")
76 .build())
77 .addOption(Option.builder().longOpt("edit-copyright").hasArg()
78 .desc("The copyright message to use in the license headers. Usually in the form of \"Copyright 2008 Foo\". "
79 + "Only valid with --edit-license")
80 .build())),
81
82
83
84
85 EDIT_OVERWRITE(new OptionGroup()
86 .addOption(Option.builder("f").longOpt("force")
87 .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("0.17")
88 .setDescription(StdMsgs.useMsg("--edit-overwrite")).get())
89 .desc("Forces any changes in files to be written directly to the source files (i.e. new files are not created).")
90 .build())
91 .addOption(Option.builder().longOpt("edit-overwrite")
92 .desc("Forces any changes in files to be written directly to the source files (i.e. new files are not created). "
93 + "Only valid with --edit-license")
94 .build())),
95
96
97
98
99 EDIT_ADD(new OptionGroup()
100 .addOption(Option.builder("a")
101 .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("0.17")
102 .setDescription(StdMsgs.useMsg("--edit-license")).get())
103 .build())
104 .addOption(Option.builder("A").longOpt("addLicense")
105 .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("0.17")
106 .setDescription(StdMsgs.useMsg("--edit-license")).get())
107 .desc("Add the default license header to any file with an unknown license that is not in the exclusion list.")
108 .build())
109 .addOption(Option.builder().longOpt("edit-license").desc(
110 "Add the default license header to any file with an unknown license that is not in the exclusion list. "
111 + "By default new files will be created with the license header, "
112 + "to force the modification of existing files use the --edit-overwrite option.").build()
113 )),
114
115
116
117
118
119 CONFIGURATION(new OptionGroup()
120 .addOption(Option.builder().longOpt("config").hasArgs().argName("File")
121 .desc("File names for system configuration.")
122 .converter(Converters.FILE_CONVERTER)
123 .type(File.class)
124 .build())
125 .addOption(Option.builder().longOpt("licenses").hasArgs().argName("File")
126 .desc("File names for system configuration.")
127 .deprecated(DeprecatedAttributes.builder().setSince("0.17").setForRemoval(true).setDescription(StdMsgs.useMsg("--config")).get())
128 .converter(Converters.FILE_CONVERTER)
129 .type(File.class)
130 .build())),
131
132
133
134
135 CONFIGURATION_NO_DEFAULTS(new OptionGroup()
136 .addOption(Option.builder().longOpt("configuration-no-defaults")
137 .desc("Ignore default configuration.").build())
138 .addOption(Option.builder().longOpt("no-default-licenses")
139 .deprecated(DeprecatedAttributes.builder()
140 .setSince("0.17")
141 .setForRemoval(true)
142 .setDescription(StdMsgs.useMsg("--configuration-no-defaults")).get())
143 .desc("Ignore default configuration.")
144 .build())),
145
146
147
148
149 LICENSES_APPROVED(new OptionGroup().addOption(Option.builder().longOpt("licenses-approved").hasArgs().argName("LicenseID")
150 .desc("The approved License IDs. These licenses will be added to the list of approved licenses.")
151 .build())),
152
153
154
155
156 LICENSES_APPROVED_FILE(new OptionGroup().addOption(Option.builder().longOpt("licenses-approved-file").hasArg().argName("File")
157 .desc("Name of file containing the approved License IDs.")
158 .converter(Converters.FILE_CONVERTER)
159 .type(File.class)
160 .build())),
161
162
163
164
165 FAMILIES_APPROVED(new OptionGroup().addOption(Option.builder().longOpt("license-families-approved").hasArgs().argName("FamilyID")
166 .desc("The approved License Family IDs. These licenses families will be added to the list of approved licenses families.")
167 .build())),
168
169
170
171
172 FAMILIES_APPROVED_FILE(new OptionGroup().addOption(Option.builder().longOpt("license-families-approved-file").hasArg().argName("File")
173 .desc("Name of file containing the approved family IDs.")
174 .converter(Converters.FILE_CONVERTER)
175 .type(File.class)
176 .build())),
177
178
179
180
181 LICENSES_DENIED(new OptionGroup().addOption(Option.builder().longOpt("licenses-denied").hasArgs().argName("LicenseID")
182 .desc("The denied License IDs. These licenses will be removed from the list of approved licenses. " +
183 "Once licenses are removed they can not be added back.")
184 .build())),
185
186
187
188
189 LICENSES_DENIED_FILE(new OptionGroup().addOption(Option.builder().longOpt("licenses-denied-file")
190 .hasArg().argName("File").type(File.class)
191 .converter(Converters.FILE_CONVERTER)
192 .desc("Name of file containing the denied license IDs.")
193 .build())),
194
195
196
197
198 FAMILIES_DENIED(new OptionGroup().addOption(Option.builder().longOpt("license-families-denied")
199 .hasArgs().argName("FamilyID")
200 .desc("The denied License family IDs. These license families will be removed from the list of approved licenses.")
201 .build())),
202
203
204
205
206 FAMILIES_DENIED_FILE(new OptionGroup().addOption(Option.builder().longOpt("license-families-denied-file").hasArg().argName("File")
207 .desc("Name of file containing the denied license IDs.")
208 .type(File.class)
209 .converter(Converters.FILE_CONVERTER)
210 .build())),
211
212
213
214
215 COUNTER_MAX(new OptionGroup().addOption(Option.builder().longOpt("counter-max").hasArgs().argName("CounterPattern")
216 .desc("The acceptable maximum number for the specified counter. A value of '-1' specifies an unlimited number.")
217 .converter(Converters.COUNTER_CONVERTER)
218 .type(Pair.class)
219 .build())),
220
221
222
223
224 COUNTER_MIN(new OptionGroup().addOption(Option.builder().longOpt("counter-min").hasArgs().argName("CounterPattern")
225 .desc("The minimum number for the specified counter.")
226 .converter(Converters.COUNTER_CONVERTER)
227 .type(Pair.class)
228 .build())),
229
230
231
232
233
234 SOURCE(new OptionGroup()
235 .addOption(Option.builder().longOpt("input-source").hasArgs().argName("File")
236 .desc("A file containing file names to process. " +
237 "File names must use linux directory separator ('/') or none at all. " +
238 "File names that do not start with '/' are relative to the directory where the " +
239 "argument is located.")
240 .converter(Converters.FILE_CONVERTER)
241 .type(File.class)
242 .build())),
243
244
245
246
247 EXCLUDE(new OptionGroup()
248 .addOption(Option.builder("e").longOpt("exclude").hasArgs().argName("Expression")
249 .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("0.17")
250 .setDescription(StdMsgs.useMsg("--input-exclude")).get())
251 .desc("Excludes files matching <Expression>.")
252 .build())
253 .addOption(Option.builder().longOpt("input-exclude").hasArgs().argName("Expression")
254 .desc("Excludes files matching <Expression>.")
255 .build())),
256
257
258
259
260 EXCLUDE_FILE(new OptionGroup()
261 .addOption(Option.builder("E").longOpt("exclude-file")
262 .argName("File").hasArg().type(File.class)
263 .converter(Converters.FILE_CONVERTER)
264 .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("0.17")
265 .setDescription(StdMsgs.useMsg("--input-exclude-file")).get())
266 .desc("Reads <Expression> entries from a file. Entries will be excluded from processing.")
267 .build())
268 .addOption(Option.builder().longOpt("input-exclude-file")
269 .argName("File").hasArg().type(File.class)
270 .converter(Converters.FILE_CONVERTER)
271 .desc("Reads <Expression> entries from a file. Entries will be excluded from processing.")
272 .build())),
273
274
275
276 EXCLUDE_STD(new OptionGroup()
277 .addOption(Option.builder().longOpt("input-exclude-std").argName("StandardCollection")
278 .hasArgs().converter(s -> StandardCollection.valueOf(s.toUpperCase()))
279 .desc("Excludes files defined in standard collections based on commonly occurring groups.")
280 .build())
281 ),
282
283
284
285
286 EXCLUDE_SIZE(new OptionGroup()
287 .addOption(Option.builder().longOpt("input-exclude-size").argName("Integer")
288 .hasArg().type(Integer.class)
289 .desc("Excludes files with sizes less than the given argument.")
290 .build())
291 ),
292
293
294
295 INCLUDE(new OptionGroup()
296 .addOption(Option.builder().longOpt("input-include").hasArgs().argName("Expression")
297 .desc("Includes files matching <Expression>. Will override excluded files.")
298 .build())
299 .addOption(Option.builder().longOpt("include").hasArgs().argName("Expression")
300 .desc("Includes files matching <Expression>. Will override excluded files.")
301 .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("0.17")
302 .setDescription(StdMsgs.useMsg("--input-include")).get())
303 .build())
304 ),
305
306
307
308
309 INCLUDE_FILE(new OptionGroup()
310 .addOption(Option.builder().longOpt("input-include-file")
311 .argName("File").hasArg().type(File.class)
312 .converter(Converters.FILE_CONVERTER)
313 .desc("Reads <Expression> entries from a file. Entries will override excluded files.")
314 .build())
315 .addOption(Option.builder().longOpt("includes-file")
316 .argName("File").hasArg().type(File.class)
317 .converter(Converters.FILE_CONVERTER)
318 .desc("Reads <Expression> entries from a file. Entries will be excluded from processing.")
319 .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("0.17")
320 .setDescription(StdMsgs.useMsg("--input-include-file")).get())
321 .build())),
322
323
324
325
326 INCLUDE_STD(new OptionGroup()
327 .addOption(Option.builder().longOpt("input-include-std").argName("StandardCollection")
328 .hasArgs().converter(s -> StandardCollection.valueOf(s.toUpperCase()))
329 .desc("Includes files defined in standard collections based on commonly occurring groups. " +
330 "Will override excluded files.")
331 .build())
332 .addOption(Option.builder().longOpt("scan-hidden-directories")
333 .desc("Scans hidden directories.")
334 .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("0.17")
335 .setDescription(StdMsgs.useMsg("--input-include-std with 'HIDDEN_DIR' argument")).get()).build()
336 )
337 ),
338
339
340
341
342 EXCLUDE_PARSE_SCM(new OptionGroup()
343 .addOption(Option.builder().longOpt("input-exclude-parsed-scm")
344 .argName("StandardCollection")
345 .hasArgs().converter(s -> StandardCollection.valueOf(s.toUpperCase()))
346 .desc("Parse SCM based exclusion files to exclude specified files and directories.")
347 .type(StandardCollection.class)
348 .build())
349 ),
350
351
352
353
354 DIR(new OptionGroup().addOption(Option.builder().option("d").longOpt("dir").hasArg()
355 .type(File.class)
356 .desc("Used to indicate end of list when using options that take multiple arguments.").argName("DirOrArchive")
357 .deprecated(DeprecatedAttributes.builder().setForRemoval(true).setSince("0.17")
358 .setDescription("Use the standard '--' to signal the end of arguments.").get()).build())),
359
360
361
362
363
364 OUTPUT_STYLE(new OptionGroup()
365 .addOption(Option.builder().longOpt("output-style").hasArg().argName("StyleSheet")
366 .desc("XSLT stylesheet to use when creating the report. "
367 + "Either an external xsl file may be specified or one of the internal named sheets.")
368 .build())
369 .addOption(Option.builder("s").longOpt("stylesheet").hasArg().argName("StyleSheet")
370 .deprecated(DeprecatedAttributes.builder().setSince("0.17").setForRemoval(true).setDescription(StdMsgs.useMsg("--output-style")).get())
371 .desc("XSLT stylesheet to use when creating the report.")
372 .build())
373 .addOption(Option.builder("x").longOpt("xml")
374 .deprecated(DeprecatedAttributes.builder()
375 .setSince("0.17")
376 .setForRemoval(true)
377 .setDescription(StdMsgs.useMsg("--output-style with the 'xml' argument")).get())
378 .desc("forces XML output rather than the textual report.")
379 .build())),
380
381
382
383
384 OUTPUT_LICENSES(new OptionGroup()
385 .addOption(Option.builder().longOpt("output-licenses").hasArg().argName("LicenseFilter")
386 .desc("List the defined licenses.")
387 .converter(s -> LicenseSetFactory.LicenseFilter.valueOf(s.toUpperCase()))
388 .build())
389 .addOption(Option.builder().longOpt("list-licenses").hasArg().argName("LicenseFilter")
390 .desc("List the defined licenses.")
391 .converter(s -> LicenseSetFactory.LicenseFilter.valueOf(s.toUpperCase()))
392 .deprecated(DeprecatedAttributes.builder().setSince("0.17").setForRemoval(true).setDescription(StdMsgs.useMsg("--output-licenses")).get())
393 .build())),
394
395
396
397
398 OUTPUT_FAMILIES(new OptionGroup()
399 .addOption(Option.builder().longOpt("output-families").hasArg().argName("LicenseFilter")
400 .desc("List the defined license families.")
401 .converter(s -> LicenseSetFactory.LicenseFilter.valueOf(s.toUpperCase()))
402 .build())
403 .addOption(Option.builder().longOpt("list-families").hasArg().argName("LicenseFilter")
404 .desc("List the defined license families.")
405 .converter(s -> LicenseSetFactory.LicenseFilter.valueOf(s.toUpperCase()))
406 .deprecated(DeprecatedAttributes.builder().setSince("0.17").setForRemoval(true).setDescription(StdMsgs.useMsg("--output-families")).get())
407 .build())),
408
409
410
411
412 LOG_LEVEL(new OptionGroup().addOption(Option.builder().longOpt("log-level")
413 .hasArg().argName("LogLevel")
414 .desc("Sets the log level.")
415 .converter(s -> Log.Level.valueOf(s.toUpperCase()))
416 .build())),
417
418
419
420
421 DRY_RUN(new OptionGroup().addOption(Option.builder().longOpt("dry-run")
422 .desc("If set do not update the files but generate the reports.")
423 .build())),
424
425
426
427
428 OUTPUT_FILE(new OptionGroup()
429 .addOption(Option.builder().option("o").longOpt("out").hasArg().argName("File")
430 .desc("Define the output file where to write a report to.")
431 .deprecated(DeprecatedAttributes.builder().setSince("0.17").setForRemoval(true).setDescription(StdMsgs.useMsg("--output-file")).get())
432 .type(File.class)
433 .converter(Converters.FILE_CONVERTER)
434 .build())
435 .addOption(Option.builder().longOpt("output-file").hasArg().argName("File")
436 .desc("Define the output file where to write a report to.")
437 .type(File.class)
438 .converter(Converters.FILE_CONVERTER)
439 .build())),
440
441
442
443
444 OUTPUT_ARCHIVE(new OptionGroup()
445 .addOption(Option.builder().longOpt("output-archive").hasArg().argName("ProcessingType")
446 .desc("Specifies the level of detail in ARCHIVE file reporting.")
447 .converter(s -> ReportConfiguration.Processing.valueOf(s.toUpperCase()))
448 .build())),
449
450
451
452
453 OUTPUT_STANDARD(new OptionGroup()
454 .addOption(Option.builder().longOpt("output-standard").hasArg().argName("ProcessingType")
455 .desc("Specifies the level of detail in STANDARD file reporting.")
456 .converter(s -> ReportConfiguration.Processing.valueOf(s.toUpperCase()))
457 .build())),
458
459
460
461
462 HELP_LICENSES(new OptionGroup()
463 .addOption(Option.builder().longOpt("help-licenses")
464 .desc("Print information about registered licenses.").build()));
465
466
467 private final OptionGroup group;
468
469
470
471
472
473
474 Arg(final OptionGroup group) {
475 this.group = group;
476 }
477
478
479
480
481
482
483 public boolean isSelected() {
484 return group.getSelected() != null;
485 }
486
487
488
489
490
491
492 public Option getSelected() {
493 String s = group.getSelected();
494 if (s != null) {
495 for (Option result : group.getOptions()) {
496 if (result.getKey().equals(s)) {
497 return result;
498 }
499 }
500 }
501 return null;
502 }
503
504
505
506
507
508
509
510
511 public Option find(final String key) {
512 for (Option result : group.getOptions()) {
513 if (key.equals(result.getKey()) || key.equals(result.getLongOpt())) {
514 return result;
515 }
516 }
517 throw new IllegalArgumentException("Can not find " + key);
518 }
519
520
521
522
523
524 public String defaultValue() {
525 return DEFAULT_VALUES.get(this);
526 }
527
528
529
530
531
532
533 public OptionGroup group() {
534 return group;
535 }
536
537
538
539
540
541
542 public Option option() {
543 for (Option result : group.getOptions()) {
544 if (!result.isDeprecated()) {
545 return result;
546 }
547 }
548 return null;
549 }
550
551
552
553
554
555 public static Options getOptions() {
556 Options options = new Options();
557 for (Arg arg : Arg.values()) {
558 options.addOptionGroup(arg.group);
559 }
560 return options;
561 }
562
563
564
565
566
567
568 private static void processEditArgs(final ArgumentContext context) {
569 if (EDIT_ADD.isSelected()) {
570 context.getCommandLine().hasOption(Arg.EDIT_ADD.getSelected());
571 boolean force = EDIT_OVERWRITE.isSelected();
572 if (force) {
573 context.getCommandLine().hasOption(EDIT_OVERWRITE.getSelected());
574 }
575 context.getConfiguration().setAddLicenseHeaders(force ? AddLicenseHeaders.FORCED : AddLicenseHeaders.TRUE);
576 if (EDIT_COPYRIGHT.isSelected()) {
577 context.getConfiguration().setCopyrightMessage(context.getCommandLine().getOptionValue(EDIT_COPYRIGHT.getSelected()));
578 }
579 }
580 }
581
582
583
584
585
586
587
588 private static void processConfigurationArgs(final ArgumentContext context) throws ConfigurationException {
589 try {
590 Defaults.Builder defaultBuilder = Defaults.builder();
591 if (CONFIGURATION.isSelected()) {
592 File[] files = CONFIGURATION.getParsedOptionValues(context.getCommandLine());
593 for (File file : files) {
594 defaultBuilder.add(file);
595 }
596 }
597 if (CONFIGURATION_NO_DEFAULTS.isSelected()) {
598
599 context.getCommandLine().hasOption(CONFIGURATION.getSelected());
600 defaultBuilder.noDefault();
601 }
602 context.getConfiguration().setFrom(defaultBuilder.build());
603
604 if (FAMILIES_APPROVED.isSelected()) {
605 for (String cat : context.getCommandLine().getOptionValues(FAMILIES_APPROVED.getSelected())) {
606 context.getConfiguration().addApprovedLicenseCategory(cat);
607 }
608 }
609 if (FAMILIES_APPROVED_FILE.isSelected()) {
610 try {
611 File f = context.getCommandLine().getParsedOptionValue(FAMILIES_APPROVED_FILE.getSelected());
612 try (InputStream in = Files.newInputStream(f.toPath())) {
613 context.getConfiguration().addApprovedLicenseCategories(IOUtils.readLines(in, StandardCharsets.UTF_8));
614 }
615 } catch (IOException e) {
616 throw new ConfigurationException(e);
617 }
618 }
619 if (FAMILIES_DENIED.isSelected()) {
620 for (String cat : context.getCommandLine().getOptionValues(FAMILIES_DENIED.getSelected())) {
621 context.getConfiguration().removeApprovedLicenseCategory(cat);
622 }
623 }
624 if (FAMILIES_DENIED_FILE.isSelected()) {
625 try {
626 File f = context.getCommandLine().getParsedOptionValue(FAMILIES_DENIED_FILE.getSelected());
627 try (InputStream in = Files.newInputStream(f.toPath())) {
628 context.getConfiguration().removeApprovedLicenseCategories(IOUtils.readLines(in, StandardCharsets.UTF_8));
629 }
630 } catch (IOException e) {
631 throw new ConfigurationException(e);
632 }
633 }
634
635 if (LICENSES_APPROVED.isSelected()) {
636 for (String id : context.getCommandLine().getOptionValues(LICENSES_APPROVED.getSelected())) {
637 context.getConfiguration().addApprovedLicenseId(id);
638 }
639 }
640 if (LICENSES_APPROVED_FILE.isSelected()) {
641 try {
642 File file = context.getCommandLine().getParsedOptionValue(LICENSES_APPROVED_FILE.getSelected());
643 try (InputStream in = Files.newInputStream(file.toPath())) {
644 context.getConfiguration().addApprovedLicenseIds(IOUtils.readLines(in, StandardCharsets.UTF_8));
645 }
646 } catch (IOException e) {
647 throw new ConfigurationException(e);
648 }
649 }
650 if (LICENSES_DENIED.isSelected()) {
651 for (String id : context.getCommandLine().getOptionValues(LICENSES_DENIED.getSelected())) {
652 context.getConfiguration().removeApprovedLicenseId(id);
653 }
654 }
655 if (LICENSES_DENIED_FILE.isSelected()) {
656 try {
657 File file = context.getCommandLine().getParsedOptionValue(LICENSES_DENIED_FILE.getSelected());
658 try (InputStream in = Files.newInputStream(file.toPath())) {
659 context.getConfiguration().removeApprovedLicenseIds(IOUtils.readLines(in, StandardCharsets.UTF_8));
660 }
661 } catch (IOException e) {
662 throw new ConfigurationException(e);
663 }
664 }
665 if (COUNTER_MAX.isSelected()) {
666 for (String arg : context.getCommandLine().getOptionValues(COUNTER_MAX.getSelected())) {
667 Pair<Counter, Integer> pair = Converters.COUNTER_CONVERTER.apply(arg);
668 int limit = pair.getValue();
669 context.getConfiguration().getClaimValidator().setMax(pair.getKey(), limit < 0 ? Integer.MAX_VALUE : limit);
670 }
671 }
672 if (COUNTER_MIN.isSelected()) {
673 for (String arg : context.getCommandLine().getOptionValues(COUNTER_MIN.getSelected())) {
674 Pair<Counter, Integer> pair = Converters.COUNTER_CONVERTER.apply(arg);
675 context.getConfiguration().getClaimValidator().setMin(pair.getKey(), pair.getValue());
676 }
677 }
678 } catch (Exception e) {
679 throw ConfigurationException.from(e);
680 }
681 }
682
683
684
685
686
687
688
689 private static void processInputArgs(final ArgumentContext context) throws ConfigurationException {
690 try {
691 if (SOURCE.isSelected()) {
692 File[] files = SOURCE.getParsedOptionValues(context.getCommandLine());
693 for (File f : files) {
694 context.getConfiguration().addSource(f);
695 }
696 }
697
698
699 if (EXCLUDE.isSelected()) {
700 String[] excludes = context.getCommandLine().getOptionValues(EXCLUDE.getSelected());
701 if (excludes != null) {
702 context.getConfiguration().addExcludedPatterns(Arrays.asList(excludes));
703 }
704 }
705 if (EXCLUDE_FILE.isSelected()) {
706 File excludeFileName = context.getCommandLine().getParsedOptionValue(EXCLUDE_FILE.getSelected());
707 if (excludeFileName != null) {
708 context.getConfiguration().addExcludedPatterns(ExclusionUtils.asIterable(excludeFileName, "#"));
709 }
710 }
711 if (EXCLUDE_STD.isSelected()) {
712 for (String s : context.getCommandLine().getOptionValues(EXCLUDE_STD.getSelected())) {
713 context.getConfiguration().addExcludedCollection(StandardCollection.valueOf(s));
714 }
715 }
716 if (EXCLUDE_PARSE_SCM.isSelected()) {
717 StandardCollection[] collections = EXCLUDE_PARSE_SCM.getParsedOptionValues(context.getCommandLine());
718 final ReportConfiguration configuration = context.getConfiguration();
719 for (StandardCollection collection : collections) {
720 if (collection == StandardCollection.ALL) {
721 Arrays.asList(StandardCollection.values()).forEach(configuration::addExcludedFileProcessor);
722 Arrays.asList(StandardCollection.values()).forEach(configuration::addExcludedCollection);
723 } else {
724 configuration.addExcludedFileProcessor(collection);
725 configuration.addExcludedCollection(collection);
726 }
727 }
728 }
729 if (EXCLUDE_SIZE.isSelected()) {
730 final int maxSize = EXCLUDE_SIZE.getParsedOptionValue(context.getCommandLine());
731 DocumentNameMatcher matcher = new DocumentNameMatcher(String.format("File size < %s bytes", maxSize),
732 (Predicate<DocumentName>) documentName -> {
733 File f = new File(documentName.getName());
734 return f.isFile() && f.length() < maxSize;
735 });
736 context.getConfiguration().addExcludedMatcher(matcher);
737 }
738 if (INCLUDE.isSelected()) {
739 String[] includes = context.getCommandLine().getOptionValues(INCLUDE.getSelected());
740 if (includes != null) {
741 context.getConfiguration().addIncludedPatterns(Arrays.asList(includes));
742 }
743 }
744 if (INCLUDE_FILE.isSelected()) {
745 File includeFileName = context.getCommandLine().getParsedOptionValue(INCLUDE_FILE.getSelected());
746 if (includeFileName != null) {
747 context.getConfiguration().addIncludedPatterns(ExclusionUtils.asIterable(includeFileName, "#"));
748 }
749 }
750 if (INCLUDE_STD.isSelected()) {
751 Option selected = INCLUDE_STD.getSelected();
752 if ("scan-hidden-directories".equals(selected.getLongOpt())) {
753 context.getConfiguration().addIncludedCollection(StandardCollection.HIDDEN_DIR);
754 } else {
755 for (String s : context.getCommandLine().getOptionValues(selected)) {
756 context.getConfiguration().addIncludedCollection(StandardCollection.valueOf(s));
757 }
758 }
759 }
760 } catch (Exception e) {
761 throw ConfigurationException.from(e);
762 }
763 }
764
765
766
767
768
769
770
771
772
773
774 private static void logParseException(final Log log, final ParseException exception, final Option opt, final CommandLine cl, final Object defaultValue) {
775 log.warn(format("Invalid %s specified: %s ", opt.getOpt(), cl.getOptionValue(opt)));
776 log.warn(format("%s set to: %s", opt.getOpt(), defaultValue));
777 log.debug(exception);
778 }
779
780
781
782
783
784
785 public static void processLogLevel(final CommandLine commandLine) {
786 if (LOG_LEVEL.getSelected() != null) {
787 Log dLog = DefaultLog.getInstance();
788 try {
789 dLog.setLevel(commandLine.getParsedOptionValue(LOG_LEVEL.getSelected()));
790 } catch (ParseException e) {
791 logParseException(DefaultLog.getInstance(), e, LOG_LEVEL.getSelected(), commandLine, dLog.getLevel());
792 }
793 }
794 }
795
796
797
798
799
800
801
802 public static void processArgs(final ArgumentContext context) throws ConfigurationException {
803 Converters.FILE_CONVERTER.setWorkingDirectory(context.getWorkingDirectory());
804 processOutputArgs(context);
805 processEditArgs(context);
806 processInputArgs(context);
807 processConfigurationArgs(context);
808 }
809
810
811
812
813
814
815 private static void processOutputArgs(final ArgumentContext context) {
816 context.getConfiguration().setDryRun(DRY_RUN.isSelected());
817
818 if (OUTPUT_FAMILIES.isSelected()) {
819 try {
820 context.getConfiguration().listFamilies(context.getCommandLine().getParsedOptionValue(OUTPUT_FAMILIES.getSelected()));
821 } catch (ParseException e) {
822 context.logParseException(e, OUTPUT_FAMILIES.getSelected(), Defaults.LIST_FAMILIES);
823 }
824 }
825
826 if (OUTPUT_LICENSES.isSelected()) {
827 try {
828 context.getConfiguration().listLicenses(context.getCommandLine().getParsedOptionValue(OUTPUT_LICENSES.getSelected()));
829 } catch (ParseException e) {
830 context.logParseException(e, OUTPUT_LICENSES.getSelected(), Defaults.LIST_LICENSES);
831 }
832 }
833
834 if (OUTPUT_ARCHIVE.isSelected()) {
835 try {
836 context.getConfiguration().setArchiveProcessing(context.getCommandLine().getParsedOptionValue(OUTPUT_ARCHIVE.getSelected()));
837 } catch (ParseException e) {
838 context.logParseException(e, OUTPUT_ARCHIVE.getSelected(), Defaults.ARCHIVE_PROCESSING);
839 }
840 }
841
842 if (OUTPUT_STANDARD.isSelected()) {
843 try {
844 context.getConfiguration().setStandardProcessing(context.getCommandLine().getParsedOptionValue(OUTPUT_STANDARD.getSelected()));
845 } catch (ParseException e) {
846 context.logParseException(e, OUTPUT_STANDARD.getSelected(), Defaults.STANDARD_PROCESSING);
847 }
848 }
849
850 if (OUTPUT_FILE.isSelected()) {
851 try {
852 File file = context.getCommandLine().getParsedOptionValue(OUTPUT_FILE.getSelected());
853 File parent = file.getParentFile();
854 if (!parent.mkdirs() && !parent.isDirectory()) {
855 DefaultLog.getInstance().error("Could not create report parent directory " + file);
856 }
857 context.getConfiguration().setOut(file);
858 } catch (ParseException e) {
859 context.logParseException(e, OUTPUT_FILE.getSelected(), "System.out");
860 context.getConfiguration().setOut((IOSupplier<OutputStream>) null);
861 }
862 }
863
864 if (OUTPUT_STYLE.isSelected()) {
865 String selected = OUTPUT_STYLE.getSelected().getKey();
866 if ("x".equals(selected)) {
867
868 context.getCommandLine().hasOption("x");
869 context.getConfiguration().setStyleSheet(StyleSheets.getStyleSheet("xml"));
870 } else {
871 String[] style = context.getCommandLine().getOptionValues(OUTPUT_STYLE.getSelected());
872 if (style.length != 1) {
873 DefaultLog.getInstance().error("Please specify a single stylesheet");
874 throw new ConfigurationException("Please specify a single stylesheet");
875 }
876 context.getConfiguration().setStyleSheet(StyleSheets.getStyleSheet(style[0]));
877 }
878 }
879 }
880
881
882
883
884 public static void reset() {
885 for (Arg a : Arg.values()) {
886 try {
887 a.group.setSelected(null);
888 } catch (AlreadySelectedException e) {
889 throw new RuntimeException("Should not happen", e);
890 }
891 }
892 }
893
894
895
896
897
898
899
900 public static Arg findArg(final Option optionToFind) {
901 if (optionToFind != null) {
902 for (Arg arg : Arg.values()) {
903 for (Option candidate : arg.group.getOptions()) {
904 if (optionToFind.equals(candidate)) {
905 return arg;
906 }
907 }
908 }
909 }
910 return null;
911 }
912
913
914
915
916
917
918 public static Arg findArg(final String key) {
919 if (key != null) {
920 for (Arg arg : Arg.values()) {
921 for (Option candidate : arg.group.getOptions()) {
922 if (key.equals(candidate.getKey()) || key.equals(candidate.getLongOpt())) {
923 return arg;
924 }
925 }
926 }
927 }
928 return null;
929 }
930
931 private <T> T getParsedOptionValue(final CommandLine commandLine) throws ParseException {
932 return commandLine.getParsedOptionValue(this.getSelected());
933 }
934
935 private String getOptionValue(final CommandLine commandLine) {
936 return commandLine.getOptionValue(this.getSelected());
937 }
938
939 private String[] getOptionValues(final CommandLine commandLine) {
940 return commandLine.getOptionValues(this.getSelected());
941 }
942
943 private <T> T[] getParsedOptionValues(final CommandLine commandLine) {
944 Option option = getSelected();
945 Class<? extends T> clazz = (Class<? extends T>) option.getType();
946 String[] values = getOptionValues(commandLine);
947 T[] result = (T[]) Array.newInstance(clazz, values.length);
948 try {
949 for (int i = 0; i < values.length; i++) {
950 result[i] = clazz.cast(option.getConverter().apply(values[i]));
951 }
952 return result;
953 } catch (Throwable t) {
954 throw new ConfigurationException(format("'%s' converter for %s '%s' does not produce a class of type %s", this,
955 option.getKey(), option.getConverter().getClass().getName(), option.getType()));
956 }
957 }
958
959
960
961
962 public static final class StdMsgs {
963 private StdMsgs() {
964
965 }
966
967
968
969
970
971
972
973 public static String useMsg(final String name) {
974 return format("Use %s instead.", name);
975 }
976 }
977
978
979
980
981 private static final Map<Arg, String> DEFAULT_VALUES = new HashMap<>();
982
983 static {
984 DEFAULT_VALUES.put(OUTPUT_FILE, "System.out");
985 DEFAULT_VALUES.put(LOG_LEVEL, Log.Level.WARN.name());
986 DEFAULT_VALUES.put(OUTPUT_ARCHIVE, Defaults.ARCHIVE_PROCESSING.name());
987 DEFAULT_VALUES.put(OUTPUT_STANDARD, Defaults.STANDARD_PROCESSING.name());
988 DEFAULT_VALUES.put(OUTPUT_LICENSES, Defaults.LIST_LICENSES.name());
989 DEFAULT_VALUES.put(OUTPUT_FAMILIES, Defaults.LIST_FAMILIES.name());
990 }
991 }