All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.metaeffekt.artifact.analysis.utils.InventoryUtils Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2021-2024 the original author or authors.
 *
 * Licensed 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 com.metaeffekt.artifact.analysis.utils;

import com.metaeffekt.artifact.analysis.metascan.Constants;
import com.metaeffekt.artifact.terms.TermsMetaDataResolver;
import com.metaeffekt.artifact.terms.model.NormalizationMetaData;
import com.metaeffekt.artifact.terms.model.TermsMetaData;
import com.metaeffekt.resource.InventoryResource;
import org.apache.commons.lang.StringEscapeUtils;
import org.metaeffekt.core.inventory.processor.model.*;
import org.metaeffekt.core.inventory.processor.report.InventoryReport;
import org.metaeffekt.core.inventory.processor.report.ReportContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static com.metaeffekt.artifact.analysis.metascan.Constants.*;
import static org.metaeffekt.core.inventory.processor.model.Constants.DELIMITER_NEWLINE;

public class InventoryUtils extends org.metaeffekt.core.inventory.InventoryUtils {

    private static final Logger LOG = LoggerFactory.getLogger(InventoryUtils.class);

    public static final String INVENTORY_KEY_LICENSE_DIFF = "License / Derived Src/Bin License Diff";

    public static final String INVENTORY_KEY_LICENSE_DIFF_SRC_BIN = "Src/Bin Derived Licenses Diff";

    public static NormalizationMetaData NORMALIZATION_META_DATA;

    public static void initialize(NormalizationMetaData normalizationMetaData) {
        NORMALIZATION_META_DATA = normalizationMetaData;
    }

    public static NormalizationMetaData getNormalizationMetaData() {
        if (NORMALIZATION_META_DATA == null) {
            synchronized (InventoryUtils.class) {
                LOG.warn("Implicitly discovering TermsMetaData. Please consider explicitly initializing.");
                NORMALIZATION_META_DATA = TermsMetaDataResolver.get();
            }
        }
        return NORMALIZATION_META_DATA;
    }

    /**
     * Creates a License Diff column. Compare the columns License with KEY_DERIVED_LICENSES and KEY_DERIVED_LICENSES__SOURCE.
     *
     * @param inventory The inventory do create the column from and in.
     */
    public static void diffDerivedLicenses(Inventory inventory) {
        // add license diff columns (artifacts)
        if (inventory == null) return;

        for (Artifact artifact : inventory.getArtifacts()) {
            String referenceLicense = artifact.getLicense();
            if (StringUtils.isEmpty(referenceLicense)) {
                referenceLicense = artifact.getLicense();
            }

            // FIXME: currently only comparing Binary Artifact and Source Artifact Licenses
            String derivedLicenses = artifact.get(KEY_BINARY_ARTIFACT_DERIVED_LICENSES);
            final String sourceDerivedLicenses = artifact.get(KEY_SOURCE_ARTIFACT_DERIVED_LICENSES);
            if (!StringUtils.isEmpty(sourceDerivedLicenses)) {
                if (StringUtils.isEmpty(derivedLicenses)) {
                    derivedLicenses = sourceDerivedLicenses;
                } else {
                    derivedLicenses = derivedLicenses + ", " + sourceDerivedLicenses;
                }
            }
            String licenseDiff = diffLicenses(derivedLicenses, referenceLicense, true);
            artifact.set(INVENTORY_KEY_LICENSE_DIFF, licenseDiff);
        }
    }

    public static String diffLicenses(String lhsLicense, String rhsLicense, boolean commaSeparatorOnly) {
        lhsLicense = normalizeLicenseOrderAndName(lhsLicense, true, commaSeparatorOnly);
        rhsLicense = normalizeLicenseOrderAndName(rhsLicense, true, commaSeparatorOnly);

        List licenseTokens = tokenizeLicense(lhsLicense, true, commaSeparatorOnly);

        // do not generate a diff, when there is no curated data
        if (licenseTokens.isEmpty()) {
            return "";
        }

        List derivedLicenseTokens = tokenizeLicense(rhsLicense, true, commaSeparatorOnly);

        List addedTokens = new ArrayList<>();
        List removedTokens = new ArrayList<>();

        for (String s : licenseTokens) {
            if (!derivedLicenseTokens.contains(s)) {
                removedTokens.add(s + " (r)");
            }
        }

        for (String s : derivedLicenseTokens) {
            if (!licenseTokens.contains(s)) {
                addedTokens.add(s + " (a)");
            }
        }

        List diffTokens = new ArrayList<>();
        diffTokens.addAll(removedTokens);
        diffTokens.addAll(addedTokens);

        return String.join(", ", diffTokens);
    }

    public static String normalizeLicenseOrderAndName(String license, boolean reorder, boolean commaSeparatorOnly) {
        if (license == null) return "";
        final List licenses = tokenizeLicense(license, reorder, commaSeparatorOnly);
        return joinLicenses(licenses);
    }

    public static void createReportFiles(Inventory inventory, File workDir, String inventoryId, File referenceInventoryDir) {

        InventoryReport report = new InventoryReport();

        report.setReportContext(new ReportContext(inventoryId, null, inventoryId));

        report.setFailOnUnknown(false);
        report.setFailOnUnknownVersion(false);

        // repository and global inventory are the same
        report.setInventory(inventory);
        report.setReferenceInventoryDir(referenceInventoryDir);
        report.setReferenceInventoryIncludes("**/*-inventory.xls");
        report.setReferenceComponentPath(new File(referenceInventoryDir, "components").getAbsolutePath());
        report.setReferenceLicensePath(new File(referenceInventoryDir, "licenses").getAbsolutePath());

        FileUtils.forceMkDirQuietly(workDir);

        report.setFailOnBanned(false);
        report.setFailOnDevelopment(false);
        report.setFailOnDowngrade(false);
        report.setFailOnError(false);
        report.setFailOnInternal(false);
        report.setFailOnMissingLicense(false);
        report.setFailOnMissingLicenseFile(false);
        report.setFailOnMissingNotice(false);

        report.setTargetReportDir(workDir);

        try {
            report.createReport();
        } catch (Exception e) {
            LOG.warn(e.getMessage(), e);
        }
    }

    public static String joinLicenses(Collection licenses) {
        return licenses.stream().sorted(String::compareToIgnoreCase).collect(Collectors.joining(", "));
    }

    public static String joinOredLicenses(Collection licenses) {
        return licenses.stream().sorted(String::compareToIgnoreCase).collect(Collectors.joining(" + "));
    }

    public static void normalize(List licenses) {
        Collections.sort(licenses, String.CASE_INSENSITIVE_ORDER);
    }

    public static void removeMarkers(Collection licenseList, NormalizationMetaData normalizationMetaData) {
        for (String license : new ArrayList<>(licenseList)) {
            TermsMetaData termsMetaData = normalizationMetaData.findTermsMetaData(license);
            if (termsMetaData != null && termsMetaData.isMarker()) {
                licenseList.remove(license);
            }
        }
    }

    public static List extractMarkers(Collection licenseList, NormalizationMetaData normalizationMetaData) {
        final List markers = new ArrayList<>();
        for (String license : new ArrayList<>(licenseList)) {
            final TermsMetaData termsMetaData = normalizationMetaData.findTermsMetaData(license);
            if (termsMetaData != null && termsMetaData.isMarker()) {
                markers.add(license);
            }
        }
        return markers;
    }

    public static void removeUnspecific(Collection licenseList, NormalizationMetaData normalizationMetaData) {
        List del = new ArrayList<>();
        for (String license : licenseList) {
            TermsMetaData lmd = normalizationMetaData.getTermsMetaData(license);
            if (lmd != null) {
                if (lmd.isUnspecific()) {
                    del.add(license);
                }
            }
        }
        licenseList.removeAll(del);
    }

    /**
     * Currently naiive implementation to remove redundant licenses. Should use License Metadata.
     *
     * @param derivedLicenses The derived licenses.
     *
     * @return Removable licenses that are covered by more specific licenses in the set.
     */
    public static Set collectCoveredRemovableLicenses(Set derivedLicenses) {
        final Set removableLicenses = new HashSet<>();
        for (String l : derivedLicenses) {
            if (l.endsWith(" (undefined)")) {
                String lm = l.substring(0, l.indexOf(" (undefined)"));
                for (String r : derivedLicenses) {
                    if (!r.equalsIgnoreCase(l) && r.startsWith(lm)) {
                        removableLicenses.add(l);
                    }
                }
            } else if (l.equalsIgnoreCase("BSD alike")) {
                for (String r : derivedLicenses) {
                    if (!r.equalsIgnoreCase(l) && r.startsWith("BSD ") && r.endsWith("-Clause License")) {
                        removableLicenses.add(l);
                    }
                }
            } else if (l.equalsIgnoreCase("MIT alike")) {
                for (String r : derivedLicenses) {
                    if (!r.equalsIgnoreCase(l) && r.equals("MIT License")) {
                        removableLicenses.add(l);
                    }
                }
            }
        }
        return removableLicenses;
    }

    @Deprecated // revise
    public static void diffBinaryAndSourceDerivedLicenses(Inventory inventory) {
        for (Artifact a : inventory.getArtifacts()) {

            String derivedLicenseSource = a.get(KEY_SOURCE_ARTIFACT_DERIVED_LICENSES);
            String derivedLicenseBinary = a.get(KEY_BINARY_ARTIFACT_DERIVED_LICENSES);

            derivedLicenseSource = derivedLicenseSource == null ? "" : derivedLicenseSource.trim();
            derivedLicenseBinary = derivedLicenseBinary == null ? "" : derivedLicenseBinary.trim();

            if (derivedLicenseSource.equalsIgnoreCase(derivedLicenseBinary)) {
                a.set(INVENTORY_KEY_LICENSE_DIFF_SRC_BIN, "-");
            } else {
                List tokenizedDerivedBinary = InventoryUtils.tokenizeLicense(derivedLicenseBinary, true, true);
                List tokenizedDerivedSource = InventoryUtils.tokenizeLicense(derivedLicenseSource, true, true);

                List tmp = new ArrayList<>(tokenizedDerivedBinary);
                tokenizedDerivedBinary.removeAll(tokenizedDerivedSource);
                tokenizedDerivedSource.removeAll(tmp);

                List filteredMappedDerivedSource = tokenizedDerivedSource.stream().map(s -> "src: " + s).collect(Collectors.toList());
                List filteredMappedDerivedBinary = tokenizedDerivedBinary.stream().map(s -> "bin: " + s).collect(Collectors.toList());

                List diff = new ArrayList<>(filteredMappedDerivedSource);
                diff.addAll(filteredMappedDerivedBinary);

                Collections.sort(diff);
                a.set(INVENTORY_KEY_LICENSE_DIFF_SRC_BIN, String.join("\n", diff));
            }
        }
    }

    public static String deriveEffectiveLicenses(final String license) {
        if (StringUtils.isEmpty(license)) {
            return "";
        }
        String effectiveLicense = license.replace(",", "|").replace("| ", "|");

        // apply effective license rules
        List licenses = new ArrayList<>(Arrays.asList(effectiveLicense.split("\\|")));

        return deriveEffectiveLicenses(licenses);
    }

