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

model.SpdxPackage.kt Maven / Gradle / Ivy

/*
 * Copyright (C) 2020 The ORT Project Authors (see )
 *
 * 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
 *
 *     https://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.
 *
 * SPDX-License-Identifier: Apache-2.0
 * License-Filename: LICENSE
 */

package org.ossreviewtoolkit.utils.spdx.model

import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonProperty

import org.ossreviewtoolkit.utils.spdx.SpdxConstants
import org.ossreviewtoolkit.utils.spdx.SpdxExpression.Strictness.ALLOW_LICENSEREF_EXCEPTIONS
import org.ossreviewtoolkit.utils.spdx.isSpdxExpressionOrNotPresent

/**
 * Information about a package used in an [SpdxDocument].
 * See https://spdx.github.io/spdx-spec/v2.2.2/package-information/.
 */
data class SpdxPackage(
    /**
     * A unique identifier for this [SpdxPackage] within a SPDX document.
     */
    @JsonProperty("SPDXID")
    val spdxId: String,

    /**
     * The [SpdxAnnotation]s for the package.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val annotations: List = emptyList(),

    /**
     * Acknowledgements for the package that may be required to be communicated in some contexts. This is not meant to
     * include the package's actual complete license text, and may include copyright notices. The SPDX data creator
     * may use this field to record other acknowledgements, such as particular clauses from license texts, which may be
     * necessary or desirable to reproduce.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val attributionTexts: List = emptyList(),

    /**
     * Checksums of the package.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val checksums: List = emptyList(),

    /**
     * Any general comments about the package.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val comment: String = "",

    /**
     * A text relating to a copyright notice, even if not complete. To represent a not present value
     * [SpdxConstants.NONE] or [SpdxConstants.NOASSERTION] must be used.
     */
    val copyrightText: String = SpdxConstants.NOASSERTION,

    /**
     * A more detailed description of the package as opposed to [summary], which may be an extract from the package.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val description: String = "",

    /**
     * The download location as URL. To represent a not present value [SpdxConstants.NONE] or
     * [SpdxConstants.NOASSERTION] must be used.
     */
    val downloadLocation: String,

    /**
     * References to external sources of additional information.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val externalRefs: List = emptyList(),

    /**
     * Indicates whether the file contents of the package have been used for the creation of the associated
     * [SpdxDocument].
     */
    val filesAnalyzed: Boolean = true,

    /**
     * The Spdx references to the files belonging to the package in the format "SPDXRef-${id-string}".
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val hasFiles: List = emptyList(),

    /**
     * The homepage URL.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val homepage: String = "",

    /**
     * Any relevant background references or analysis that went in to arriving at the concluded License for the package.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val licenseComments: String = "",

    /**
     * The concluded license for the package as SPDX expression. To represent a not present value [SpdxConstants.NONE]
     * or [SpdxConstants.NOASSERTION] must be used.
     */
    val licenseConcluded: String,

    /**
     * The declared license for the package as SPDX expression. To represent a not present value [SpdxConstants.NONE] or
     * [SpdxConstants.NOASSERTION] must be used.
     */
    val licenseDeclared: String,

    /**
     * A list of all licenses found in the package. These are simply license ID strings, no SPDX license expressions,
     * and license operators are not maintained. To represent a not present value [SpdxConstants.NONE] or
     * [SpdxConstants.NOASSERTION] must be used.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val licenseInfoFromFiles: List = emptyList(),

    /**
     * The name of the package.
     */
    val name: String,

    /**
     * Identifies from where or whom the package originally came. The value must be "NOASSERTION" or a single line text
     * in one of the following formats:
     *
     * 1. "Person: person name" or "Person: person name (email)"
     * 2. "Organization: organization name" or "Organization: organization name (email)"
     *
     * TODO: Introduce a data type for above subjects.
     */
    @JsonInclude(JsonInclude.Include.NON_NULL)
    val originator: String? = null,

    /**
     * The actual file name of the package, or path of the directory being treated as a package.
     */
    @JsonProperty("packageFileName")
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val packageFilename: String = "",

    /**
     * The [SpdxPackageVerificationCode] for the package.
     */
    @JsonInclude(JsonInclude.Include.NON_NULL)
    val packageVerificationCode: SpdxPackageVerificationCode? = null,

    /**
     * Any relevant background information or additional comments about the origin of the package.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val sourceInfo: String = "",

    /**
     * A short description of the package.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val summary: String = "",

    /**
     * The distribution source for the package. The value must be "NOASSERTION" or a single line of text in one of the
     * following formats:
     *
     * 1. "Person: person name" or "Person: person name (email)"
     * 2. "Organization: organization name" or "Organization: organization name (email)"
     */
    @JsonInclude(JsonInclude.Include.NON_NULL)
    val supplier: String? = null,

    /**
     * The version of the package.
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    val versionInfo: String = ""
) {
    init {
        require(spdxId.startsWith(SpdxConstants.REF_PREFIX)) {
            "The SPDX ID '$spdxId' has to start with '${SpdxConstants.REF_PREFIX}'."
        }

        require(spdxId.all { it.isLetterOrDigit() || it == '.' || it == '-' }) {
            "The SPDX ID '$spdxId' is only allowed to contain letters, numbers, '.', and '-'."
        }

        require(copyrightText.isNotBlank()) { "The copyright text must not be blank." }

        require(downloadLocation.isNotBlank()) { "The download location must not be blank." }

        require(name.isNotBlank()) { "The package name for SPDX-ID '$spdxId' must not be blank." }

        val validPrefixes = listOf(SpdxConstants.PERSON, SpdxConstants.ORGANIZATION)

        if (originator != null) {
            require(originator == SpdxConstants.NOASSERTION || validPrefixes.any { originator.startsWith(it) }) {
                "If specified, the originator has to start with any of $validPrefixes or be set to 'NOASSERTION'."
            }
        }

        if (supplier != null) {
            require(supplier == SpdxConstants.NOASSERTION || validPrefixes.any { supplier.startsWith(it) }) {
                "If specified, the supplier has to start with any of $validPrefixes or be set to 'NOASSERTION'."
            }
        }

        // TODO: The check for [licenseInfoFromFiles] can be made more strict, but the SPDX specification is not exact
        //       enough yet to do this safely.
        licenseInfoFromFiles.filterNot {
            it.isSpdxExpressionOrNotPresent(ALLOW_LICENSEREF_EXCEPTIONS)
        }.let { nonSpdxLicenses ->
            require(nonSpdxLicenses.isEmpty()) {
                "The entries in 'licenseInfoFromFiles' must each be either an SPDX expression, 'NONE' or " +
                    "'NOASSERTION', but found ${nonSpdxLicenses.joinToString { "'$it'" }}."
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy