blob: dee2915cb9a89c064f13f9681cb9fd03817e2499 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
*/
package org.apache.rat;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Consumer;
import org.apache.commons.io.filefilter.FalseFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.function.IOSupplier;
import org.apache.rat.config.AddLicenseHeaders;
import org.apache.rat.license.ILicense;
import org.apache.rat.license.ILicenseFamily;
import org.apache.rat.license.LicenseFamilySetFactory;
import org.apache.rat.license.LicenseSetFactory;
import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
import org.apache.rat.report.IReportable;
import org.apache.rat.utils.Log;
import org.apache.rat.utils.ReportingSet;
/**
* A configuration object is used by the front end to invoke the
* {@link Reporter}. The sole purpose of the front ends is to create the
* configuration and invoke the {@link Reporter}.
*/
public class ReportConfiguration {
private final ReportingSet<ILicenseFamily> families;
private final ReportingSet<ILicense> licenses;
private final SortedSet<String> approvedLicenseCategories;
private final SortedSet<String> removedLicenseCategories;
private boolean addingLicenses;
private boolean addingLicensesForced;
private String copyrightMessage;
private IOSupplier<OutputStream> out;
private boolean styleReport;
private IOSupplier<InputStream> styleSheet;
private IReportable reportable;
private FilenameFilter filesToIgnore;
private IOFileFilter directoriesToIgnore;
private final Log log;
private LicenseFilter listFamilies;
private LicenseFilter listLicenses;
private boolean dryRun;
/**
* Constructor
* @param log The Log implementation that messages will be written to.
*/
public ReportConfiguration(Log log) {
this.log = log;
families = new ReportingSet<>(LicenseFamilySetFactory.emptyLicenseFamilySet()).setLog(log)
.setMsgFormat( s -> String.format("Duplicate LicenseFamily category: %s", s.getFamilyCategory()));
licenses = new ReportingSet<>(LicenseSetFactory.emptyLicenseSet()).setLog(log)
.setMsgFormat( s -> String.format( "Duplicate License %s (%s) of type %s", s.getName(), s.getId(), s.getLicenseFamily().getFamilyCategory()));
approvedLicenseCategories = new TreeSet<>();
removedLicenseCategories = new TreeSet<>();
styleReport = true;
listFamilies = LicenseFilter.NONE;
listLicenses = LicenseFilter.NONE;
dryRun = false;
}
/**
* Retrieves the Log that was provided in the constructor.
* @return the Log for the system.
*/
public Log getLog() {
return log;
}
/**
* Set the log level for reporting collisions in the set of license families.
* <p>NOTE: should be set before licenses or license families are added.</p>
* @param level The log level to use.
*/
public void logFamilyCollisions(Log.Level level) {
families.setLogLevel(level);
}
/**
* Sets the reporting option for duplicate license families.
* @param state The ReportingSet.Option to use for reporting.
*/
public void familyDuplicateOption(ReportingSet.Options state) {
families.setDuplicateOption(state);
}
/**
* Sets the log level for reporting license collisions.
* @param level The log level.
*/
public void logLicenseCollisions(Log.Level level) {
licenses.setLogLevel(level);
}
/**
* Sets the reporting option for duplicate licenses.
* @param state the ReportingSt.Option to use for reporting.
*/
public void licenseDuplicateOption(ReportingSet.Options state) {
licenses.setDuplicateOption(state);
}
/**
* Set the level of license families that should be output in the XML document.
* @param filter the license families to list.
*/
public void listFamilies(LicenseFilter filter) {
listFamilies = filter;
}
public LicenseFilter listFamilies() {
return listFamilies;
}
/**
* Set the level of licenses that should be output in the XML document.
* @param filter the licenses to list.
*/
public void listLicenses(LicenseFilter filter) {
listLicenses = filter;
}
public LicenseFilter listLicenses() {
return listLicenses;
}
/**
* Sets the dry run flag.
* @param state the state for the dry run flag.
*/
public void setDryRun(boolean state) {
dryRun = state;
}
/**
* Returns the state of the dry run flag.
* @return the stae of the dry run flag.
*/
public boolean isDryRun() {
return dryRun;
}
/**
* @return The filename filter for the potential input files.
*/
public FilenameFilter getFilesToIgnore() {
return filesToIgnore;
}
/**
* @param filesToIgnore the filename filter to filter the input files.
*/
public void setFilesToIgnore(FilenameFilter filesToIgnore) {
this.filesToIgnore = filesToIgnore;
}
public IOFileFilter getDirectoriesToIgnore() {
return directoriesToIgnore;
}
public void setDirectoriesToIgnore(IOFileFilter directoriesToIgnore) {
if (directoriesToIgnore == null) {
this.directoriesToIgnore = FalseFileFilter.FALSE;
} else {
this.directoriesToIgnore = directoriesToIgnore;
}
}
public void addDirectoryToIgnore(IOFileFilter directoryToIgnore) {
this.directoriesToIgnore = this.directoriesToIgnore.and(directoryToIgnore);
}
/**
* @return the thing being reported on.
*/
public IReportable getReportable() {
return reportable;
}
/**
* @param reportable the thing being reported on.
*/
public void setReportable(IReportable reportable) {
this.reportable = reportable;
}
/**
* @return the Supplier of the InputStream that is the XSLT style sheet to style
* the report with.
*/
public IOSupplier<InputStream> getStyleSheet() {
return styleSheet;
}
/**
* Sets the style sheet for custom processing. The IOSupplier may be called
* multiple times, so the input stream must be able to be opened and closed
* multiple times.
*
* @param styleSheet the XSLT style sheet to style the report with.
*/
public void setStyleSheet(IOSupplier<InputStream> styleSheet) {
this.styleSheet = styleSheet;
}
/**
* Adds the licenses and approved licenses from the defaults object to the
* configuration. <em>Side effect: </em> if the report should be styled and no
* style sheet has been set the plain stylesheet from the defaults will be used.
*
* @param defaults The defaults to set.
*/
public void setFrom(Defaults defaults) {
setFilesToIgnore(Defaults.getFilesToIgnore());
setDirectoriesToIgnore(Defaults.getDirectoriesToIgnore());
addLicensesIfNotPresent(defaults.getLicenses(LicenseFilter.ALL));
addApprovedLicenseCategories(defaults.getLicenseIds(LicenseFilter.APPROVED));
if (isStyleReport() && getStyleSheet() == null) {
setStyleSheet(Defaults.getPlainStyleSheet());
}
}
/**
*
* @param styleSheet the XSLT style sheet to style the report with.
*/
public void setStyleSheet(File styleSheet) {
Objects.requireNonNull(styleSheet, "styleSheet file should not be null");
setStyleSheet(styleSheet.toURI());
}
/**
* Sets the style sheet for custom processing. The stylesheet may be opened
* multiple times so the URI must be capable of being opened multiple times.
*
* @param styleSheet the URI of the XSLT style sheet to style the report with.
*/
public void setStyleSheet(URI styleSheet) {
Objects.requireNonNull(styleSheet, "styleSheet file should not be null");
try {
setStyleSheet(styleSheet.toURL());
} catch (MalformedURLException e) {
throw new ConfigurationException("Unable to process stylesheet", e);
}
}
/**
* Sets the style sheet for custom processing. The stylesheet may be opened
* multiple times so the URL must be capable of being opened multiple times.
*
* @param styleSheet the URL of the XSLT style sheet to style the report with.
*/
public void setStyleSheet(URL styleSheet) {
Objects.requireNonNull(styleSheet, "styleSheet file should not be null");
setStyleSheet(styleSheet::openStream);
}
/**
* @return {@code true} if the XML report should be styled.
*/
public boolean isStyleReport() {
return styleReport;
}
/**
* @param styleReport specifies whether the XML report should be styled.
*/
public void setStyleReport(boolean styleReport) {
this.styleReport = styleReport;
}
/**
* Sets the supplier for the output stream. The supplier may be called multiple
* times to provide the stream. Suppliers should prepare streams that are
* appended to and that can be closed. If an {@code OutputStream} should not be
* closed consider wrapping it in a {@code NoCloseOutputStream}
*
* @param out The OutputStream supplier that provides the output stream to write
* the report to. A null value will use System.out.
* @see NoCloseOutputStream
*/
public void setOut(IOSupplier<OutputStream> out) {
this.out = out;
}
/**
* Sets the OutputStream supplier to use the specified file. The file may be
* opened and closed several times. File is deleted first and then may be
* repeatedly opened in append mode.
*
* @see #setOut(IOSupplier)
* @param file The file to create the supplier with.
*/
public void setOut(File file) {
Objects.requireNonNull(file, "output file should not be null");
if (file.exists()) {
try {
Files.delete(file.toPath());
} catch (IOException e) {
log.warn("Unable to delete file:"+file);
}
}
setOut(() -> new FileOutputStream(file, true));
}
/**
* Returns the output stream supplier. If no stream has been set returns a
* supplier for System.out.
*
* @return The supplier of the output stream to write the report to.
*/
public IOSupplier<OutputStream> getOutput() {
return out == null ? () -> new NoCloseOutputStream(System.out) : out;
}
/**
* @return A supplier for a PrintWriter that wraps the output stream.
* @see #getOutput()
*/
public IOSupplier<PrintWriter> getWriter() {
return () -> new PrintWriter(new OutputStreamWriter(getOutput().get(), StandardCharsets.UTF_8));
}
/**
* Adds a license to the list of licenses. Does not add the license to the list
* of approved licenses.
*
* @param license The license to add to the list of licenses.
*/
public void addLicense(ILicense license) {
if (license != null) {
this.licenses.add(license);
this.families.addIfNotPresent(license.getLicenseFamily());
}
}
/**
* Adds a license to the list of licenses. Does not add the license to the list
* of approved licenses.
*
* @param builder The license builder to build and add to the list of licenses.
* @return The ILicense implementation that was added.
*/
public ILicense addLicense(ILicense.Builder builder) {
if (builder != null) {
ILicense license = builder.setLicenseFamilies(families).build();
this.licenses.add(license);
return license;
}
return null;
}
/**
* Adds multiple licenses to the list of licenses. Does not add the licenses to
* the list of approved licenses.
*
* @param licenses The licenses to add.
*/
public void addLicenses(Collection<ILicense> licenses) {
this.licenses.addAll(licenses);
licenses.stream().map(ILicense::getLicenseFamily).forEach(families::add);
}
/**
* Adds multiple licenses to the list of licenses. Does not add the licenses to
* the list of approved licenses.
*
* @param licenses The licenses to add.
*/
public void addLicensesIfNotPresent(Collection<ILicense> licenses) {
this.licenses.addAllIfNotPresent(licenses);
licenses.stream().map(ILicense::getLicenseFamily).forEach(families::addIfNotPresent);
}
/**
* Adds a license family to the list of families. Does not add the family to the
* list of approved licenses.
*
* @param family The license family to add to the list of license families.
*/
public void addFamily(ILicenseFamily family) {
if (family != null) {
this.families.add(family);
}
}
/**
* Adds a license family to the list of families. Does not add the family to the
* list of approved licenses.
*
* @param builder The licenseFamily.Builder to build and add to the list of
* licenses.
*/
public void addFamily(ILicenseFamily.Builder builder) {
if (builder != null) {
this.families.add(builder.build());
}
}
/**
* Adds multiple families to the list of license families. Does not add the
* licenses to the list of approved licenses.
*
* @param families The license families to add.
*/
public void addFamilies(Collection<ILicenseFamily> families) {
this.families.addAll(families);
}
/**
* Adds an ILicenseFamily to the list of approved licenses.
*
* @param approvedILicenseFamily the LicenseFamily to add.
*/
public void addApprovedLicenseCategory(ILicenseFamily approvedILicenseFamily) {
approvedLicenseCategories.add(approvedILicenseFamily.getFamilyCategory());
}
/**
* Adds a license family category (id) to the list of approved licenses
*
* @param familyCategory the category to add.
*/
public void addApprovedLicenseCategory(String familyCategory) {
approvedLicenseCategories.add(ILicenseFamily.makeCategory(familyCategory));
}
/**
* Adds a collection of license family categories to the set of approved license
* names.
*
* @param approvedLicenseCategories set of approved license categories.
*/
public void addApprovedLicenseCategories(Collection<String> approvedLicenseCategories) {
approvedLicenseCategories.forEach(this::addApprovedLicenseCategory);
}
/**
* Adds a license family category to the list of approved licenses. <em>Once a
* license has been removed from the approved list it cannot be re-added</em>
*
* @param familyCategory the category to add.
*/
public void removeApprovedLicenseCategory(String familyCategory) {
removedLicenseCategories.add(ILicenseFamily.makeCategory(familyCategory));
}
/**
* Removes a license family category from the list of approved licenses.
* <em>Once a license has been removed from the approved list it cannot be
* re-added</em>
*
* @param familyCategory the family category to remove.
*/
public void removeApprovedLicenseCategories(Collection<String> familyCategory) {
familyCategory.forEach(this::removeApprovedLicenseCategory);
}
/**
* Gets the SortedSet of approved license categories. <em>Once a license has
* been removed from the approved list it cannot be re-added</em>
*
* @return the Sorted set of approved license categories.
*/
public SortedSet<String> getApprovedLicenseCategories() {
SortedSet<String> result = new TreeSet<>(approvedLicenseCategories);
result.removeAll(removedLicenseCategories);
return result;
}
/**
* Returns the optional license copyright being added if RAT is adding headers.
* This value is ignored, if no license headers are added.
*
* @return the optional copyright message.
* @see #isAddingLicenses()
*/
public String getCopyrightMessage() {
return copyrightMessage;
}
/**
* Sets the optional copyright message used if RAT is adding license headers.
* This value is ignored, if no license headers are added.
*
* @param copyrightMessage message to set.
* @see #isAddingLicenses()
*/
public void setCopyrightMessage(String copyrightMessage) {
this.copyrightMessage = copyrightMessage;
}
/**
* This value is ignored if RAT is not adding licenses.
*
* @return {@code true} if RAT is forcing the adding license headers.
* @see #isAddingLicenses()
*/
public boolean isAddingLicensesForced() {
return addingLicensesForced;
}
/**
* @return whether RAT should add missing license headers.
* @see #isAddingLicensesForced()
* @see #getCopyrightMessage()
*/
public boolean isAddingLicenses() {
return addingLicenses;
}
/**
* Sets whether RAT should enable, disable, or force the adding of license
* headers.
*
* @param addLicenseHeaders enables/disables or forces adding of licenses
* headers.
* @see #isAddingLicenses()
* @see #setCopyrightMessage(String)
*/
public void setAddLicenseHeaders(AddLicenseHeaders addLicenseHeaders) {
addingLicenses = false;
addingLicensesForced = false;
switch (addLicenseHeaders) {
case FALSE:
// do nothing
break;
case FORCED:
addingLicensesForced = true;
// fall through
case TRUE:
addingLicenses = true;
break;
}
}
/**
* Gets a set Licenses of depending on the {@code filter} if filter is set:
* <ul>
* <li>{@code all} - All licenses will be returned.</li>
* <li>{@code approved} - Only approved licenses will be returned</li>
* <li>{@code none} - No licenses will be returned</li>
* </ul>
*
* @param filter The license filter.
* @return The set of defined licenses.
*/
public SortedSet<ILicense> getLicenses(LicenseFilter filter) {
switch (filter) {
case ALL:
return Collections.unmodifiableSortedSet(licenses);
case APPROVED:
return new LicenseSetFactory(licenses, getApprovedLicenseCategories()).getLicenses(filter);
case NONE:
default:
return LicenseSetFactory.emptyLicenseSet();
}
}
/**
* Gets a sorted set of ILicenseFamily objects based on {@code filter}. if
* filter is set:
* <ul>
* <li>{@code all} - All licenses families will be returned.</li>
* <li>{@code approved} - Only approved license families will be returned</li>
* <li>{@code none} - No license families will be returned</li>
* </ul>
*
* @param filter The license filter.
* @return The set of defined licenses.
*/
public SortedSet<ILicenseFamily> getLicenseFamilies(LicenseFilter filter) {
return new LicenseFamilySetFactory(families, getApprovedLicenseCategories()).getFamilies(filter);
}
/**
* Validates that the configuration is valid.
*
* @param logger String consumer to log warning messages to.
*/
public void validate(Consumer<String> logger) {
if (reportable == null) {
throw new ConfigurationException("Reportable may not be null");
}
if (licenses.isEmpty()) {
throw new ConfigurationException("You must specify at least one license");
}
if (styleSheet != null && !isStyleReport()) {
logger.accept("Ignoring stylesheet because styling is not selected");
}
if (styleSheet == null && isStyleReport()) {
throw new ConfigurationException("Stylesheet must be specified if report styling is selected");
}
}
/**
* A wrapper around an output stream that does not close the output stream.
*/
public static class NoCloseOutputStream extends OutputStream {
private final OutputStream delegate;
public NoCloseOutputStream(OutputStream delegate) {
this.delegate = delegate;
}
@Override
public void write(int arg0) throws IOException {
delegate.write(arg0);
}
@Override
public void close() throws IOException {
this.delegate.flush();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
@Override
public void flush() throws IOException {
delegate.flush();
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public String toString() {
return delegate.toString();
}
@Override
public void write(byte[] arg0, int arg1, int arg2) throws IOException {
delegate.write(arg0, arg1, arg2);
}
@Override
public void write(byte[] b) throws IOException {
delegate.write(b);
}
}
}