    public static String deriveEffectiveLicenses(Collection associatedLicenses) {
        List licenses = new ArrayList<>(associatedLicenses);
        licenses.remove(MARKER_LICENSING_OPTION);

        // FIXME: include generic step that eliminates undefined
        // FIXME: (remove duplicates before applying mappings)

        String gplUndefined = "GNU General Public License (undefined)";
        String gpl3OrLater = "GNU General Public License 3.0 (or any later version)";
        String gpl2OrLater = "GNU General Public License 2.0 (or any later version)";
        String gpl1OrLater = "GNU General Public License 1.0 (or any later version)";
        String gpl3 = "GNU General Public License 3.0";
        String gpl2 = "GNU General Public License 2.0";
        String gpl1 = "GNU General Public License 1.0";

        String lgplUndefined = "GNU Lesser General Public License (undefined)";
        String lgpl3OrLater = "GNU Lesser General Public License 3.0 (or any later version)";
        String lgpl21OrLater = "GNU Lesser General Public License 2.1 (or any later version)";
        String lgpl2OrLater = "GNU Library General Public License 2.0 (or any later version)";
        String lgpl2 = "GNU Library General Public License 2.0";
        String lgpl3 = "GNU Lesser General Public License 3.0";
        String lgpl21 = "GNU Lesser General Public License 2.1";

        {
            if (licenses.contains(gplUndefined)) {
                if (licenses.contains(gpl3) || licenses.contains(gpl3OrLater)) {
                    replace(licenses, gplUndefined, gpl3);
                } else if (licenses.contains(lgpl2) || licenses.contains(lgpl2OrLater)) {
                    replace(licenses, gplUndefined, gpl2);
                } else if (licenses.contains(gpl2) || licenses.contains(gpl2OrLater)) {
                    replace(licenses, gplUndefined, gpl2);
                } else if (licenses.contains(lgpl21) || licenses.contains(lgpl21OrLater)) {
                    replace(licenses, gplUndefined, gpl2);
                } else if (licenses.contains(lgpl3) || licenses.contains(lgpl3OrLater)) {
                    replace(licenses, gplUndefined, gpl3);
                }
                replace(licenses, gplUndefined, gpl3);
            }
            if (licenses.contains(gpl3OrLater) || licenses.contains(gpl3)) {
                licenses.remove(gpl2OrLater);
                licenses.remove(gpl1OrLater);
                replace(licenses, gpl3OrLater, gpl3);
            }
            if (licenses.contains(gpl2OrLater) || licenses.contains(gpl2)) {
                licenses.remove(gpl1OrLater);
                replace(licenses, gpl2OrLater, gpl2);
            }
            if (licenses.contains(gpl1OrLater)) {
                replace(licenses, gpl1OrLater, gpl1);
            }
        }

        {
            if (licenses.contains(lgplUndefined)) {
                if (licenses.contains(lgpl21) || licenses.contains(lgpl21OrLater)) {
                    if (licenses.contains(lgpl21)) {
                        replace(licenses, lgplUndefined, lgpl21);
                    } else {
                        replace(licenses, lgplUndefined, lgpl21OrLater);
                    }
                } else {
                    if (licenses.contains(gpl3)) {
                        replace(licenses, lgplUndefined, lgpl3);
                    } else {
                        if (licenses.contains(gpl2)) {
                            replace(licenses, lgplUndefined, lgpl21);
                        } else {
                            replace(licenses, lgplUndefined, lgpl3);
                        }
                    }
                }
            }
            // 3.0 or later --> 3.0
            if (licenses.contains(lgpl3OrLater) || licenses.contains(lgpl3)) {
                licenses.remove(lgpl21OrLater);
                licenses.remove(lgpl2OrLater);
                replace(licenses, lgpl3OrLater, lgpl3);
            }
            // 2.1 or later + GPL 3 --> 3.0
            if (licenses.contains(lgpl21OrLater) && licenses.contains(gpl3)) {
                licenses.remove(lgpl2OrLater);
                replace(licenses, lgpl21OrLater, lgpl3);
            }
            // 2.1 or later + GPL 2 --> 2.1
            if (licenses.contains(lgpl21OrLater) && licenses.contains(gpl2)) {
                licenses.remove(lgpl2OrLater);
                replace(licenses, lgpl21OrLater, lgpl21);
            }
            // 2.1 or later --> 2.1
            if (licenses.contains(lgpl21OrLater) || licenses.contains(lgpl21)) {
                licenses.remove(lgpl2OrLater);
                replace(licenses, lgpl21OrLater, lgpl21);
            }
            // 2.1 or later & GPL 3  --> 3.0
            if (licenses.contains(lgpl2OrLater) && licenses.contains(gpl3)) {
                replace(licenses, lgpl2OrLater, lgpl3);
            }
            // 2.1 or later & GPL 2  --> 2.1
            if (licenses.contains(lgpl2OrLater) && licenses.contains(gpl2)) {
                replace(licenses, lgpl2OrLater, lgpl21);
            }
            // 2.0 or later --> 2.1
            if (licenses.contains(lgpl2OrLater)) {
                replace(licenses, lgpl2OrLater, lgpl21);
            }
        }

        {
            String gfdlUndefined = "GNU Free Documentation License (undefined)";
            String gfdl13OrLater = "GNU Free Documentation License 1.3 (or any later version)";
            String gfdl12OrLater = "GNU Free Documentation License 1.2 (or any later version)";
            String gfdl11OrLater = "GNU Free Documentation License 1.1 (or any later version)";
            String gfdl13 = "GNU Free Documentation License 1.3";
            String gfdl12 = "GNU Free Documentation License 1.2";
            String gfdl11 = "GNU Free Documentation License 1.1";

            if (licenses.contains(gfdlUndefined)) {
                replace(licenses, gfdlUndefined, gfdl13);
            }
            if (licenses.contains(gfdl13OrLater) || licenses.contains(gfdl13)) {
                licenses.remove(gfdl12OrLater);
                licenses.remove(gfdl11OrLater);
                replace(licenses, gfdl13OrLater, gfdl13);
            }
            if (licenses.contains(gfdl12OrLater) || licenses.contains(gfdl12)) {
                licenses.remove(gfdl12OrLater);
                replace(licenses, gfdl12OrLater, gfdl12);
            }
            if (licenses.contains(gfdl11OrLater)) {
                replace(licenses, gfdl11OrLater, gfdl11);
            }
        }

        {
            String lpplUndefined = "LaTeX Project Public License (undefined)";
            String lppl12OrLater = "LaTeX Project Public License 1.2 (or any later version)";
            String lppl11OrLater = "LaTeX Project Public License 1.1 (or any later version)";
            String lppl10OrLater = "LaTeX Project Public License 1.0 (or any later version)";
            String lppl12 = "LaTeX Project Public License 1.2";
            String lppl11 = "LaTeX Project Public License 1.1";
            String lppl10 = "LaTeX Project Public License 1.0";

            if (licenses.contains(lpplUndefined)) {
                replace(licenses, lpplUndefined, lppl12);
            }
            if (licenses.contains(lppl12OrLater) || licenses.contains(lppl12)) {
                licenses.remove(lppl11OrLater);
                licenses.remove(lppl10OrLater);
                replace(licenses, lppl12OrLater, lppl12);
            }
            if (licenses.contains(lppl11OrLater) || licenses.contains(lppl11)) {
                licenses.remove(lppl10OrLater);
                replace(licenses, lppl11OrLater, lppl11);
            }
            if (licenses.contains(lppl10OrLater)) {
                replace(licenses, lppl10OrLater, lppl10);
            }
        }

        {
            String beopen = "BeOpen Python Open Source License Agreement 1.0";
            String cnri = "CNRI License Agreement";
            String permission = "Permission Terms (no advertising; no warranty; no liability)"; // CWI permission
            String phyton = "Python Software Foundation License 2.0";

            if (licenses.contains(beopen) && licenses.contains(cnri) && licenses.contains(permission) && licenses.contains(phyton)) {
                licenses.remove(beopen);
                licenses.remove(cnri);
                licenses.remove(permission);
            }
        }

        {
            String bsdUndefined = "BSD (undefined)";
            String bsd3Clause = "BSD 3-Clause License";

            if (licenses.contains(bsdUndefined)) {
                licenses.remove(bsdUndefined);
                licenses.add(bsd3Clause);
            }
        }

        {
            String apacheLicenseUndefined = "Apache License (undefined)";
            String apacheLicense20 = "Apache License 2.0";

            if (licenses.contains(apacheLicenseUndefined)) {
                licenses.remove(apacheLicenseUndefined);
                licenses.add(apacheLicense20);
            }
        }

        {
            String licenseUndefined = "Mozilla Public License (undefined)";
            String license20 = "Mozilla Public License 2.0";

            if (licenses.contains(licenseUndefined)) {
                licenses.remove(licenseUndefined);
                licenses.add(license20);
            }
        }

        {
            String cddlUndefined = "CDDL (undefined)";
            String cddl10 = "CDDL 1.0";
            String cddl11 = "CDDL 1.1";

            // remove unspecific if specific are available
            if (licenses.contains(cddl10) && licenses.contains(cddl11)) {
                licenses.remove(cddlUndefined);
            }
            // replace unspecific with 1.0
            if (licenses.contains(cddlUndefined)) {
                replace(licenses, cddlUndefined, cddl10);
            }
        }


        for (String l : new ArrayList<>(licenses)) {
            replaceLicense(licenses, l, "FreeType Project License + GNU General Public License 2.0 (or any later version)", "FreeType Project License");
            replaceLicense(licenses, l, "GNU Library General Public License 2.0 (or any later version) + GNU General Public License 2.0 (or any later version) + Mozilla Public License (undefined)", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "Netscape + GNU General Public License 2.0 (or any later version) + GNU Library General Public License 2.0 (or any later version)", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "BSD 3-Clause License (variant 003) + GNU General Public License (undefined)", "BSD 3-Clause License (variant 003)");
            replaceLicense(licenses, l, "BSD 3-Clause License (variant 003) + GNU General Public License 2.0", "BSD 3-Clause License (variant 003)");
            replaceLicense(licenses, l, "GNU Lesser General Public License 2.1 (or any later version), GNU Library General Public License 2.0 (or any later version) + GNU General Public License 2.0 (or any later version) + Mozilla Public License (undefined)", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "Netscape + GNU General Public License 2.0 (or any later version) + GNU Library General Public License 2.0 (or any later version)", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "Apache License 2.0 + CC0 Universal 1.0", "Apache License 2.0");
            replaceLicense(licenses, l, "Mozilla Public License 2.0 + GNU General Public License 2.0 (or any later version)", "Mozilla Public License 2.0");
            replaceLicense(licenses, l, "Mozilla Public License 1.1 + GNU Lesser General Public License 2.1 (or any later version) + GNU General Public License 2.0 (or any later version)", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + Academic Free License 2.1", "GNU General Public License 2.0");

            replaceLicense(licenses, l, "Apache License 2.0 + MIT License", "Apache License 2.0");

            replaceLicense(licenses, l, "GNU General Public License 3.0 (or any later version) + MIT License", "MIT License");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + Academic Free License 2.0", "GNU General Public License 2.0");

            // xx
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + GNU Lesser General Public License 3.0 (or any later version)", "GNU Lesser General Public License 3.0");
            replaceLicense(licenses, l, "Mozilla Public License 1.1 + GNU General Public License 2.0 + GNU Lesser General Public License 2.1", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "GNU Lesser General Public License 3.0 + GNU General Public License 2.0", "GNU Lesser General Public License 3.0");

            replaceLicense(licenses, l, "GNU General Public License 2.0 + GNU Lesser General Public License 2.1", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "GNU Lesser General Public License 2.1 + Mozilla Public License 2.0 + GNU General Public License 2.0", " GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "MIT License + GNU Genral Public License 3.0", "MIT License");
            replaceLicense(licenses, l, "MIT License + GNU General Public License 3.0", "MIT License");
            replaceLicense(licenses, l, "GNU General Public License 2.0 + GNU Lesser General Public License 3.0", "GNU Lesser General Public License 3.0");
            replaceLicense(licenses, l, "Mozilla Public License 1.1 + GNU General Public License 2.0 + GNU Lesser General Public License 2.1", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "MIT License + GNU General Public License 3.0", "MIT License");
            replaceLicense(licenses, l, "GNU General Public License 2.0 + GNU Lesser General Public License 3.0", "GNU Lesser General Public License 3.0");
            replaceLicense(licenses, l, "MIT License + GNU General Public License 3.0", "MIT License");
            replaceLicense(licenses, l, "MIT License + GNU General Public License 3.0 (or any later version)", "MIT License");
            replaceLicense(licenses, l, "Apache License 2.0 + CC0 Universal (undefined)", "Apache License 2.0");
            replaceLicense(licenses, l, "Mozilla Public License 1.1 + GNU General Public License 2.0 (or any later version) + GNU Lesser General Public License 2.1 (or any later version)", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "GNU Lesser General Public License 3.0 (or any later version) + GNU General Public License 2.0 (or any later version)", "GNU Lesser General Public License 3.0");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + GNU Lesser General Public License 2.1 (or any later version)", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "GNU Lesser General Public License 2.1 (or any later version) + Mozilla Public License 2.0 + GNU General Public License 2.0 (or any later version)", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "GNU General Public License 1.0 (or any later version) + Artistic License (undefined)", "GNU General Public License 2.0");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + X11 License", "X11 License");
            replaceLicense(licenses, l, "GNU General Public License 2.0 + GNU Free Documentation License 1.1 (or any later version)", "GNU General Public License 2.0");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + Transitive Grace Period Public Licence 1.0 (or any later version)", "GNU General Public License 2.0");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + GNU Lesser General Public License  3.0 (or any later version)", "GNU Lesser General Public License  3.0");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + GNU Lesser General Public License version 3.0 (or any later version)", "GNU Lesser General Public License 3.0");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + GNU Lesser General Public License 3.0 (or any later version)", "GNU Lesser General Public License 3.0");
            replaceLicense(licenses, l, "MIT License + GNU Genral Public License 3.0 (or any later version)", "MIT License");
            replaceLicense(licenses, l, "MIT License + GNU General Public License 3.0 (or any later version)", "MIT License");
            replaceLicense(licenses, l, "MIT License + GNU General Public License 2.0 (or any later version)", "MIT License");
            replaceLicense(licenses, l, "MIT License + GNU General Public License 2.0", "MIT License");
            replaceLicense(licenses, l, "GNU Lesser General Public License (undefined)", "GNU Lesser General Public License 2.1");

            // review with TLS
            replaceLicense(licenses, l, "Artistic License (undefined) + GNU General Public License 1.0", "Artistic License 1.0");
            replaceLicense(licenses, l, "Artistic License 1.0 + GNU General Public License 1.0", "Artistic License 1.0");
            replaceLicense(licenses, l, "GNU Lesser General Public License 3.0 + Creative Commons BY-SA 3.0", "GNU Lesser General Public License 3.0");
            replaceLicense(licenses, l, "Frontier Artistic License (undefined) + GNU General Public License (undefined)", "Frontier Artistic License 1.0");
            replaceLicense(licenses, l, "Frontier Artistic License (undefined) + GNU General Public License 1.0", "Frontier Artistic License 1.0");
            replaceLicense(licenses, l, "Frontier Artistic License 1.0 + GNU General Public License (undefined)", "Frontier Artistic License 1.0");
            replaceLicense(licenses, l, "Frontier Artistic License 1.0 + GNU General Public License 3.0", "Frontier Artistic License 1.0");
            replaceLicense(licenses, l, "Frontier Artistic License 1.0 + GNU General Public License 2.0", "Frontier Artistic License 1.0");
            replaceLicense(licenses, l, "Frontier Artistic License 1.0 + GNU General Public License 1.0", "Frontier Artistic License 1.0");
            replaceLicense(licenses, l, "GNU General Public License 1.0 + Artistic License (undefined)", "Frontier Artistic License 1.0");
            replaceLicense(licenses, l, "Artistic License (undefined) + GNU General Public License 1.0 (or any later version)", "Artistic License 1.0");
            replaceLicense(licenses, l, "GNU Lesser General Public License 2.1 + Mozilla Public License 1.1", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "Artistic License 1.0 + GNU General Public License 1.0", "Artistic License 1.0");
            replaceLicense(licenses, l, "GNU General Public License 2.0 + GNU Lesser General Public License 2.1 + Mozilla Public License 1.1", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "HSIEH-DERIVATIVE + HSIEH-BSD + GNU Lesser General Public License 2.1", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + Artistic License 1.0 (Perl)", "Artistic License 1.0 (Perl)");
            replaceLicense(licenses, l, "GNU General Public License 1.0 (or any later version) + Artistic License 1.0 (Perl)", "Artistic License 1.0 (Perl)");
            replaceLicense(licenses, l, "GNU General Public License 1.0 + Artistic License 1.0 (Perl)", "Artistic License 1.0 (Perl)");
            replaceLicense(licenses, l, "Expat License + GNU General Public License 1.0 + Artistic License 1.0 (Perl)", "Expat License");
            replaceLicense(licenses, l, "Expat License + GNU General Public License 1.0 (or any later version) + Artistic License 1.0 (Perl)", "Expat License");

            replaceLicense(licenses, l, "GNU General Public License 3.0 (or any later version) + GNU Free Documentation License 1.2 (or any later version)", "GNU General Public License 3.0 (or any later version)");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + FreeType Project License", "GNU General Public License 2.0 (or any later version)");

            replaceLicense(licenses, l, "Mozilla Public License 1.1 + GNU General Public License 2.0 + Apache License 2.0", "Apache License 2.0");
            replaceLicense(licenses, l, "CDDL 1.1 + GPL 2.0 with classpath exception", "CDDL 1.1");
            replaceLicense(licenses, l, "CDDL 1.1 + GNU General License 2.0", "CDDL 1.1");
            replaceLicense(licenses, l, "CDDL 1.1 + GNU General Public License 2.0", "CDDL 1.1");
            replaceLicense(licenses, l, "CDDL 1.0 + GNU General Public License 2.0", "CDDL 1.0");
            replaceLicense(licenses, l, "Apache License 2.0 + GNU General Public License 3.0 (or any later version)", "Apache License 2.0");
            replaceLicense(licenses, l, "BSD 3-Clause License + GNU General Public License 3.0 (or any later version)", "BSD 3-Clause License");
            replaceLicense(licenses, l, "BSD 3-Clause License (variant 013) + GNU General Public License (undefined)", "BSD 3-Clause License (variant 013)");

            replaceLicense(licenses, l, "CDDL 1.0 + GNU General Public License 2.0 (with classpath exception)", "CDDL 1.0");
            replaceLicense(licenses, l, "CDDL 1.0 + GNU General Public License 2.0 (with Oracle classpath exception)", "CDDL 1.0");
            replaceLicense(licenses, l, "CDDL 1.1 + GNU General Public License 2.0 (with Oracle classpath exception)", "CDDL 1.1");
            replaceLicense(licenses, l, "CDDL 1.1 + GNU General Public License 2.0 (with classpath exception)", "CDDL 1.1");
            replaceLicense(licenses, l, "CDDL 1.0 + GNU General Public License 2.0 (with Sun classpath exception)", "CDDL 1.0");

            replaceLicense(licenses, l, "CDDL 1.1 + GPL 2 (with classpath exception)", "CDDL 1.1");
            replaceLicense(licenses, l, "CDDL 1.0 + GPL 2 (with classpath exception)", "CDDL 1.0");
            replaceLicense(licenses, l, "CDDL 1.1 + GPL 2 with classpath exception", "CDDL 1.1");
            replaceLicense(licenses, l, "CDDL 1.0 + GPL 2 with classpath exception", "CDDL 1.0");

            replaceLicense(licenses, l, "Apache License 2.0 + GNU Lesser General Public License 2.1 (or any later version)", "Apache License 2.0");
            replaceLicense(licenses, l, "Apache License 2.0 + GNU Lesser General Public License 3.0 (or any later version)", "Apache License 2.0");

            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + Mozilla Public License 1.1", "Mozilla Public License 1.1");
            replaceLicense(licenses, l, "Eclipse Public License 1.0 + Mozilla Public License 2.0", "Eclipse Public License 1.0");
            replaceLicense(licenses, l, "Eclipse Public License 1.0 + GNU Lesser General Public License 2.1", "Eclipse Public License 1.0");

            replaceLicense(licenses, l, "GNU Lesser General Public License 2.1 + Creative Commons BY-SA 4.0", "GNU Lesser General Public License 2.1");

            replaceLicense(licenses, l, "Eclipse Public License 2.0 + GNU General Public License 2.0 (with classpath exception)", "Eclipse Public License 2.0");
            replaceLicense(licenses, l, "Eclipse Public License 1.0 + GNU General Public License 2.0 (with classpath exception)", "Eclipse Public License 1.0");

            replaceLicense(licenses, l, "GNU Lesser General Public License 2.1 (or any later version)", "GNU Lesser General Public License 2.1");

            replaceLicense(licenses, l, "Eclipse Public License 1.0 + Apache License 2.0", "Apache License 2.0");

            replaceLicense(licenses, l, "GNU General Public License (undefined) + Frontier Artistic License", "Frontier Artistic License 1.0");
            replaceLicense(licenses, l, "Frontier Artistic License", "Frontier Artistic License 1.0");

            replaceLicense(licenses, l, "MIT License + NCSA License", "MIT License");
            replaceLicense(licenses, l, "Public Domain + BSD alike", "Public Domain");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + gSOAP Public License + Genivia's Commercial License", "gSOAP Public License");

            replaceLicense(licenses, l, "Mozilla Public License 1.1 + GNU Library General Public License 2.0 (or any later version)", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "Mozilla Public License 1.1 + GNU Library General Public License 2.0 (or any later version)", "GNU Lesser General Public License 2.1");

            replaceLicense(licenses, l, "Eclipse Public License 1.0 + Mozilla Public License 2.0", "Eclipse Public License 2.0");
            replaceLicense(licenses, l, "Eclipse Public License 1.0 + GNU Lesser General Public License 3.0 (or any later version) + Mozilla Public License 2.0", "Eclipse Public License 2.0");
            replaceLicense(licenses, l, "Apache License 2.0 + Mozilla Public License 1.1 + GNU Lesser General Public License 2.1 (or any later version)", "Apache License 2.0");
            replaceLicense(licenses, l, "Mozilla Public License 1.1 + GNU Lesser General Public License 2.1 (or any later version) + Apache License 2.0", "Apache License 2.0");
            replaceLicense(licenses, l, "GNU General Public License 2.0 + GNU General Public License 2.0 (with classpath exception)", "GNU General Public License 2.0 (with classpath exception)");

            replaceLicense(licenses, l, "Apache License 2.0 + GNU Lesser General Public License 3.0", "Apache License 2.0");
            replaceLicense(licenses, l, "CDDL 1.1 + GNU General Public License 2.0 (with classpath exception)", "CDDL 1.1");
            replaceLicense(licenses, l, "BSD 3-Clause License (copyright variant) + GNU General Public License (undefined)", "BSD 3-Clause License (copyright holder variant)");
            replaceLicense(licenses, l, "BSD 3-Clause License (copyright holder variant) + GNU General Public License (undefined)", "BSD 3-Clause License (copyright holder  variant)");

            replaceLicense(licenses, l, "GNU Lesser General Public License 2.1  + Eclipse Public License 1.0", "GNU Lesser General Public License 2.1");

            replaceLicense(licenses, l, "CDDL (undefined) + GNU General Public License 2.0 (with classpath exception), CDDL 1.0", "CDDL 1.0");
            replaceLicense(licenses, l, "Apache License 2.0 + GNU Lesser General Public License 2.1 (or any later version) + Mozilla Public License 1.1", "Apache License 2.0");
            replaceLicense(licenses, l, "Academic Free License 2.1 + GNU Library General Public License 2.0", "GNU Library General Public License 2.0");

            replaceLicense(licenses, l, "GNU General Public License 1.0 (or any later version) + MIT License", "MIT License");
            replaceLicense(licenses, l, "CDDL 1.0 + GNU General Public License 2.0 (with classpath exception 2.0)", "CDDL 1.0");
            replaceLicense(licenses, l, "CDDL (undefined) + GNU General Public License 2.0 (with classpath exception 2.0)", "CDDL 1.0");
            replaceLicense(licenses, l, "BSD 2-Clause License (copyright holder variant) + GNU General Public License (undefined) 002", "BSD 2-Clause License (copyright holder variant)");
            replaceLicense(licenses, l, "GNU General Public License (undefined) 002", "GNU General Public License (undefined)");
            replaceLicense(licenses, l, "GNU General Public License 3.0 002", "GNU General Public License 3.0");

            replaceLicense(licenses, l, "Eclipse Public License 1.0 + GNU Lesser General Public License 2.1", "Eclipse Public License 1.0");
            replaceLicense(licenses, l, "MIT License + CC0 Universal 1.0", "MIT License");
            replaceLicense(licenses, l, "Eclipse Public License 2.0 + GNU General Public License 2.0 (with classpath exception)", "Eclipse Public License 2.0");
            replaceLicense(licenses, l, "Eclipse Public License 2.0 + GNU General Public License 2.0 (with classpath exception)", "Eclipse Public License 2.0");
            replaceLicense(licenses, l, "Eclipse Public License 2.0 + GNU General Public License 2.0", "Eclipse Public License 2.0");
            replaceLicense(licenses, l, "CDDL 1.1 + GNU General Public License 2.0 (with classpath exception 2.0)", "CDDL 1.1");

            replaceLicense(licenses, l, "GNU Lesser General Public License 3.0 (or any later version) + GNU General Public License 2.0 (or any later version)", "GNU Lesser General Public License 3.0");
            replaceLicense(licenses, l, "GNU General Public License 1.0 + Artistic License (undefined)", "GNU General Public License 1.0");
            replaceLicense(licenses, l, "BSD 3-Clause License (variant 003) + GNU General Public License (undefined)", "BSD 3-Clause License (variant 003)");

            replaceLicense(licenses, l, "Apache License 2.0 + GNU General Public License 2.0", "Apache License 2.0");
            replaceLicense(licenses, l, "GNU Lesser General Public License 2.1 + Eclipse Public License 2.0", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "Apache License 1.1 + Apache License 2.0 + BSD (undefined) + Public Domain + Indiana University Extreme! Lab Software License 1.1.1", "Apache License 2.0");
            replaceLicense(licenses, l, "LGPLv2 + with exceptions", "GNU Library General Public License 2.0");
            replaceLicense(licenses, l, "GPLv2 + with exceptions", "GNU General Public License 2.0 (or any later version; with exceptions)");
            replaceLicense(licenses, l, "GPLv3 + with exceptions", "GNU General Public License 3.0 (or any later version; with exceptions)");

            replaceLicense(licenses, l, "CDDL (undefined) + GNU General Public License 2.0 (with classpath exception), CDDL 1.0 + GNU General Public License 2.0 (with classpath exception), GNU General Public License 2.0 (with classpath exception)", "CDDL 1.0 + GNU General Public License 2.0 (with classpath exception)");

            replaceLicense(licenses, l, "GNU General Public License 1.0 (or any later version) + Artistic License 1.0", "GNU General Public License 2.0");
            replaceLicense(licenses, l, "GNU General Public License 1.0 (or any later version) + Artistic License (undefined)", "GNU General Public License 2.0");
            replaceLicense(licenses, l, "GNU General Public License (undefined) + Artistic License (undefined)", "GNU General Public License (undefined)");
            replaceLicense(licenses, l, "GNU Lesser General Public License 2.1 + Mozilla Public License 1.1 + GNU General Public License 2.0 (or any later version)", "GNU Lesser General Public License 2.1");

            // Red Hat Linux
            replaceLicense(licenses, l, "GNU General Public License (undefined; or any later version) + Artistic License (undefined)", "GNU General Public License 2.0"); // REVIEW
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + Artistic License (undefined)", "GNU General Public License 2.0"); // REVIEW
            replaceLicense(licenses, l, "Artistic License 1.0 (Perl) + GNU General Public License 1.0 (or any later version)", "GNU General Public License 2.0 (or any later version)"); // REVIEW
            replaceLicense(licenses, l, "Artistic License 1.0 + GNU General Public License 1.0 (or any later version)", "GNU General Public License 2.0 (or any later version)"); // REVIEW
            replaceLicense(licenses, l, "BSD 3-Clause License (copyright holder variant) + GNU General Public License 2.0", "BSD 3-Clause License (copyright holder variant)");
            replaceLicense(licenses, l, "GNU General Public License 2.0 + Linux Kernel Variant of OpenIB.org License", "Linux Kernel Variant of OpenIB.org License");
            replaceLicense(licenses, l, "OpenSSL License + Original SSLeay License", "OpenSSL License");
            replaceLicense(licenses, l, "Academic Free License 2.1 + GNU General Public License 2.0 (or any later version)", "Academic Free License 2.1");
            replaceLicense(licenses, l, "Academic Free License 2.1 + GNU General Public License 2.0", "Academic Free License 2.1");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + Open Software License 2.1", "Open Software License 2.1");
            replaceLicense(licenses, l, "BSD 3-Clause License + GNU General Public License 2.0", "BSD 3-Clause License");
            replaceLicense(licenses, l, "MIT License + X11 License", "MIT License");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + GNU General Public License 3.0", "GNU General Public License 2.0");
            replaceLicense(licenses, l, "GNU General Public License 2.0 + GNU Lesser General Public License 3.0 (or any later version)", "GNU General Public License 2.0");
            replaceLicense(licenses, l, "Cavium EULA + Apache License 2.0", "Apache License 2.0");
            replaceLicense(licenses, l, "GNU Lesser General Public License 3.0 (or any later version) + GNU General Public License 2.0", "GNU Lesser General Public License 3.0");
            replaceLicense(licenses, l, "Artistic License (undefined) + GNU Lesser General Public License (undefined)", "GNU Lesser General Public License 2.1");

            replaceLicense(licenses, l, "BSD 3-Clause License + GNU Lesser General Public License 2.1 (or any later version)", "BSD 3-Clause License");
            replaceLicense(licenses, l, "BSD 3-Clause License + GNU General Public License 2.0 (or any later version)", "BSD 3-Clause License");

            // eclipse-bundles / java
            replaceLicense(licenses, l, "FreeType Project License + GNU General Public License 2.0", "FreeType Project License");
            replaceLicense(licenses, l, "Eclipse Public License 1.0 (or any later version) + GNU Lesser General Public License 2.1 (or any later version) + GNU General Public License 2.0 (or any later version) + Apache License 2.0 (or any later version) + BSD 3-Clause License", "Apache License 2.0");
            replaceLicense(licenses, l, "CDDL 1.1 + GNU General Public License 2.0 (with classpath exception)", "CDDL 1.1");

            // 004
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + Mozilla Public License (undefined)", "Mozilla Public License 2.0");
            replaceLicense(licenses, l, "Academic Free License 2.1 + GNU General Public License 2.0 (or any later version)", "Academic Free License 2.1");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + Academic Free License (undefined)", "Academic Free License 2.1");
            replaceLicense(licenses, l, "Apache License 2.0 + Boost Software License 1.0", "Apache License 2.0");

            replaceLicense(licenses, l, "Apache License 2.0 + Eclipse Public License 1.0 + Eclipse Public License 2.0", "Apache License 2.0");

            replaceLicense(licenses, l, "Apache License 2.0 + BSD (undefined)", "Apache License 2.0");
            replaceLicense(licenses, l, "BSD 3-Clause License (Libcap) + GNU General Public License 2.0", "BSD 3-Clause License (Libcap)");
            replaceLicense(licenses, l, "GNU Library General Public License 2.0 (or any later version) + GNU General Public License 2.0 (or any later version) + Mozilla Public License (undefined)", "GNU Lesser General Public License 2.1");
            replaceLicense(licenses, l, "Netscape Public License (undefined) + GNU General Public License 2.0 (or any later version) + GNU Library General Public License 2.0 (or any later version)", "GNU Lesser General Public License 2.1");


            replaceLicense(licenses, l, "GNU General Public License 3.0 (or any later version) + BSD (undefined)", "GNU General Public License 3.0");
            replaceLicense(licenses, l, "GNU General Public License 2.0 + BSD (undefined)", "GNU General Public License 2.0");
            replaceLicense(licenses, l, "BSD (undefined) + GNU General Public License 2.0", "GNU General Public License 2.0");
            replaceLicense(licenses, l, "BSD (undefined) + GNU General Public License 2.0 (or any later version)", "GNU General Public License 2.0");
            replaceLicense(licenses, l, "BSD (undefined) + GNU General Public License (undefined; or any later version)", "GNU General Public License 3.0");

            replaceLicense(licenses, l, "Apache License 2.0 + Eclipse Public License 1.0", "Apache License 2.0");
            replaceLicense(licenses, l, "Eclipse Public License 2.0 + Apache License 2.0", "Apache License 2.0");
            replaceLicense(licenses, l, "CDDL (undefined) + GNU General Public License 2.0 (with classpath exception)", "CDDL 1.0");

            replaceLicense(licenses, l, "BSD 3-Clause License (variant 003) + GNU Library General Public License 2.0 (or any later version)", "BSD 3-Clause License (variant 003)");
            replaceLicense(licenses, l, "BSD 4-Clause License (UC) + Permission Terms (variant 137)", "BSD 4-Clause License (UC)");
            replaceLicense(licenses, l, "BSD 2-Clause License (copyright holder variant) + GNU General Public License 2.0 (or any later version)", "BSD 2-Clause License (copyright holder variant)");
            replaceLicense(licenses, l, "GNU General Public License 2.0 (or any later version) + GNU Lesser General Public License 2.1 (or any later version) + Mozilla Public License 1.1 + BSD 2-Clause License (copyright holder variant)", "BSD 2-Clause License (copyright holder variant)");
            replaceLicense(licenses, l, "OpenSSL License + BSD 3-Clause License (copyright holder variant) + GNU General Public License (undefined)", "BSD 3-Clause License (copyright holder variant)");
            replaceLicense(licenses, l, "OpenSSL License + BSD 3-Clause License (copyright holder variant) + GNU General Public License 2.0", "BSD 3-Clause License (copyright holder variant)");
            replaceLicense(licenses, l, "Public Domain + BSD (undefined)", "Public Domain");

        }

        licenses.stream().filter(l -> l.contains(" + ")).forEach(l -> LOG.info("License [{}] still contains a license option!", l));

        String derived = licenses.stream().distinct().sorted().collect(Collectors.joining("|"));

        // handle undefined / or any later version:
        derived = derived.replace(" (undefined; or any later version)", "(undefined)");

        // handle undefined:
        derived = derived.replace("Artistic License (undefined)", "Artistic License 1.0");
        derived = derived.replace("CC0 Universal (undefined)", "CC0 Universal 1.0");
        derived = derived.replace("CC0 Universal License (undefined)", "CC0 Universal 1.0");
        derived = derived.replace("GNU Lesser General Public License (undefined)", "GNU Lesser General Public License 2.1");
        derived = derived.replace("GNU General Public License (undefined)", "GNU General Public License 2.0");
        derived = derived.replace("Eclipse Public License (undefined)", "Eclipse Public License 2.0");

        // handle EPL 1.0 to EPL 2.0
        derived = derived.replace("Eclipse Public License 1.0", "Eclipse Public License 2.0");

        derived = derived.replace(" (or any later version)", "");
        derived = derived.replace(" (or any later version; ", " (");

        return derived;
    }

