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   *   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;
20  
21  import java.io.File;
22  import java.io.FileFilter;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  import java.io.OutputStreamWriter;
28  import java.io.PrintWriter;
29  import java.net.MalformedURLException;
30  import java.net.URI;
31  import java.net.URL;
32  import java.nio.charset.StandardCharsets;
33  import java.nio.file.Files;
34  import java.util.ArrayList;
35  import java.util.Collection;
36  import java.util.List;
37  import java.util.Objects;
38  import java.util.SortedSet;
39  import java.util.function.Consumer;
40  
41  import org.apache.commons.io.function.IOSupplier;
42  import org.apache.rat.analysis.IHeaderMatcher;
43  import org.apache.rat.commandline.StyleSheets;
44  import org.apache.rat.config.AddLicenseHeaders;
45  import org.apache.rat.config.exclusion.ExclusionProcessor;
46  import org.apache.rat.config.exclusion.StandardCollection;
47  import org.apache.rat.config.results.ClaimValidator;
48  import org.apache.rat.configuration.builders.AnyBuilder;
49  import org.apache.rat.document.DocumentName;
50  import org.apache.rat.document.DocumentNameMatcher;
51  import org.apache.rat.document.FileDocument;
52  import org.apache.rat.license.ILicense;
53  import org.apache.rat.license.ILicenseFamily;
54  import org.apache.rat.license.LicenseSetFactory;
55  import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
56  import org.apache.rat.report.IReportable;
57  import org.apache.rat.utils.DefaultLog;
58  import org.apache.rat.utils.Log.Level;
59  import org.apache.rat.utils.ReportingSet;
60  import org.apache.rat.walker.FileListWalker;
61  import org.apache.rat.walker.IReportableListWalker;
62  
63  /**
64   * A configuration object is used by the front end to invoke the
65   * {@link Reporter}. The sole purpose of the frontends is to create the
66   * configuration and invoke the {@link Reporter}.
67   */
68  public class ReportConfiguration {
69  
70      /**
71       * The styles of processing for various categories of documents.
72       */
73      public enum Processing {
74          /** List file as present only */
75          NOTIFICATION("List file as present"),
76          /** List all present licenses */
77          PRESENCE("List any licenses found"),
78          /** List all present licenses and unknown licenses */
79          ABSENCE("List licenses found and any unknown licences");
80  
81          /**
82           * Description of the processing
83           */
84          private final String description;
85  
86  
87          Processing(final String description) {
88              this.description = description;
89          }
90  
91          /**
92           * Gets the description of the processing type.
93           * @return the description of the processing type.
94           */
95          public String desc() {
96              return description;
97          }
98      }
99  
100     /** The LicenseSetFactory for the configuration */
101     private final LicenseSetFactory licenseSetFactory;
102 
103     /**
104      * {@code true} if we are adding license headers to the files.
105      */
106     private boolean addingLicenses;
107     /**
108      * {@code true} if we are adding license headers in place (no *.new files)
109      */
110     private boolean addingLicensesForced;
111     /**
112      * The copyright message to add if we are adding headers. Will be null if we are not
113      * adding copyright messages.
114      */
115     private String copyrightMessage;
116     /**
117      * The IOSupplier that provides the output stream to write the report to.
118      */
119     private IOSupplier<OutputStream> out;
120     /**
121      * The IOSupplier that provides the stylesheet to style the XML output.
122      */
123     private IOSupplier<InputStream> styleSheet;
124 
125     /**
126      * A list of files to read file names from.
127      */
128     private final List<File> sources;
129 
130     /**
131      * A list of reportables to process;
132      */
133     private final List<IReportable> reportables;
134 
135     /**
136      * A predicate to test if a path should be included in the processing.
137      */
138     private final ExclusionProcessor exclusionProcessor;
139 
140     /**
141      * The default filter for displaying families.
142      */
143     private LicenseFilter listFamilies;
144     /**
145      * The default filter for displaying licenses.
146      */
147     private LicenseFilter listLicenses;
148     /**
149      * {@code true} if this is a dry run and no processing is to take place.
150      */
151     private boolean dryRun;
152     /**
153      * How to process ARCHIVE document types.
154      */
155     private Processing archiveProcessing;
156     /**
157      * How to process STANDARD document types.
158      */
159     private Processing standardProcessing;
160     /**
161      * The ClaimValidator to validate min/max counts and similar claims.
162      */
163     private final ClaimValidator claimValidator;
164     /**
165      * Constructor
166      */
167     public ReportConfiguration() {
168         licenseSetFactory = new LicenseSetFactory();
169         listFamilies = Defaults.LIST_FAMILIES;
170         listLicenses = Defaults.LIST_LICENSES;
171         dryRun = false;
172         exclusionProcessor = new ExclusionProcessor();
173         claimValidator = new ClaimValidator();
174         sources = new ArrayList<>();
175         reportables = new ArrayList<>();
176     }
177 
178     /**
179      * Report the excluded files to the appendable object.
180      * @param appendable the appendable object to write to.
181      */
182     public void reportExclusions(final Appendable appendable) {
183         try {
184             exclusionProcessor.reportExclusions(appendable);
185         } catch (IOException e) {
186             DefaultLog.getInstance().warn("Unable to report exclusions", e);
187         }
188     }
189 
190     /**
191      * Adds a file as a source of files to scan.
192      * The file must be a text file that lists files to be included.
193      * File within the file must be in linux format with a
194      * "/" file separator.
195      * @param file the file to process.
196      */
197     public void addSource(final File file) {
198         notNull(file, "File may not be null.");
199         sources.add(file);
200     }
201 
202     private void notNull(final Object o, final String msg) {
203         if (o == null) {
204             throw new ConfigurationException(msg);
205         }
206     }
207 
208     /**
209      * Adds a Reportable as a source of files to scan.
210      * @param reportable the reportable to process.
211      */
212     public void addSource(final IReportable reportable) {
213         notNull(reportable, "Reportable may not be null.");
214         reportables.add(reportable);
215     }
216 
217     /**
218      * Returns {@code true} if the configuration has any sources defined.
219      * @return {@code true} if the configuration has any sources defined.
220      */
221     public boolean hasSource() {
222         return !reportables.isEmpty() || !sources.isEmpty();
223     }
224 
225     /**
226      * Gets a builder initialized with any files specified as sources.
227      * @return a configured builder.
228      */
229     public IReportableListWalker.Builder getSources() {
230         DocumentName name = DocumentName.builder(new File(".")).build();
231         IReportableListWalker.Builder builder = IReportableListWalker.builder(name);
232         sources.forEach(file -> builder.addReportable(new FileListWalker(new FileDocument(file, DocumentNameMatcher.MATCHES_ALL))));
233         reportables.forEach(builder::addReportable);
234         return builder;
235     }
236 
237     /**
238      * Gets the matcher that matches generated text.
239      * @return the matcher that matches generated text.
240      */
241     public IHeaderMatcher getGeneratedMatcher() {
242         return new AnyBuilder().setResource("/org/apache/rat/generation-keywords.txt").build();
243     }
244 
245     /**
246      * Retrieves the archive processing type.
247      * @return The archive processing type.
248      */
249     public Processing getArchiveProcessing() {
250         return archiveProcessing == null ? Defaults.ARCHIVE_PROCESSING : archiveProcessing;
251     }
252 
253     /**
254      * Sets the archive processing type. If not set will default to NOTIFICATION.
255      * @param archiveProcessing the type of processing archives should have.
256      */
257     public void setArchiveProcessing(final Processing archiveProcessing) {
258         this.archiveProcessing = archiveProcessing;
259     }
260 
261     /**
262      * Retrieves the archive processing type.
263      * @return The archive processing type.
264      */
265     public Processing getStandardProcessing() {
266         return standardProcessing == null ? Defaults.STANDARD_PROCESSING : standardProcessing;
267     }
268 
269     /**
270      * Sets the archive processing type. If not set will default to NOTIFICATION.
271      * @param standardProcessing the type of processing archives should have.
272      */
273     public void setStandardProcessing(final Processing standardProcessing) {
274         this.standardProcessing = standardProcessing;
275     }
276 
277     /**
278      * Set the log level for reporting collisions in the set of license families.
279      * <p>NOTE: should be set before licenses or license families are added.</p>
280      * @param level The log level to use.
281      */
282     public void logFamilyCollisions(final Level level) {
283         licenseSetFactory.logFamilyCollisions(level);
284     }
285 
286     /**
287      * Sets the reporting option for duplicate license families.
288      * @param state The ReportingSet.Option to use for reporting.
289      */
290     public void familyDuplicateOption(final ReportingSet.Options state) {
291         licenseSetFactory.familyDuplicateOption(state);
292     }
293 
294     /**
295      * Sets the log level for reporting license collisions.
296      * @param level The log level.
297      */
298     public void logLicenseCollisions(final Level level) {
299         licenseSetFactory.logLicenseCollisions(level);
300     }
301 
302     /**
303      * Sets the reporting option for duplicate licenses.
304      * @param state the ReportingSt.Option to use for reporting.
305      */
306     public void licenseDuplicateOption(final ReportingSet.Options state) {
307         licenseSetFactory.licenseDuplicateOption(state);
308     }
309 
310     /**
311      * Set the level of license families that should be output in the XML document.
312      * @param filter the license families to list.
313      */
314     public void listFamilies(final LicenseFilter filter) {
315         listFamilies = filter;
316     }
317 
318     /**
319      * Return the current filter that determines which families will be output in the XML document.
320      * @return the filter that defines the families to list.
321      */
322     public LicenseFilter listFamilies() {
323         return listFamilies;
324     }
325 
326     /**
327      * Set the level of licenses that should be output in the XML document.
328      * @param filter the licenses to list.
329      */
330     public void listLicenses(final LicenseFilter filter) {
331         listLicenses = filter;
332     }
333 
334     /**
335      * Gets the selected license filter.
336      * @return the filter to limit license display.
337      */
338     public LicenseFilter listLicenses() {
339         return listLicenses;
340     }
341 
342     /**
343      * Sets the dry run flag.
344      * @param state the state for the dry run flag.
345      */
346     public void setDryRun(final boolean state) {
347         dryRun = state;
348     }
349 
350     /**
351      * Returns the state of the dry run flag.
352      * @return the state of the dry run flag.
353      */
354     public boolean isDryRun() {
355         return dryRun;
356     }
357 
358     /**
359      * Excludes a StandardCollection of patterns.
360      * @param collection the StandardCollection to exclude.
361      * @see ExclusionProcessor#addExcludedCollection(StandardCollection)
362      */
363     public void addExcludedCollection(final StandardCollection collection) {
364         exclusionProcessor.addExcludedCollection(collection);
365     }
366 
367     /**
368      * Excludes the file processor defined in the StandardCollection.
369      * @param collection the StandardCollection to exclude.
370      * @see ExclusionProcessor#addFileProcessor(StandardCollection)
371      */
372     public void addExcludedFileProcessor(final StandardCollection collection) {
373         exclusionProcessor.addFileProcessor(collection);
374     }
375 
376     /**
377      * Excludes files that match a FileFilter.
378      * @param fileFilter the file filter to match.
379      */
380     public void addExcludedFilter(final FileFilter fileFilter) {
381         exclusionProcessor.addExcludedMatcher(new DocumentNameMatcher(fileFilter));
382     }
383 
384     /**
385      * Excludes files that match a DocumentNameMatcher.
386      * @param matcher the DocumentNameMatcher to match.
387      */
388     public void addExcludedMatcher(final DocumentNameMatcher matcher) {
389         exclusionProcessor.addExcludedMatcher(matcher);
390     }
391 
392     /**
393      * Excludes files that match the pattern.
394      *
395      * @param patterns the collection of patterns to exclude.
396      * @see ExclusionProcessor#addIncludedPatterns(Iterable)
397      */
398     public void addExcludedPatterns(final Iterable<String> patterns) {
399         exclusionProcessor.addExcludedPatterns(patterns);
400     }
401 
402     /**
403      * Adds the patterns from the standard collection as included patterns.
404      * @param collection the standard collection to include.
405      */
406     public void addIncludedCollection(final StandardCollection collection) {
407         exclusionProcessor.addIncludedCollection(collection);
408     }
409 
410     /**
411      * Adds the fileFilter to filter files that should be included, this overrides any
412      * exclusion of the same files.
413      * @param fileFilter the filter to identify files that should be included.
414      */
415     public void addIncludedFilter(final FileFilter fileFilter) {
416         exclusionProcessor.addIncludedMatcher(new DocumentNameMatcher(fileFilter));
417     }
418 
419     /**
420      * Add file patterns that are to be included. These patterns override any exclusion of
421      * the same files.
422      * @param patterns The iterable of Strings containing the patterns.
423      */
424     public void addIncludedPatterns(final Iterable<String> patterns) {
425         exclusionProcessor.addIncludedPatterns(patterns);
426     }
427 
428     /**
429      * Get the DocumentNameMatcher that excludes files found in the directory tree..
430      * @param baseDir the DocumentName for the base directory.
431      * @return the DocumentNameMatcher for the base directory.
432      */
433     public DocumentNameMatcher getDocumentExcluder(final DocumentName baseDir) {
434         return exclusionProcessor.getNameMatcher(baseDir);
435     }
436 
437     /**
438      * Gets the IOSupplier with the style sheet.
439      * @return the Supplier of the InputStream that is the XSLT style sheet to style
440      * the report with.
441      */
442     public IOSupplier<InputStream> getStyleSheet() {
443         return styleSheet;
444     }
445 
446     /**
447      * Sets the style sheet for custom processing. The IOSupplier may be called
448      * multiple times, so the input stream must be able to be opened and closed
449      * multiple times.
450      * @param styleSheet the XSLT style sheet to style the report with.
451      */
452     public void setStyleSheet(final IOSupplier<InputStream> styleSheet) {
453         this.styleSheet = styleSheet;
454     }
455 
456     /**
457      * Adds the licenses and approved licenses from the defaults object to the
458      * configuration. <em>Side effect:</em> if the report should be styled and no
459      * style sheet has been set the plain stylesheet from the defaults will be used.
460      * @param defaults The defaults to set.
461      */
462     public void setFrom(final Defaults defaults) {
463         licenseSetFactory.add(defaults.getLicenseSetFactory());
464         if (getStyleSheet() == null) {
465             setStyleSheet(StyleSheets.PLAIN.getStyleSheet());
466         }
467         defaults.getStandardExclusion().forEach(this::addExcludedCollection);
468     }
469 
470     /**
471      * Sets the style sheet.
472      * @param styleSheet the XSLT style sheet file to style the report with.
473      */
474     public void setStyleSheet(final File styleSheet) {
475         Objects.requireNonNull(styleSheet, "styleSheet file should not be null");
476         setStyleSheet(styleSheet.toURI());
477     }
478 
479     /**
480      * Sets the style sheet for custom processing. The stylesheet may be opened
481      * multiple times so the URI must be capable of being opened multiple times.
482      * @param styleSheet the URI of the XSLT style sheet to style the report with.
483      */
484     public void setStyleSheet(final URI styleSheet) {
485         Objects.requireNonNull(styleSheet, "Stylesheet file must not be null");
486         try {
487             setStyleSheet(styleSheet.toURL());
488         } catch (MalformedURLException e) {
489             throw new ConfigurationException("Unable to process stylesheet", e);
490         }
491     }
492 
493     /**
494      * Sets the style sheet for custom processing. The stylesheet may be opened
495      * multiple times so the URL must be capable of being opened multiple times.
496      * @param styleSheet the URL of the XSLT style sheet to style the report with.
497      */
498     public void setStyleSheet(final URL styleSheet) {
499         Objects.requireNonNull(styleSheet, "Stylesheet file must not be null");
500         setStyleSheet(styleSheet::openStream);
501     }
502 
503     /**
504      * Sets the supplier for the output stream. The supplier may be called multiple
505      * times to provide the stream. Suppliers should prepare streams that are
506      * appended to and that can be closed. If an {@code OutputStream} should not be
507      * closed consider wrapping it in a {@code NoCloseOutputStream}
508      * @param out The OutputStream supplier that provides the output stream to write
509      * the report to. A null value will use System.out.
510      * @see NoCloseOutputStream
511      */
512     public void setOut(final IOSupplier<OutputStream> out) {
513         this.out = out;
514     }
515 
516     /**
517      * Sets the OutputStream supplier to use the specified file. The file may be
518      * opened and closed several times. File is deleted first and then may be
519      * repeatedly opened in append mode.
520      * @see #setOut(IOSupplier)
521      * @param file The file to create the supplier with.
522      */
523     public void setOut(final File file) {
524         Objects.requireNonNull(file, "output file should not be null");
525         if (file.exists()) {
526             try {
527                 Files.delete(file.toPath());
528             } catch (IOException e) {
529                 DefaultLog.getInstance().warn("Unable to delete file: " + file);
530             }
531         }
532         File parent = file.getParentFile();
533         if (!parent.mkdirs() && !parent.isDirectory()) {
534             DefaultLog.getInstance().warn("Unable to create directory: " + file.getParentFile());
535         }
536         setOut(() -> new FileOutputStream(file, true));
537     }
538 
539     /**
540      * Returns the output stream supplier. If no stream has been set returns a
541      * supplier for System.out.
542      * @return The supplier of the output stream to write the report to.
543      */
544     public IOSupplier<OutputStream> getOutput() {
545         return out == null ? () -> new NoCloseOutputStream(System.out) : out;
546     }
547 
548     /**
549      * Gets a PrintWriter that wraps the output stream.
550      * @return A supplier for a PrintWriter that wraps the output stream.
551      * @see #getOutput()
552      */
553     public IOSupplier<PrintWriter> getWriter() {
554         return () -> new PrintWriter(new OutputStreamWriter(getOutput().get(), StandardCharsets.UTF_8));
555     }
556 
557     /**
558      * Adds a license to the list of licenses. Does not add the license to the list
559      * of approved licenses.
560      * @param license The license to add to the list of licenses.
561      */
562     public void addLicense(final ILicense license) {
563         licenseSetFactory.addLicense(license);
564     }
565 
566     /**
567      * Adds a license to the list of licenses. Does not add the license to the list
568      * of approved licenses.
569      * @param builder The license builder to build and add to the list of licenses.
570      * @return The ILicense implementation that was added.
571      */
572     public ILicense addLicense(final ILicense.Builder builder) {
573         return licenseSetFactory.addLicense(builder);
574     }
575 
576     /**
577      * Adds multiple licenses to the list of licenses. Does not add the licenses to
578      * the list of approved licenses.
579      * @param licenses The licenses to add.
580      */
581     public void addLicenses(final Collection<ILicense> licenses) {
582         licenseSetFactory.addLicenses(licenses);
583     }
584 
585     /**
586      * Adds a license family to the list of families. Does not add the family to the
587      * list of approved licenses.
588      * @param family The license family to add to the list of license families.
589      */
590     public void addFamily(final ILicenseFamily family) {
591        licenseSetFactory.addFamily(family);
592     }
593 
594     /**
595      * Adds a license family to the list of families. Does not add the family to the
596      * list of approved licenses.
597      * @param builder The licenseFamily.Builder to build and add to the list of
598      * licenses.
599      */
600     public void addFamily(final ILicenseFamily.Builder builder) {
601         licenseSetFactory.addFamily(builder);
602     }
603 
604     /**
605      * Adds multiple families to the list of license families. Does not add the
606      * licenses to the list of approved licenses.
607      * @param families The license families to add.
608      */
609     public void addFamilies(final Collection<ILicenseFamily> families) {
610         families.forEach(this::addApprovedLicenseCategory);
611     }
612 
613     /**
614      * Adds an ILicenseFamily to the list of approved licenses.
615      * @param approvedILicenseFamily the LicenseFamily to add.
616      */
617     public void addApprovedLicenseCategory(final ILicenseFamily approvedILicenseFamily) {
618         addApprovedLicenseCategory(approvedILicenseFamily.getFamilyCategory());
619     }
620 
621     /**
622      * Adds a license family category (id) to the list of approved licenses
623      * @param familyCategory the category to add.
624      */
625     public void addApprovedLicenseCategory(final String familyCategory) {
626         licenseSetFactory.approveLicenseCategory(familyCategory);
627     }
628 
629     /**
630      * Adds a collection of license family categories to the set of approved license
631      * names.
632      * @param approvedLicenseCategories set of approved license categories.
633      */
634     public void addApprovedLicenseCategories(final Collection<String> approvedLicenseCategories) {
635         approvedLicenseCategories.forEach(this::addApprovedLicenseCategory);
636     }
637 
638     /**
639      * Adds a license family category to the list of approved licenses. <em>Once a
640      * license has been removed from the approved list it cannot be re-added</em>
641      * @param familyCategory the category to add.
642      */
643     public void removeApprovedLicenseCategory(final String familyCategory) {
644         licenseSetFactory.removeLicenseCategory(ILicenseFamily.makeCategory(familyCategory));
645     }
646 
647     /**
648      * Removes a license family category from the list of approved licenses.
649      * <em>Once a license has been removed from the approved list it cannot be
650      * re-added</em>
651      * @param familyCategory the family category to remove.
652      */
653     public void removeApprovedLicenseCategories(final Collection<String> familyCategory) {
654         familyCategory.forEach(this::removeApprovedLicenseCategory);
655     }
656 
657     /**
658      * Gets the SortedSet of approved license categories. <em>Once a license has
659      * been removed from the approved list it cannot be re-added</em>
660      * @param filter The LicenseFilter to filter the categories by.
661      * @return the Sorted set of approved license categories.
662      */
663     public SortedSet<String> getLicenseCategories(final LicenseFilter filter) {
664         return licenseSetFactory.getLicenseCategories(filter);
665     }
666 
667     /**
668      * Gets the SortedSet of approved license categories. <em>Once a license has
669      * been removed from the approved list it cannot be re-added</em>
670      * @param filter The LicenseFilter to filter the licenses by.
671      * @return the Sorted set of approved license categories.
672      */
673     public SortedSet<ILicense> getLicenses(final LicenseFilter filter) {
674         return licenseSetFactory.getLicenses(filter);
675     }
676 
677     /**
678      * Gets the SortedSet of approved license categories. <em>Once a license has
679      * been removed from the approved list it cannot be re-added</em>
680      * @param filter The LicenseFilter to filter the licenses by.
681      * @return the Sorted set of approved license categories.
682      */
683     public SortedSet<String> getLicenseIds(final LicenseFilter filter) {
684         return licenseSetFactory.getLicenseIds(filter);
685     }
686 
687     /**
688      * Adds an ILicenseFamily to the list of approved licenses.
689      * @param approvedLicense the License to add.
690      */
691     public void addApprovedLicenseId(final ILicense approvedLicense) {
692         addApprovedLicenseId(approvedLicense.getId());
693     }
694 
695     /**
696      * Adds a license family category (id) to the list of approved licenses
697      * @param licenseId the license id to add.
698      */
699     public void addApprovedLicenseId(final String licenseId) {
700         licenseSetFactory.approveLicenseId(licenseId);
701     }
702 
703     /**
704      * Adds a collection of license family categories to the set of approved license
705      * names.
706      * @param approvedLicenseIds set of approved license IDs.
707      */
708     public void addApprovedLicenseIds(final Collection<String> approvedLicenseIds) {
709         approvedLicenseIds.forEach(this::addApprovedLicenseId);
710     }
711 
712     /**
713      * Adds a license family category to the list of approved licenses. <em>Once a
714      * license has been removed from the approved list it cannot be re-added</em>
715      * @param licenseId the license ID to add.
716      */
717     public void removeApprovedLicenseId(final String licenseId) {
718         licenseSetFactory.removeLicenseId(licenseId);
719     }
720 
721     /**
722      * Removes a license family category from the list of approved licenses.
723      * <em>Once a license has been removed from the approved list it cannot be
724      * re-added</em>
725      * @param licenseIds the license IDs to remove.
726      */
727     public void removeApprovedLicenseIds(final Collection<String> licenseIds) {
728         licenseIds.forEach(this::removeApprovedLicenseId);
729     }
730 
731     /**
732      * Returns the optional license copyright being added if RAT is adding headers.
733      * This value is ignored, if no license headers are added.
734      * @return the optional copyright message.
735      * @see #isAddingLicenses()
736      */
737     public String getCopyrightMessage() {
738         return copyrightMessage;
739     }
740 
741     /**
742      * Sets the optional copyright message used if RAT is adding license headers.
743      * This value is ignored, if no license headers are added.
744      * @param copyrightMessage message to set.
745      * @see #isAddingLicenses()
746      */
747     public void setCopyrightMessage(final String copyrightMessage) {
748         this.copyrightMessage = copyrightMessage;
749     }
750 
751     /**
752      * Gets the flag that determines if license headers are "forced" overwriting existing files.
753      * This value is ignored if RAT is not adding licenses.
754      * @return {@code true} if RAT is forcing the adding license headers.
755      * @see #isAddingLicenses()
756      */
757     public boolean isAddingLicensesForced() {
758         return addingLicensesForced;
759     }
760 
761     /**
762      * Gets the flag that determines if license headers should be added if missing.
763      * @return whether RAT should add missing license headers.
764      * @see #isAddingLicensesForced()
765      * @see #getCopyrightMessage()
766      */
767     public boolean isAddingLicenses() {
768         return addingLicenses;
769     }
770 
771     /**
772      * Sets whether RAT should enable, disable, or force the adding of license
773      * headers.
774      * @param addLicenseHeaders enables/disables or forces adding of licenses
775      * headers.
776      * @see #isAddingLicenses()
777      * @see #setCopyrightMessage(String)
778      */
779     public void setAddLicenseHeaders(final AddLicenseHeaders addLicenseHeaders) {
780         addingLicenses = false;
781         addingLicensesForced = false;
782         switch (addLicenseHeaders) {
783         case FALSE:
784             // do nothing
785             break;
786         case FORCED:
787             addingLicensesForced = true;
788             addingLicenses = true;
789             break;
790         case TRUE:
791             addingLicenses = true;
792             break;
793         }
794     }
795 
796     /**
797      * Gets a sorted set of ILicenseFamily objects based on {@code filter}. if
798      * filter is set:
799      * <ul>
800      * <li>{@code all} - All licenses families will be returned.</li>
801      * <li>{@code approved} - Only approved license families will be returned</li>
802      * <li>{@code none} - No license families will be returned</li>
803      * </ul>
804      * @param filter The license filter.
805      * @return The set of defined licenses.
806      */
807     public SortedSet<ILicenseFamily> getLicenseFamilies(final LicenseFilter filter) {
808         return licenseSetFactory.getLicenseFamilies(filter);
809     }
810 
811     /**
812      * Gets the ClaimValidator for the configuration.
813      * @return the ClaimValidator.
814      */
815     public ClaimValidator getClaimValidator() {
816         return claimValidator;
817     }
818 
819     /**
820      * Gets the enclosed LicenseSetFactory.
821      * @return the license set factory.
822      */
823     public LicenseSetFactory getLicenseSetFactory() {
824         return licenseSetFactory;
825     }
826 
827     /**
828      * Validates that the configuration is valid.
829      * @param logger String consumer to log warning messages to.
830      * @throws ConfigurationException on configuration error.
831      */
832     public void validate(final Consumer<String> logger) {
833         if (!hasSource()) {
834             String msg = "At least one source must be specified";
835             logger.accept(msg);
836             throw new ConfigurationException(msg);
837         }
838         if (licenseSetFactory.getLicenses(LicenseFilter.ALL).isEmpty()) {
839             String msg = "You must specify at least one license";
840             logger.accept(msg);
841             throw new ConfigurationException(msg);
842         }
843     }
844 
845     /**
846      * A wrapper around an output stream that does not close the output stream.
847      */
848     public static class NoCloseOutputStream extends OutputStream {
849         /** the output stream this stream wraps */
850         private final OutputStream delegate;
851 
852         /**
853          * Constructor.
854          * @param delegate the output stream to wrap.
855          */
856         public NoCloseOutputStream(final OutputStream delegate) {
857             this.delegate = delegate;
858         }
859 
860         @Override
861         public void write(final int arg0) throws IOException {
862             delegate.write(arg0);
863         }
864 
865         /**
866          * Does not actually close the delegate. But does perform a flush.
867          * @throws IOException on Error.
868          */
869         @Override
870         public void close() throws IOException {
871             this.delegate.flush();
872         }
873 
874         @Override
875         public boolean equals(final Object obj) {
876             return delegate.equals(obj);
877         }
878 
879         @Override
880         public void flush() throws IOException {
881             delegate.flush();
882         }
883 
884         @Override
885         public int hashCode() {
886             return delegate.hashCode();
887         }
888 
889         @Override
890         public String toString() {
891             return delegate.toString();
892         }
893 
894         @Override
895         public void write(final byte[] arg0, final int arg1, final int arg2) throws IOException {
896             delegate.write(arg0, arg1, arg2);
897         }
898 
899         @Override
900         public void write(final byte[] b) throws IOException {
901             delegate.write(b);
902         }
903     }
904 }