    protected static void replaceLicense(List licenses, String l, String dual, String replace) {
        if (l.equals(dual)) {
            if (licenses.remove(dual)) {
                // only if the removal changed something we do the addon
                licenses.add(replace);
            }
        }
    }

    public static void replace(List licenses, String gpl2OrLater, String gpl2) {
        int index = licenses.indexOf(gpl2OrLater);
        if (index != -1) {
            licenses.remove(gpl2OrLater);
            licenses.add(index, gpl2);
        }
    }

    public static void removeNonPublicArtifacts(Inventory referenceInventory) {
        referenceInventory.getArtifacts().removeIf(artifact -> !isPublicArtifact(artifact));
    }

    /**
     * Returns true in case the Public field is TRUE or null.
     *
     * @param artifact Artifact to check.
     *
     * @return Whether the artifact is public or not.
     */
    public static boolean isPublicArtifact(Artifact artifact) {
        String sPublic = artifact.get("Public");

        // defaults to true
        if (sPublic == null) {
            sPublic = String.valueOf(Boolean.TRUE);
        }
        return String.valueOf(Boolean.TRUE).equalsIgnoreCase(sPublic);
    }

    public static void updatePackageLicenseNotice(org.metaeffekt.core.inventory.processor.model.LicenseMetaData lmd) {
        StringBuilder sb = new StringBuilder();
        String newline = String.format("%n");
        String license = lmd.getLicense();
        String licensesInEffect = lmd.deriveLicenseInEffect();
        sb.append("

The package "). append(lmd.getComponent()). append(" in the present version is licensed under "). append(convertAssociatedLicenseToText(license)). append(".

"). append(newline); if (!tokenizeLicense(license, true, false).equals( tokenizeLicense(licensesInEffect, true, false))) { sb.append("

As effective licenses for distribution "). append(licensesInEffect.replace("|", ", ")); if (licensesInEffect.contains("|")) { sb.append(" have been identified."); } else { sb.append(" has been identified."); } sb.append("

").append(newline); if (license.contains("GNU Affero General Public License") && !licensesInEffect.contains("GNU Affero General Public License")) { sb.append("

In particular, the GNU Affero General Public License does not apply as the " + "affected portions are not part of the distribution.

"); } } sb.append("

Detailed licensing information can be found in the copyright file " + "included in the software distribution annex.

").append(newline); sb.append("

The corresponding source code of the covered open source components is provided on demand.

"); lmd.setNotice(sb.toString()); if (licensesInEffect.contains(" + ")) { throw new IllegalStateException("Undecided effective license detected " + license + " --> " + licensesInEffect); } } private static String convertAssociatedLicenseToText(String license) { int lastIndexOfComma = license.lastIndexOf(","); if (lastIndexOfComma != -1) { String replacement = license.substring(0, lastIndexOfComma) + ", and " + license.substring(lastIndexOfComma + 1); replacement = replacement.replaceAll("and ", "and "); return replacement; } return license; } public static String escapeXml(String s) { return StringEscapeUtils.escapeXml(s); } public static String filterAuthors(String authors) { final String[] split = authors.split("\\|\\n"); final List list = new ArrayList<>(Arrays.asList(split)); for (String author : new ArrayList<>(list)) { String improved = author.replaceAll("> ", ">, "); improved = improved.replaceAll("Patrick McHardy, ", "Patrick McHardy "); improved = improved.replaceAll("Alexey Kuznetsov, ", "Alexey Kuznetsov "); improved = improved.replaceAll("McHardy\\.", "McHardy"); improved = improved.replace(", ,", ", "); improved = improved.replace(" ,", ","); list.remove(author); if (StringUtils.hasText(improved)) { list.add(improved); } } Collections.sort(list, String::compareToIgnoreCase); return String.join(DELIMITER_NEWLINE, list); } private static final Set filterFullList = new HashSet<>(); static { filterFullList.add("(c) MethodHandle mh MethodHandle"); filterFullList.add("(c) ECFieldElement A1"); filterFullList.add("(c) Nat224.isOne"); filterFullList.add("(c) Epsi (c)"); filterFullList.add("(c) Ia\u001AIu (c) Y"); filterFullList.add("(c) IoIof (c)"); filterFullList.add("(c) iu.'e iuiu.be"); filterFullList.add("(c) KE- (c) K"); filterFullList.add("(c) m (c) D"); filterFullList.add("(c) \u0004I (c) I\n(c) \u0005a1/2 (c) I"); filterFullList.add("(c) \u0000\u0002\u0003K\u0001IoIo\u0000\u0002\u0003K\u0001I (c) I"); filterFullList.add("(c) X\"\u0000zE (c) ID"); filterFullList.add("?"); filterFullList.add("Apache Maven"); filterFullList.add("A. (c) B. (c)"); filterFullList.add("[(c) AIQAEBnI L]"); filterFullList.add("(c) deg (c) A]"); filterFullList.add("A9 (c)"); filterFullList.add("|C/!23th'yuyPy*y,y1yoy th1/4th1/2y3/4th?yAuAnA1/2* (c) Y"); filterFullList.add("$', RegexOptions.None, abac', Pass. Group"); filterFullList.add("$? (c) Y"); filterFullList.add("$?1/2P u,O / !- http://www.wretch.cc/blog/fsj&article_id"); filterFullList.add("(c) (c) (c) (c) (c) (c) (c) (c) A989 A98A"); filterFullList.add("(c) A"); filterFullList.add("(c) and G G G"); filterFullList.add("(c) COMMA"); filterFullList.add("(c) CSTYLED"); filterFullList.add("(c) CTRLC"); filterFullList.add("(c) QUIT"); filterFullList.add("(c) UNDEFINED"); // REVIEW-MARKER filterFullList.add("(r)+-u,o 1/4AAEEIIOOxUUThssaaaceeeiiiiddnnooouoooAoUoynyd@iiiieeecaeaaassThUUxOONIIEEAEAAAAou-a!3+- (c) Y"); filterFullList.add(",$?o2Y$?1$? <$?E'O$?1$?e3/4UoU$?I!$?EI$?I http://www.w3.org/TR/NOTE-sgml-xml'>"); filterFullList.add("(c). (c)"); filterFullList.add("(c). (R). End"); filterFullList.add("(c). .It Ar"); filterFullList.add("(c). A"); filterFullList.add("(c). C"); filterFullList.add("(c). C1B"); filterFullList.add("(c). cipher/blowfish-arm.S Ditto"); filterFullList.add("(c). D."); filterFullList.add("(c). END T2"); filterFullList.add("(c). Explicitly"); filterFullList.add("(c). FAILED"); filterFullList.add("(c). Genera"); filterFullList.add("(c). I1 CONSTANT"); filterFullList.add("(c). inline UBool"); filterFullList.add("(c). Instead group"); filterFullList.add("(c). intel/eu Fix"); filterFullList.add("(c). Looping"); filterFullList.add("(c). LxuGAU"); filterFullList.add("(c). Op1"); filterFullList.add("(c). Seemingly"); filterFullList.add("(c). Set"); filterFullList.add("(c). Similarly"); filterFullList.add("(c). TESTS"); filterFullList.add("(c). Therefore"); filterFullList.add("(c). Thus"); filterFullList.add("(c). trans-common.c Forward"); filterFullList.add("(c). Try"); filterFullList.add("(c). UnicodeString"); filterFullList.add("(c). We'll"); filterFullList.add("(c). xT6,g MxtE99"); filterFullList.add("(c).all Sections (First).all then Group"); filterFullList.add("(c)1 a (c) I"); filterFullList.add("(c)1 e (c) I"); filterFullList.add("Copyright $y. $z Free Software Foundation"); filterFullList.add("Copyright $year Free Software Foundation, Inc."); filterFullList.add("(c) 1947 Editions Gallimard"); // REVIEW-MARKER filterFullList.add("(c) 2/I Capture group"); filterFullList.add("(c) <2014>"); filterFullList.add("(c) <2015>"); filterFullList.add("(c) A F0"); filterFullList.add("(c) A M1Nj"); filterFullList.add("(c) ASSERT valid, Failure"); filterFullList.add("(c) Attribut BIND"); filterFullList.add("(c) BasicBlock Entry"); // REVIEW-MARKER filterFullList.add("(c) BasicBlock EntryBB"); // REVIEW-MARKER filterFullList.add("(c) BLANK Fortran"); filterFullList.add("(c) Bob Smith"); filterFullList.add("(c) BOOL F1"); filterFullList.add("(c) Borrowed pragma Assert"); filterFullList.add("(c) BranchInst BI BranchInst::Create(Exit, Exit, False, Entry)"); filterFullList.add("(c) C (c) 0BA11AB"); filterFullList.add("(c) C . C"); filterFullList.add("(c) C C.Next"); filterFullList.add("(c) C EOF. Modern"); filterFullList.add("(c) C Fz"); filterFullList.add("(c) C Scope"); filterFullList.add("(c) C Temp"); filterFullList.add("(c) C/ (c) PS"); filterFullList.add("(c) CAEC/$?eEC/AIUxIC http://www.w3.org/XML"); filterFullList.add("(c) CMD Coding Method Delimiter E CNL Cursor Next Line E Pn"); filterFullList.add("(c) co"); filterFullList.add("(c) Col Col"); filterFullList.add("(c) Communication Foundation"); filterFullList.add("(c) Constant NullV2I32Ptr"); filterFullList.add("(c) Constant One"); filterFullList.add("(c) Constant PosDividendC"); filterFullList.add("(c) Constant PosDivisorC"); filterFullList.add("(c) ConstantInt C2"); filterFullList.add("(c) CopFILE (c)"); filterFullList.add("(c) CopFILESV (c)"); filterFullList.add("(c) DaI (c)"); filterFullList.add("(c) Di3jSS (c)"); filterFullList.add("(c) Dingsbums GmbH"); // TEST CASE / TEST STRING (gettext) filterFullList.add("(c) Frobby Inc."); // TEST CASE / TEST STRING (gettext) filterFullList.add("(c) E AIUeIACIy"); filterFullList.add("(c) Ee-EaEuE OIuI"); filterFullList.add("(c) EFa4 (c)"); filterFullList.add("(c) EO*eUxICEI http://www.w3.org/TR/NOTE-sgml-xml'"); filterFullList.add("(c) ErrMsg CodeView Error"); filterFullList.add("(c) ErrMsg DIA Error"); filterFullList.add("(c) ErrMsg MSF Error"); filterFullList.add("(c) ErrMsg Native PDB Error"); filterFullList.add("(c) ErrMsg PDB Error"); filterFullList.add("(c) ErrMsg Stream Error"); filterFullList.add("(c) ExifDataType dt ExifEntry"); filterFullList.add("(c) expose one performance counter group"); filterFullList.add("(c) EYe3ee (c)"); // JAPANESE filterFullList.add("(c) E|ESSE|E (c) I"); // .raw filterFullList.add("(c) FcChar16 numbers FcCharSetNumbers"); filterFullList.add("(c) FcFontSet new FcFontSetCreate"); filterFullList.add("(c) FE Found M88RS2000"); filterFullList.add("(c) FE Found Stv0288 6LME2510"); filterFullList.add("(c) FE Found Stv0299 6LME2510"); filterFullList.add("(c) Firmware Status"); filterFullList.add("(c) FOR NON-MODEL NUMBERS"); filterFullList.add("(c) FRM Changing"); filterFullList.add("(c) FRM Could"); filterFullList.add("(c) FRM Firmware Cold Reset"); filterFullList.add("(c) FRM Firmware Download Completed - Resetting Device"); filterFullList.add("(c) FRM Firmware Download Completed - Resetting Device 6LME2510"); filterFullList.add("(c) FRM Firmware Download Failed"); filterFullList.add("(c) FRM Starting Firmware Download 6LME2510"); filterFullList.add("(c) From FuncId"); filterFullList.add("(c) FunctionType FnTy"); filterFullList.add("(c) FunctionType FTy"); filterFullList.add("(c) GlobalVariable Handle"); filterFullList.add("(c) group"); filterFullList.add("(c) I Ci Es @ AEuAI"); filterFullList.add("(c) I ISSE\"I\"IaD (c) I"); filterFullList.add("(c) I Write/Read Mangled Y"); filterFullList.add("(c) I\"I (c) I"); filterFullList.add("(c) I\"I (c) ISSI"); filterFullList.add("(c) I\"I\"I (c) I"); filterFullList.add("(c) I\"I\"I\"EaI\"I (c) I"); filterFullList.add("(c) I$?iAT g3eCXMLdT g*e http://www.w3.org/XML"); filterFullList.add("(c) Ia VXefBNgIOADLL EoCh3eAC/eae$?A*BeuA Internet ExploreaWindows Media PlayerIWIT"); filterFullList.add("(c) IaeH, ANH"); filterFullList.add("(c) IaI (c)"); filterFullList.add("(c) IAvP VADLL"); filterFullList.add("(c) IDi (c)"); filterFullList.add("(c) Ie$?R\u007FO (c) R"); filterFullList.add("(c) INT Interrupt Service Started 6LME2510"); filterFullList.add("(c) INT Unable"); filterFullList.add("(c) intermediateCerts.Add"); filterFullList.add("(c) Interrupt Service Stopped"); filterFullList.add("(c) Interrupt Service Stopped 6LME2510"); filterFullList.add("(c) Ioiia (c)"); filterFullList.add("(c) ISALPHA (c) ISDIGIT"); filterFullList.add("(c) isALPHANUMERIC (c) UNDEFINED"); filterFullList.add("(c) IsDigit (c)"); filterFullList.add("(c) isdigit (c) C"); filterFullList.add("(c) ISLETTER (c) DIGIT"); filterFullList.add("(c) IsNetbiosSplChar (c)"); filterFullList.add("(c) ISSE\"ISSI I (c) E"); filterFullList.add("(c) ISSIaI (c)"); filterFullList.add("(c) ISSI|E N (c) I"); filterFullList.add("(c) isSPACE (c) UNDEFINED"); filterFullList.add("(c) isWORDCHAR (c) UNDEFINED"); filterFullList.add("(c) It's OK"); // MARKER filterFullList.add("(c) I|ESSE|E (c) ESSE"); filterFullList.add("(c) K97I V3m"); filterFullList.add("(c) l (c) L"); filterFullList.add("(c) L'attribut BIND"); filterFullList.add("(c) Last Free"); // MARKER filterFullList.add("(c) LIMITED"); // MARKER filterFullList.add("(c) LLVMContextDispose (c)"); filterFullList.add("(c) mc_latency_mclk.full"); filterFullList.add("(c) MEM Error"); filterFullList.add("(c) Metadata Ops MD"); // MARKER filterFullList.add("(c) MidLetter or MidNumLet"); filterFullList.add("(c) Mnemonic C's"); // MARKER filterFullList.add("(c) Na o Io io Ks ks Ps"); filterFullList.add("(c) Object o Hello"); filterFullList.add("(c) Offset C- Offset"); filterFullList.add("(c) OkRaR (c)"); filterFullList.add("(c) Old Old"); filterFullList.add("(c) OR'OR'OR (c) O SR"); filterFullList.add("(c) OR'OR'OR (c) OO"); filterFullList.add("(c) OR'OR'OR c'OR co'OR (c) O SR"); filterFullList.add("(c) Outchar OO"); filterFullList.add("(c) oY X2 PAdeg\"ao+-!ap!C! degN|O$?U+-,N !!A\"A1/2DdegN|O!G ??? http://phorum.vb..."); filterFullList.add("(c) oY X2 PAdeg\"ao+-!ap!C! degN|O$?U+-,N !!A\"A1/2DdegN|O!G ??? http://phorum.vb... / property og:url content http://www.wretch.cc/blog/fsj/3269525"); filterFullList.add("(c) p type(b) bv"); filterFullList.add("(c) P2 (B) P3"); filterFullList.add("(c) Page i/o Todo/Under"); filterFullList.add("(c) Pas de branchement Pas d'appel"); // MARKER filterFullList.add("(c) PointerType InitPtrType InitValue"); filterFullList.add("(c) pragma mangle, clock$UNIX2003"); filterFullList.add("(c) r (c) R"); filterFullList.add("(c) R Value"); filterFullList.add("(c) RaAERR (c)"); filterFullList.add("(c) readpng2$ (c) PROGHDRS"); filterFullList.add("(c) Remove (PROTO) Remove (.PHONY) Remove C and PROTO"); filterFullList.add("(c) Scale 1 Offset"); filterFullList.add("(c) SE0o OS"); filterFullList.add("(c) SelectInst SelI"); filterFullList.add("(c) SmallVector MDs"); filterFullList.add("(c) SmallVector ToErase"); filterFullList.add("(c) SmallVector Types FnPtrTy, FnPtrTy, PromiseType"); filterFullList.add("(c) State UNQUOTED"); filterFullList.add("(c) StringWriter sw HtmlTextWriter"); filterFullList.add("(c) t (c) T"); filterFullList.add("(c) Temp. (F) Summary"); filterFullList.add("(c) Th (c)"); filterFullList.add("(c) TitlecaseLetter E"); filterFullList.add("(c) TUN Found"); filterFullList.add("(c) TUN Found Frontend TDA10086"); filterFullList.add("(c) Type ArgTys Int32Ty, Int32Ty, Int32Ty FunctionType FnTy"); filterFullList.add("(c) Type ArgTys Int32Ty, Int32Ty, Int32Ty Type FnTy"); filterFullList.add("(c) Type AtExitFuncArgs VoidStar FunctionType AtExitFuncTy"); filterFullList.add("(c) Type DoubleTy"); filterFullList.add("(c) Type FloatTy"); filterFullList.add("(c) Type FnTy"); filterFullList.add("(c) Type I1Ty"); filterFullList.add("(c) Type I32PtrTy"); filterFullList.add("(c) Type I32Ty"); filterFullList.add("(c) Type Int16Ty"); filterFullList.add("(c) Type Int32PtrTy"); filterFullList.add("(c) Type Int32Ty"); filterFullList.add("(c) Type Int64PtrTy"); filterFullList.add("(c) Type Int64Ty"); filterFullList.add("(c) Type Int8Ty"); filterFullList.add("(c) Type V8x8Ty"); filterFullList.add("(c) u (c) P"); filterFullList.add("(c) u (c) U"); filterFullList.add("(c) U3269 CIRCLED"); filterFullList.add("(c) Unlock (c)"); filterFullList.add("(c) Value A"); filterFullList.add("(c) Value AllocA"); filterFullList.add("(c) Value Alloca"); filterFullList.add("(c) Value AllocaA"); filterFullList.add("(c) Value Elt0"); filterFullList.add("(c) Value RHS"); filterFullList.add("(c) Value Sd"); filterFullList.add("(c) various MONO"); filterFullList.add("(c) VectorType Int8PtrVecTy"); filterFullList.add("(c) x (c) O"); filterFullList.add("(c) X1 ... X1"); filterFullList.add("(c) Xhadcnt 1 Xcnt"); filterFullList.add("(c) YPe A"); // MARKER filterFullList.add("(c) Yu C"); // MARKER filterFullList.add("(c) Yu L"); // MARKER filterFullList.add("(c) z (c) Z"); filterFullList.add("(c) ZA'-x'E1/2 JU Co"); filterFullList.add("(c) | (c) PSF"); // MARKER filterFullList.add("(c) | (c) SS"); // MARKER filterFullList.add("(c), (bv)- bv_len"); filterFullList.add("(c), (bv)- bv_len (dst)- bv_len (dst)- bv_val"); filterFullList.add("(c), (c)"); filterFullList.add("(c), AnsiChar Array"); filterFullList.add("(c), FcCacheDirs (c)"); filterFullList.add("(c), FcCharSetLeaves (c)"); filterFullList.add("AMDGPUMangledLibFunc (c) From"); filterFullList.add("AttrOf (c) ChAttrOf (c)"); filterFullList.add("AttrOf (c), GetPair (c)"); filterFullList.add("BeeYeaaODIUUOxOEdeaodioonooithyua (c) (c)"); // MARKER filterFullList.add("Bind (c)"); filterFullList.add("BuildProjectForSingleTaskItem '$ (c) CheckItems"); filterFullList.add("BuildProjectForSingleTaskItem '$(D)$ (c) CheckItems"); filterFullList.add("C. copyright D. (c)"); filterFullList.add("CharOf (c) ChCharOf (c)"); filterFullList.add("Chars (c) Chars"); filterFullList.add("Context (c) LineToUnit"); filterFullList.add("Conventions, Example, Copyright, Top"); filterFullList.add("Copr GbE SKU"); // MARKER filterFullList.add("Copr. Self-extracting PKZIP"); filterFullList.add("CopyrightText Copyright (c)"); // MARKER filterFullList.add("CreateChangedMethodDecl (c), CreateChangingMethodDecl (c)"); filterFullList.add("Emit (c) First"); filterFullList.add("EventWatcher (c) BindingContext"); filterFullList.add("EventWatcher (c) ContextMenu"); filterFullList.add("EventWatcher (c) ContextMenuStrip"); filterFullList.add("EventWatcher (c) Font"); filterFullList.add("EventWatcher (c) Image"); filterFullList.add("EventWatcher (c) Region"); filterFullList.add("Fix (c)"); filterFullList.add("Fix copyright Make"); filterFullList.add("Foo (c) Bar"); filterFullList.add("Found bind (c)"); filterFullList.add("GetColumnName (c), GetColumnTSqlType (c)"); filterFullList.add("GvQ3O4A (c) W2"); filterFullList.add("H-c copyright H-a"); filterFullList.add("IaeH, ANH (c) IaeH"); filterFullList.add("IAo3eAAAE (c) (c)"); filterFullList.add("IdI WAOd ACfgAC/ (c) IpublicE"); filterFullList.add("Info. xref"); filterFullList.add("InitCache (c) TransferBatch"); filterFullList.add("Interface (c)"); filterFullList.add("IsHighSurrogate (c) IsLowSurrogate (c)"); filterFullList.add("Kind (c) Reference"); filterFullList.add("len (c) Explicit"); filterFullList.add("LxuG (c)"); filterFullList.add("MemTotal (c) Bytes"); filterFullList.add("Name copyright Type"); filterFullList.add("NonPod (c), Inter"); filterFullList.add("O4 (c) WCn"); filterFullList.add("Omitted (c)"); filterFullList.add("OutCopFILE (c) CopFILE (c)"); filterFullList.add("Power (c) State"); filterFullList.add("ReadOnlyCollection (c) Array"); filterFullList.add("Result (c) First"); filterFullList.add("Result (c) Last"); filterFullList.add("rr"); filterFullList.add("Scope (c) Ctyp"); filterFullList.add("Sloc (c) Ix"); filterFullList.add("Specifies Copyright Filename"); filterFullList.add("TextSyndicationContent copyright ExtensibleSyndicationObject"); filterFullList.add("ToolStripControlHost (c) ToolStrip"); filterFullList.add("UnicodeString src (c) UnicodeString"); filterFullList.add("UnicodeString srcStr (c) UnicodeString"); filterFullList.add("UnicodeString str (c) UErrorCode"); filterFullList.add("Unlock (c) Unlock"); filterFullList.add("(c) $? (c) Y"); filterFullList.add("(c) (3) public benefit corporation"); filterFullList.add("(c) (c) (c) (c) (c) (c) (c) (c) (c) A989 A98A"); filterFullList.add("(c) (c) A"); filterFullList.add("(c) (c) and G G G"); filterFullList.add("(c) (c) COMMA"); filterFullList.add("(c) (c) CSTYLED"); filterFullList.add("(c) (c) CTRLC"); filterFullList.add("(c) (c) QUIT"); filterFullList.add("(c) (c) UNDEFINED"); // HINT; REVIEW filterFullList.add("(c) . (c)"); filterFullList.add("(c) . (R). End"); filterFullList.add("(c) . .It Ar"); filterFullList.add("(c) . A"); filterFullList.add("(c) . C"); filterFullList.add("(c) . C1B"); filterFullList.add("(c) . cipher/blowfish-arm.S Ditto"); filterFullList.add("(c) . D."); filterFullList.add("(c) . END T2"); filterFullList.add("(c) . Explicitly"); filterFullList.add("(c) . FAILED"); filterFullList.add("(c) . Genera"); filterFullList.add("(c) . I1 CONSTANT"); filterFullList.add("(c) . inline UBool"); filterFullList.add("(c) . Instead group"); filterFullList.add("(c) . intel/eu Fix"); filterFullList.add("(c) . Looping"); filterFullList.add("(c) . LxuGAU"); filterFullList.add("(c) . Op1"); filterFullList.add("(c) . Seemingly"); filterFullList.add("(c) . Set"); filterFullList.add("(c) . Similarly"); filterFullList.add("(c) . TESTS"); filterFullList.add("(c) . Therefore"); filterFullList.add("(c) . Thus"); filterFullList.add("(c) . trans-common.c Forward"); filterFullList.add("(c) . Try"); filterFullList.add("(c) . UnicodeString"); filterFullList.add("(c) . We'll"); filterFullList.add("(c) . xT6,g MxtE99"); filterFullList.add("(c) .all Sections (First).all then Group"); filterFullList.add("(c) 1 a (c) I"); filterFullList.add("(c) 1 e (c) I"); filterFullList.add("(c) \"|C/!23th'yuyPy*y,y1yoy th1/4th1/2y3/4th?yAuAn\u0006A1/2* (c) Y"); filterFullList.add("(c) $', RegexOptions.None, abac', Pass. Group"); filterFullList.add("c) $? (c) Y"); filterFullList.add("(c) $?1/2P u,O / !- http://www.wretch.cc/blog/fsj&article_id"); filterFullList.add("(c) (r)+-u,o 1/4AAEEIIOO\u0013xUUThssaaaceeeiiiiddnnooouoooAoUoynyd@iiiieeecaeaaassThUUxOONIIEEAEAAAAou-a!3+- (c) Y"); filterFullList.add("(c) ,$?o2Y$?1$? <$?E'O$?1$?e3/4UoU$?I!$?EI$?I http://www.w3.org/TR/NOTE-sgml-xml'>"); filterFullList.add("(c) d (c) D"); filterFullList.add("(c) e (c) E"); filterFullList.add("(c) i (c) I"); filterFullList.add("(c) e+-RcEEex$?I$?ReAEAE3/4Rceekcee$?R\u007FO (c) R"); filterFullList.add("(c) U,AE:IF\u0014q BVhF'$"); filterFullList.add("(c) iX3/4\bOiMPSAi-Tss5Edeg (c) NU"); } public static List filterCopyrightsAndAuthorsList(String copyrights) { if (!StringUtils.hasText(copyrights)) return new ArrayList<>(); final String[] split = copyrights.split("\\|\\n"); final List copyrightList = new ArrayList<>(Arrays.asList(split)); Collections.sort(copyrightList, String::compareToIgnoreCase); Set filterPrefixList = new HashSet<>(); filterPrefixList.add("(c) ."); Set filterSuffixList = new HashSet<>(); filterSuffixList.add("pppppp \u0010 .kawasaki.jp"); for (String copyright : new ArrayList<>(copyrightList)) { if (fullMatch(copyright, filterFullList)) { removeCopyright(copyright, copyrightList); continue; } if (prefixMatch(copyright, filterPrefixList)) { removeCopyright(copyright, copyrightList); continue; } if (suffixMatch(copyright, filterSuffixList)) { removeCopyright(copyright, copyrightList); continue; } // correct selected copyright if (copyright.equals("GPL. Patrick McHardy (c) 2006-2012")) { copyrightList.remove(copyright); copyrightList.add("Patrick McHardy (C) 2006-2012"); } if (copyright.toLowerCase().contains("$year")) { copyrightList.remove(copyright); } if (copyright.toLowerCase().contains("copyright if $contents")) { copyrightList.remove(copyright); } if (copyright.toLowerCase().contains("$(year)")) { copyrightList.remove(copyright); } if (copyright.toLowerCase().contains("\u0000")) { copyrightList.remove(copyright); } if (copyright.toLowerCase().contains("\u0001")) { copyrightList.remove(copyright); } if (copyright.toLowerCase().contains("\u0002")) { copyrightList.remove(copyright); } if (copyright.toLowerCase().contains("\u0003")) { copyrightList.remove(copyright); } if (copyright.toLowerCase().contains("\u0004")) { copyrightList.remove(copyright); } if (copyright.toLowerCase().contains("\u0005")) { copyrightList.remove(copyright); } if (copyright.startsWith("(c) Returns ")) { copyrightList.remove(copyright); } } return copyrightList; } protected static boolean suffixMatch(String copyright, Set filterSuffixList) { for (String suffix : filterSuffixList) { if (copyright.endsWith(suffix)) { return true; } } return false; } protected static boolean prefixMatch(String copyright, Set filterPrefixList) { for (String prefix : filterPrefixList) { if (copyright.endsWith(prefix)) { return true; } } return false; } protected static boolean fullMatch(String copyright, Set filterFullList) { for (String candidate : filterFullList) { if (copyright.equals(candidate)) { return true; } } return false; } protected static void removeCopyright(String copyright, List copyrightList) { copyrightList.remove(copyright); LOG.debug("Removed copyright: [{}] found in {}.", copyright, copyrightList); } private static String escape(String s) { return StringEscapeUtils.escapeXml(s.trim()); } @Deprecated // renew implementation public static void diffScanCodeResults(Inventory inventory) { for (Artifact a : inventory.getArtifacts()) { String derivedLicense = a.get(KEY_DERIVED_LICENSES); String derivedLicenseScanCode = StringUtils.stripArrayBoundaries(a.get(KEY_DERIVED_LICENSES_SCANCODE)); Set remainingScanCodeLicenses = diffLicensesWithScanCodeLicenses(derivedLicense, derivedLicenseScanCode); String sourceDerivedLicenses = a.get(KEY_SOURCE_ARTIFACT_DERIVED_LICENSES); String sourceDerivedLicenses_ScanCode = StringUtils.stripArrayBoundaries(a.get(KEY_SOURCE_ARTIFACT_DERIVED_LICENSES_SCANCODE)); Set remainingScanCodeLicenses_Source = diffLicensesWithScanCodeLicenses(sourceDerivedLicenses, sourceDerivedLicenses_ScanCode); StringBuilder sb = new StringBuilder(); if (!remainingScanCodeLicenses.isEmpty()) { sb.append("bin: "); sb.append(remainingScanCodeLicenses); } if (!remainingScanCodeLicenses_Source.isEmpty()) { if (sb.length() > 0) { sb.append(", "); } sb.append("src: "); sb.append(remainingScanCodeLicenses); } a.set("Diff - Derived License / Scancode", sb.toString()); } } protected static Set diffLicensesWithScanCodeLicenses(String derivedLicense, String derivedLicenseScanCode) { List licenseList = tokenizeLicense(derivedLicense, false, false); List termsList = NORMALIZATION_META_DATA.convert(licenseList); List scanCodeLicenseList = tokenizeLicense(derivedLicenseScanCode, false, false); Set coveredScanCodeIds = new HashSet<>(); for (TermsMetaData termsMetaData : termsList) { List scanCodeIds = termsMetaData.getScanCodeIdentifiers(); coveredScanCodeIds.addAll(scanCodeIds); } Set remainingScanCodeLicenses = new HashSet<>(scanCodeLicenseList); remainingScanCodeLicenses.removeAll(coveredScanCodeIds); return remainingScanCodeLicenses; } public static void insertLicenseDataForCanonicalNames(Collection canonicalNames, Inventory projectInventory) { for (final String canonicalName : canonicalNames) { final LicenseData matchedLd = projectInventory.findMatchingLicenseData(canonicalName); // check whether already inserted if (matchedLd == null) { final TermsMetaData termsMetaData = NORMALIZATION_META_DATA.getTermsMetaData(canonicalName); if (termsMetaData != null) { final LicenseData ld = new LicenseData(); transferTmdAttributesToLicenseData(ld, termsMetaData); projectInventory.getLicenseData().add(ld); } else { // no validation is performed here; we just report on debug level here; validation happens elsewhere LOG.debug("No explicit license metadata for [{}].", canonicalName); // resolve via alternative name (assuming canonicalNameHistory is already considered) final TermsMetaData alternativeTermsMetaData = NORMALIZATION_META_DATA.findTermsMetaData(canonicalName); if (alternativeTermsMetaData != null) { final String alternativeCanonicalName = alternativeTermsMetaData.getCanonicalName(); // only add in case the license is not yet covered final LicenseData alternativeLicensData = projectInventory.findMatchingLicenseData(alternativeCanonicalName); if (alternativeLicensData == null) { LicenseData ld = new LicenseData(); transferTmdAttributesToLicenseData(ld, alternativeTermsMetaData); projectInventory.getLicenseData().add(ld); } else { // real duplicate (since we use the original canonicalName) } } else { if (canonicalName.contains(" + ")) { // FIXME: shall we decompose the expression? Is this already done? // do not include license expressions } else { LicenseData ld = new LicenseData(); ld.set(LicenseData.Attribute.CANONICAL_NAME, canonicalName); projectInventory.getLicenseData().add(ld); ld.set("Unknown License Type", "x"); // derive information from name if (canonicalName.toLowerCase().contains("commercial")) { ld.set(LicenseData.Attribute.COMMERCIAL, "x"); } } } } } else { // already included; real duplicate } } } public static void transferTmdAttributesToLicenseData(LicenseData ld, TermsMetaData termsMetaData) { ld.set(LicenseData.Attribute.CANONICAL_NAME, termsMetaData.getCanonicalName()); ld.set(LicenseData.Attribute.ID, termsMetaData.deriveId()); ld.set(LicenseData.Attribute.SPDX_ID, termsMetaData.getSpdxIdentifier()); ld.set(LicenseData.Attribute.REPRESENTED_AS, termsMetaData.getRepresentedAs()); final List scanCodeIds = termsMetaData.getScanCodeIdentifiers(); if (scanCodeIds != null) { ld.set("ScanCode Ids", scanCodeIds.stream().sorted().collect(Collectors.joining(", "))); } // insert/manage OSI status ld.set("OSI Status", termsMetaData.getOsiStatus()); // insert/manage Open CoDE status ld.set("Open CoDE Status", termsMetaData.getOpenCoDEStatus()); ld.set("Open CoDE Similar License", termsMetaData.getOpenCoDESimilarLicenseId()); boolean openCodeAssessmentPending = true; if (StringUtils.hasText(termsMetaData.getOpenCoDEStatus())) openCodeAssessmentPending = false; if (termsMetaData.isException()) openCodeAssessmentPending = false; if (termsMetaData.isUnspecific()) openCodeAssessmentPending = false; if (termsMetaData.isMarker()) openCodeAssessmentPending = false; if (termsMetaData.isExpression()) openCodeAssessmentPending = false; if (termsMetaData.allowLaterVersions()) openCodeAssessmentPending = false; // TODO: condition incomplete if (StringUtils.hasText(termsMetaData.getRepresentedAs()) && StringUtils.isEmpty(termsMetaData.getSpdxIdentifier()) && (termsMetaData.getOtherIds() == null || termsMetaData.getOtherIds().isEmpty())) openCodeAssessmentPending = false; // mark set to indicate that an assessment from open code is not yet available if (openCodeAssessmentPending) { ld.set("Open CoDE Assessment Pending", "x"); } else { ld.set("Open CoDE Assessment Pending", null); } // FIXME: improve; organize questions in TMD final String licenseClassification = termsMetaData.getClassification(); if (licenseClassification != null) { final String licenseClassificationLowerCase = licenseClassification.toLowerCase(); ld.set("C-" + licenseClassificationLowerCase, "x"); } String type = termsMetaData.getType(); if (StringUtils.isEmpty(type)) type = "terms"; ld.set("Type", type); if ("terms".equalsIgnoreCase(type)) { final List matchedMarkers = termsMetaData.getMatchedMarkers(); if (matchedMarkers != null) { for (String marker : matchedMarkers) { TermsMetaData tmd = getNormalizationMetaData().resolveTermsMetaData(marker); ld.set("M-" + tmd.getShortName(), "x"); } } } } public static void applyDerivedLicenses(Inventory projectInventory) { for (Artifact artifact : projectInventory.getArtifacts()) { if (!StringUtils.hasText(artifact.getLicense())) { final String derivedLicense = artifact.get(KEY_DERIVED_LICENSES); if (StringUtils.hasText(derivedLicense)) { artifact.setLicense(derivedLicense); } } } } // FIXME: consider moving to Artifact utils. /** * Currently LicenseMetaData can only be associated to an artifact if license, version and component are defined. * This method evaluates the pre-requisites. * * @param artifact The artifacts to evaluate. * @return Returns true in case LicenseMetaData can be stored with the artifact. */ public static boolean canCarryLicenseMetaData(Artifact artifact) { return StringUtils.notEmpty(artifact.getLicense()) && StringUtils.notEmpty(artifact.getVersion()) && StringUtils.notEmpty(artifact.getComponent()); } public static void addToInventory(Inventory sourceInventory, Inventory targetInventory, boolean processAssets) { InventorySearch inventorySearch = new InventorySearch(targetInventory); addToInventory(sourceInventory.getArtifacts(), inventorySearch); // FIXME: yet naive implementation; may produce duplicates if (processAssets) { targetInventory.getAssetMetaData().addAll(sourceInventory.getAssetMetaData()); InventoryUtils.mergeDuplicateAssets(targetInventory); } } public static void addToInventory(Collection artifacts, InventorySearch inventorySearch) { for (Artifact artifact : artifacts) { final Artifact referenceArtifact; if (!StringUtils.isEmpty(artifact.getChecksum())) { referenceArtifact = inventorySearch.findArtifactByIdAndChecksum(artifact); } else { referenceArtifact = inventorySearch.findArtifactById(artifact); } if (referenceArtifact != null) { // at the moment we do nothing; we may want to validate attributes and add projects. // FIXME: compare columns; check for deviations; eg. architecture; think of a policy // FIXME: fix merge method in core; inserts null string final String value = artifact.get(Artifact.Attribute.PROJECTS); if (!StringUtils.isEmpty(value)) { referenceArtifact.append(Artifact.Attribute.PROJECTS.getKey(), value, Artifact.PROJECT_DELIMITER); } // merge / don't overwrite for other attributes for (String attribute : artifact.getAttributes()) { if (attribute.equals(Artifact.Attribute.PROJECTS.getKey())) continue; if (referenceArtifact.get(attribute) == null) { referenceArtifact.set(attribute, artifact.get(attribute)); } } } else { // the artifact is not contained in the aggregated inventory yet; we add it inventorySearch.add(artifact); } } } public static void deriveComponentNamesAndVersions(Inventory filteredInventory) { for (Artifact a : filteredInventory.getArtifacts()) { if (StringUtils.isEmpty(a.getComponent())) { if (StringUtils.notEmpty(a.getId())) { // use artifact id as component name if (StringUtils.notEmpty(a.getVersion())) { a.deriveArtifactId(); a.setComponent(a.getArtifactId()); } // otherwise try to extract from id if (StringUtils.isEmpty(a.getComponent())) { String componentName = a.getId(); final int dashIndex = componentName.lastIndexOf("-"); if (dashIndex > 0) { componentName = componentName.substring(0, dashIndex); } else { final int dotIndex = componentName.lastIndexOf("."); if (dotIndex > 0) { componentName = componentName.substring(0, dotIndex); } } a.setComponent(componentName); } } } String proposedVersion = a.get(Constants.KEY_DESCRIPTOR_DERIVED_VERSION); if (StringUtils.isEmpty(a.getVersion()) && StringUtils.notEmpty(proposedVersion)) { a.setVersion(proposedVersion); } String proposedGroupId = a.get(Constants.KEY_DESCRIPTOR_DERIVED_GROUPID); if (StringUtils.isEmpty(a.getGroupId()) && StringUtils.notEmpty(proposedGroupId)) { a.setGroupId(proposedGroupId); } } } public static void inheritInformationFromReferenceInventory(Inventory inventory, InventorySearch referenceInventory) { // NOTE: inherit... // - requires a version (also unspecific can be supported) // - id must either identical or must include the version // - the longest match is the best choice (considering major.minor.patch is part of the version string) // - currently the implementation does not evaluate the closest version but the longest string match for (Artifact artifact : inventory.getArtifacts()) { final String id = artifact.getId(); final String version = artifact.getVersion(); if (id == null || version == null) continue; int index = id.indexOf(version); final String prefix = index != -1 ? id.substring(0, index) : id; final String suffix = index != -1 ? id.substring(index + version.length()) : ""; int count = -1; // check whether we have an exact match (optimized) Artifact bestMatch = referenceInventory.findArtifactByIdAndChecksum(artifact); // fallback to wildcard matching (optimized) if (bestMatch == null) { bestMatch = referenceInventory.findArtifactWildcardMatchingId(artifact); } // check for similar versions (non-optimized) if (bestMatch == null) { for (Artifact a : referenceInventory.getArtifacts()) { final String aId = a.getId(); final String aVersion = a.getVersion(); if (aId == null || a.getVersion() == null) continue; int aIndex = aId.indexOf(a.getVersion()); final String aPrefix = aIndex != -1 ? aId.substring(0, aIndex) : id; final String aSuffix = aIndex != -1 ? aId.substring(aIndex + aVersion.length()) : ""; if (prefix.equals(aPrefix) && suffix.equals(aSuffix)) { // break, when the version is an exact match (should have found this eralier) if (aVersion.equals(version)) { bestMatch = a; break; } // count common characters from left to right for (int i = 0; i < Math.min(version.length(), aVersion.length()); i++) { if (version.charAt(i) == aVersion.charAt(i)) { if (count < i) { count = i; bestMatch = a; } } else { break; } } } } } if (bestMatch != null) { final String qualifier = artifact.getId() + "/" + artifact.getChecksum() + "/" + artifact.getVersion() + "/" + artifact.getGroupId(); final String bestMatchQualifier = bestMatch.getId() + "/" + bestMatch.getChecksum() + "/" + bestMatch.getVersion() + "/" + bestMatch.getGroupId(); if (!qualifier.equals(bestMatchQualifier)) { artifact.set("Inherited Component", bestMatch.getComponent()); artifact.set("Inherited Version", bestMatch.getVersion()); artifact.set("Inherited GroupId", bestMatch.getGroupId()); artifact.set("Inherited License", bestMatch.getLicense()); artifact.set("Inherited Notice Parameter", bestMatch.get("Notice Parameter")); artifact.set("Inherited Analysis", bestMatch.get("Analysis")); artifact.set("Inherited Protocol of Research", bestMatch.get("Protocol of Research")); } else { // removal temp. disabled; removed "curated-inherited" if (false) { LOG.info(id + " does not inherit from any artifact."); artifact.set("Inherited Component", null); artifact.set("Inherited Version", null); artifact.set("Inherited GroupId", null); artifact.set("Inherited License", null); artifact.set("Inherited Notice Parameter", null); artifact.set("Inherited Analysis", null); artifact.set("Inherited Protocol of Research", null); } } } } } public static void filterExceptionsFromLicenses(Inventory inventory) { for (Artifact artifact : inventory.getArtifacts()) { final List licenses = artifact.getLicenses(); for (String license : new ArrayList<>(licenses)) { final TermsMetaData termsMetaData = getNormalizationMetaData().getTermsMetaData(license); if (termsMetaData != null) { if (termsMetaData.isException()) { licenses.remove( license); } } } artifact.setLicense(joinLicenses(licenses)); } } public static void orderLicenses(Inventory inventory) { for (Artifact artifact : inventory.getArtifacts()) { final List licenses = tokenizeLicense(artifact.getLicense(), true, true); final List newLicenses = new ArrayList<>(); for (String license : licenses) { if (license.contains(" + ")) { List orLicenses = tokenizeLicense(license, true, false); newLicenses.add(joinOredLicenses(orLicenses)); } else { newLicenses.add(license); } } Collections.sort(newLicenses, String.CASE_INSENSITIVE_ORDER); artifact.setLicense(joinLicenses(newLicenses)); } } public static void renameArtifactAttributes(Inventory inventory, String oldAttributeKey, String newAttributeKey) { renameArtifactAttributes(inventory, oldAttributeKey, newAttributeKey, false); } public static void renameArtifactAttributes(Inventory inventory, String oldAttributeKey, String newAttributeKey, boolean allowOverwrites) { for (Artifact artifact : inventory.getArtifacts()) { String oldValue = artifact.get(oldAttributeKey); String newValue = artifact.get(newAttributeKey); // normalize to null if (StringUtils.isEmpty(oldValue)) oldValue = null; if (StringUtils.isEmpty(newValue)) newValue = null; if (Objects.equals(oldValue, newValue)) { // already the same value; remove old attribute key artifact.set(oldAttributeKey, null); } else if (newValue != null && oldValue != null) { // new value is already set and different from the value in the old column if (!allowOverwrites) { final String message = String.format("Cannot rename [%s] attribute %s to %s. " + "New attribute already exists and contains different values: old [%s] vs new [%s]", artifact.getId(), oldAttributeKey, newAttributeKey, oldValue, newValue); LOG.error(message); // throw new IllegalStateException(message); } else { artifact.set(newAttributeKey, oldValue); artifact.set(oldAttributeKey, null); } } else if (newValue == null) { // newValue is null; replace artifact.set(newAttributeKey, oldValue); artifact.set(oldAttributeKey, null); } else { // oldValue is null; do nothing } } // manage serialization context final InventorySerializationContext serializationContext = inventory.getSerializationContext(); final ArrayList list = serializationContext.get(InventorySerializationContext.CONTEXT_ARTIFACT_COLUMN_LIST); // manage serialization context only if available if (list != null) { int index = list.indexOf(oldAttributeKey); int indexNew = list.indexOf(newAttributeKey); // in case the new key already exists, it remains its position. if (indexNew == -1) { if (index != -1) { // replace current position with new attribute key list.set(index, newAttributeKey); } else { // rename does not add any new items } } while (list.contains(oldAttributeKey)) { list.remove(oldAttributeKey); } } } public static void removeArtifactInheritedAttributes(Inventory inventory) { removeArtifactAttributeStartingWith(inventory, "Inherited "); } final static HashSet INTERNAL_COLUMNS = new HashSet<>(); static { INTERNAL_COLUMNS.add("Analysis"); INTERNAL_COLUMNS.add("Projects"); INTERNAL_COLUMNS.add("Protocol of Research"); INTERNAL_COLUMNS.add("Source Project"); INTERNAL_COLUMNS.add("Source Inventory"); INTERNAL_COLUMNS.add("Inventory Source"); INTERNAL_COLUMNS.add("Src/Bin Derived Licenses Diff"); INTERNAL_COLUMNS.add("Task"); INTERNAL_COLUMNS.add("Tasks"); INTERNAL_COLUMNS.add("Validation Mode"); INTERNAL_COLUMNS.add("License Text"); INTERNAL_COLUMNS.add("MANIFEST.MF/JAR (Bundled License Information)"); INTERNAL_COLUMNS.add("Notice.txt"); INTERNAL_COLUMNS.add("Review Remark"); // classification may change INTERNAL_COLUMNS.add("Notice Parameter"); // relevant only for internal resolving INTERNAL_COLUMNS.add("Derived ArtifactId (POM)"); INTERNAL_COLUMNS.add("Derived Component (POM)"); INTERNAL_COLUMNS.add("Derived GroupId (POM)"); INTERNAL_COLUMNS.add("Derived Version (POM)"); // redundant INTERNAL_COLUMNS.add("Filtered Terms"); INTERNAL_COLUMNS.add("Identified Terms"); // issue may contain free text information and expose information that is not desired // INTERNAL_COLUMNS.add("Issue"); INTERNAL_COLUMNS.add("Comment"); INTERNAL_COLUMNS.add("Copyright Notice"); INTERNAL_COLUMNS.add("Header Text Copy"); INTERNAL_COLUMNS.add("Ignored Terms"); INTERNAL_COLUMNS.add("Latest Version"); // added 10.05.2023: INTERNAL_COLUMNS.add("Content Checksum (MD5)"); INTERNAL_COLUMNS.add("Content MD5"); INTERNAL_COLUMNS.add("Archive Path"); // NOTE: the markers are report relevant and must not be excluded. // INTERNAL_COLUMNS.add("Derived Markers"); // INTERNAL_COLUMNS.add("POM - Derived Markers"); // INTERNAL_COLUMNS.add("Source - Derived Licenses"); // INTERNAL_COLUMNS.add("Source - Derived Markers"); // INTERNAL_COLUMNS.add("Source - Archive Path"); INTERNAL_COLUMNS.add("Derived Notice Parameter"); INTERNAL_COLUMNS.add("Diff - Derived License / Scancode"); INTERNAL_COLUMNS.add(KEY_EXTRACTED_COPYRIGHTS_SCANCODE); INTERNAL_COLUMNS.add(KEY_EXTRACTED_AUTHORS_SCANCODE); INTERNAL_COLUMNS.add("License / Derived Src/Bin License Diff"); INTERNAL_COLUMNS.add("License Expressions (ScanCode)"); INTERNAL_COLUMNS.add("POM - Derived ArtifactId (POM)"); INTERNAL_COLUMNS.add("POM - Derived Component (POM)"); INTERNAL_COLUMNS.add("POM - Derived GroupId (POM)"); INTERNAL_COLUMNS.add("POM - Derived Licenses (POM)"); INTERNAL_COLUMNS.add("POM - Derived Licenses (ScanCode)"); INTERNAL_COLUMNS.add("POM - Derived POM Licenses"); INTERNAL_COLUMNS.add("POM - Derived Version (POM)"); INTERNAL_COLUMNS.add("POM - Extracted Authors (ScanCode)"); INTERNAL_COLUMNS.add("POM - Extracted Copyrights (ScanCode)"); INTERNAL_COLUMNS.add("POM - Identified Terms"); INTERNAL_COLUMNS.add("Source - Derived ArtifactId (POM)"); INTERNAL_COLUMNS.add("Source - Derived Component (POM)"); INTERNAL_COLUMNS.add("Source - Derived GroupId (POM)"); INTERNAL_COLUMNS.add("Source - Derived License (POM)"); INTERNAL_COLUMNS.add("Source - Derived Licenses (POM)"); INTERNAL_COLUMNS.add("Source - Derived Licenses (ScanCode)"); INTERNAL_COLUMNS.add("Source - Derived Version (POM)"); INTERNAL_COLUMNS.add("Source - Extracted Authors (ScanCode)"); INTERNAL_COLUMNS.add("Source - Extracted Copyrights (ScanCode)"); INTERNAL_COLUMNS.add("Source - Filtered Terms"); INTERNAL_COLUMNS.add("Source - Identified Terms"); INTERNAL_COLUMNS.add("Source - Ignored Terms"); } public static void removeArtifactInternalAttributes(Inventory inventory) { removeAttributes(inventory, INTERNAL_COLUMNS); } public static String createStringRepresentation(Artifact artifact) { final String id = artifact.getId(); final String groupId = artifact.getGroupId(); final String version = artifact.getVersion(); String s = ""; s = id != null ? s + "/" + id : s + "/"; s = groupId != null ? s + "/" + groupId : s + "/"; s = version != null ? s + "/" + version : s + "/"; return s; } public static String createStringRepresentation(AssetMetaData assetMetaData) { final String assetId = assetMetaData.get(AssetMetaData.Attribute.ASSET_ID); String s = ""; s = assetId != null ? s + "/" + assetId : s + "/"; return s; } public static String createStringRepresentation(LicenseData licenseData) { final String assetId = licenseData.get(LicenseData.Attribute.CANONICAL_NAME); String s = ""; s = assetId != null ? s + "/" + assetId : s + "/"; return s; } public static void removeLicenseDataAttributeContaining(String substring, Inventory inventory) { for (LicenseData licenseData : inventory.getLicenseData()) { for (String key : new HashSet<>(licenseData.getAttributes())) { if (key.contains(substring)) { licenseData.set(key, null); } } } } public static void removeAssetAttribute(String key, Inventory inventory) { for (AssetMetaData assetMetaData : inventory.getAssetMetaData()) { assetMetaData.set(key, null); } } /** * Removes all assets from an inventory, as well as their artifact attributes, if for example CONTAINS or DESCRIBES * relationships are tracked. * @param inventory the inventory */ public static void removeAssetsAndReferences(Inventory inventory) { for (AssetMetaData asset : inventory.getAssetMetaData()) { String assetId = asset.get(AssetMetaData.Attribute.ASSET_ID); for (Artifact artifact : inventory.getArtifacts()) { if (StringUtils.notEmpty(artifact.get(asset.get(AssetMetaData.Attribute.ASSET_ID)))) { artifact.set(assetId, null); } } } inventory.setAssetMetaData(Collections.emptyList()); } public static void mergeDuplicateAssets(Inventory mergedInventory) { final Map> qualiferAssetMap = new HashMap<>(); for (AssetMetaData assetMetaData : mergedInventory.getAssetMetaData()) { qualiferAssetMap.computeIfAbsent(assetMetaData.deriveQualifier(), c -> new LinkedHashSet<>()).add(assetMetaData); } final Set toBeDeleted = new HashSet<>(); for (HashSet assetMetaDataSet : qualiferAssetMap.values()) { // first item is the reference final Iterator assetMetaDataIterator = assetMetaDataSet.iterator(); final AssetMetaData referenceAsset = assetMetaDataIterator.next(); while (assetMetaDataIterator.hasNext()) { final AssetMetaData duplicate = assetMetaDataIterator.next(); toBeDeleted.add(duplicate); // FIXME: expose a merge signature on artifact level for (final String key : duplicate.getAttributes()) { final String value = referenceAsset.get(key); if (!StringUtils.hasText(value)) { referenceAsset.set(key, duplicate.get(key)); } } } } toBeDeleted.forEach(mergedInventory.getAssetMetaData()::remove); } public static void addContentChecksum(Inventory referenceInventory) throws IOException { final File tmpDir = new File(System.getProperty("java.io.tmpdir"), "ae-content-checksum"); for (Artifact artifact : referenceInventory.getArtifacts()) { if (StringUtils.isEmpty(artifact.get("Content MD5"))) { final String analysisPath = artifact.get("Analysis Path"); if (StringUtils.notEmpty(analysisPath)) { if (LOG.isDebugEnabled()) { LOG.debug("Processing content checksum for " + artifact.deriveQualifier()); } final File artifactAnalysisPath = new File(analysisPath); if (artifactAnalysisPath.exists()) { final String contentChecksum = FileUtils.computeContentChecksum(artifactAnalysisPath, tmpDir); artifact.set("Content MD5", contentChecksum); } else { LOG.warn("Expected content in path " + artifactAnalysisPath + ". Cannot compute content checksum."); } } } } } public static void inheritInformationFromReferenceInventory(Inventory inventory, Inventory referenceInventory) { for (Artifact artifact : inventory.getArtifacts()) { final String id = artifact.getId(); for (Artifact a : referenceInventory.getArtifacts()) { final String aId = a.getId(); if (aId == null) continue; if (a.getVersion() == null) continue; int index = aId.indexOf(a.getVersion()); if (index > 0) { String prefix = aId.substring(0, index); String suffix = aId.substring(index + a.getVersion().length()); if (id.startsWith(prefix) && id.endsWith(suffix)) { final int startIndex = prefix.length(); final int endIndex = id.length() - suffix.length(); if (a != artifact) { // these parts are not inherited from self if (StringUtils.isEmpty(artifact.getComponent())) { artifact.setComponent(a.getComponent()); } if (StringUtils.isEmpty(artifact.getVersion())) { if (startIndex < endIndex) { artifact.setVersion(id.substring(startIndex, endIndex)); } } if (StringUtils.isEmpty(artifact.getGroupId())) { artifact.setGroupId(a.getGroupId()); } if (StringUtils.isEmpty(a.get("Public"))) { artifact.set("Public", a.get("Public")); } if (!a.createCompareStringRepresentation().equals(artifact.createCompareStringRepresentation())) { if (StringUtils.notEmpty(a.getLicense())) { artifact.set("Inherited License", a.getLicense()); LOG.debug("Inherited license from {} to {}.", a.deriveQualifier(), artifact.deriveQualifier()); } if (StringUtils.notEmpty(a.get("Notice Parameter"))) { artifact.set("Inherited Notice Parameter", a.get("Notice Parameter")); LOG.debug("Inherited notice parameter from {} to {}.", a.deriveQualifier(), artifact.deriveQualifier()); } if (StringUtils.notEmpty(a.get("Analysis"))) { artifact.set("Inherited Analysis", a.get("Analysis")); LOG.debug("Inherited analysis from {} to {}.", a.deriveQualifier(), artifact.deriveQualifier()); } if (StringUtils.notEmpty(a.get("Protocol of Research"))) { artifact.set("Inherited Protocol of Research", a.get("Protocol of Research")); LOG.debug("Inherited protocol of research from {} to {}.", a.deriveQualifier(), artifact.deriveQualifier()); } } } // include legacy columns; remove once inventories have been consolidated if (StringUtils.notEmpty(a.get("Protocol of Research - OLD"))) { artifact.set("Inherited Protocol of Research", a.get("Protocol of Research - OLD")); LOG.debug("Inherited protocol of research from {} to {}.", a.deriveQualifier(), artifact.deriveQualifier()); } if (StringUtils.notEmpty(a.get("Risk Log - Aufgaben zur Klärung"))) { artifact.set("Inherited Protocol of Research", a.get("Risk Log - Aufgaben zur Klärung")); LOG.debug("Inherited protocol of research from {} to {}.", a.deriveQualifier(), artifact.deriveQualifier()); } if (StringUtils.notEmpty(a.get("Protokoll"))) { artifact.set("Inherited Protocol of Research", a.get("Protokoll")); LOG.debug("Inherited protocol of research from {} to {}.", a.deriveQualifier(), artifact.deriveQualifier()); } } } } } for (Artifact artifact : inventory.getArtifacts()) { if (StringUtils.isEmpty(artifact.getVersion()) && StringUtils.notEmpty(artifact.getId())) { String artifactId = artifact.getId(); if (artifactId.contains("_")) { artifact.setArtifactId(artifactId.substring(0, artifactId.lastIndexOf("_"))); artifact.setVersion(artifactId.substring(artifactId.lastIndexOf("_") + 1, artifactId.length() - 4)); } } } } /** * Effectively removes empty columns in the inventory. * * @param inventory Inventory to remove empty attributes from */ public static void removeEmptyAttributes(Inventory inventory) { final Set attributes = collectArtifactAttributes(inventory); for (String attribute : new ArrayList<>(attributes)) { final Optional first = inventory.getArtifacts().stream() .map(a -> a.get(attribute)) .filter(StringUtils::hasText) .findFirst(); if (!first.isPresent()) { InventoryUtils.removeArtifactAttribute(attribute, inventory); attributes.remove(attribute); } } } /** * Matches artifacts from sourceInventoryResource with artifacts in the referenceInventoryResource. Only uses * equivalent matches. * * @param sourceInventoryResource The source {@link InventoryResource}. * @param referenceInventoryResource The reference {@link InventoryResource}. * @param includeIdMatches Boolean indicating whether to include matches only on id level. */ public static void mergeReferenceArtifactAttributes(InventoryResource sourceInventoryResource, InventoryResource referenceInventoryResource, boolean includeIdMatches) { final Inventory referenceInventory = referenceInventoryResource.getInventory(); for (final Artifact artifact : sourceInventoryResource.getInventory().getArtifacts()) { final Artifact referenceArtifact = matchIdentifiedArtifact(artifact, referenceInventory, includeIdMatches); if (referenceArtifact != null) { System.out.println("Merging artifact: " + artifact.getId()); artifact.merge(referenceArtifact); } } } public static Artifact matchIdentifiedArtifact(final Artifact artifact, final Inventory referenceInventory, boolean includeIdMatches) { // find by id/checksum Artifact referenceMatch = referenceInventory.findArtifactByIdAndChecksum(artifact.getId(), artifact.getChecksum()); // FIXME: better find by PURL; requires PURLs in reference inventory if (referenceMatch == null && StringUtils.hasText(artifact.getGroupId())) { artifact.deriveArtifactId(); if (StringUtils.hasText(artifact.getArtifactId())) { referenceMatch = referenceInventory.findArtifact(artifact.getGroupId(), artifact.getArtifactId()); } } // find by id only (first match); this anticipates that if (referenceMatch == null && includeIdMatches) { final List allWithId = referenceInventory.findAllWithId(artifact.getId()); if (allWithId.size() != 0) { referenceMatch = allWithId.get(0); if (allWithId.size() > 1) { LOG.warn("Multiple artifacts in reference inventory matching id-only: [{}]. Picking first match.", artifact.getId()); } } } return referenceMatch; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy