1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
// Package v2_3 Package contains the struct definition for an SPDX Document
// and its constituent parts.
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
package v2_3
import (
"encoding/json"
"fmt"
converter "github.com/anchore/go-struct-converter"
"github.com/spdx/tools-golang/spdx/v2/common"
)
const Version = "SPDX-2.3"
const DataLicense = "CC0-1.0"
// ExternalDocumentRef is a reference to an external SPDX document as defined in section 6.6
type ExternalDocumentRef struct {
// DocumentRefID is the ID string defined in the start of the
// reference. It should _not_ contain the "DocumentRef-" part
// of the mandatory ID string.
DocumentRefID string `json:"externalDocumentId"`
// URI is the URI defined for the external document
URI string `json:"spdxDocument"`
// Checksum is the actual hash data
Checksum common.Checksum `json:"checksum"`
}
// Document is an SPDX Document:
// See https://spdx.github.io/spdx-spec/v2.3/document-creation-information
type Document struct {
// 6.1: SPDX Version; should be in the format "SPDX-<version>"
// Cardinality: mandatory, one
SPDXVersion string `json:"spdxVersion"`
// 6.2: Data License; should be "CC0-1.0"
// Cardinality: mandatory, one
DataLicense string `json:"dataLicense"`
// 6.3: SPDX Identifier; should be "DOCUMENT" to represent
// mandatory identifier of SPDXRef-DOCUMENT
// Cardinality: mandatory, one
SPDXIdentifier common.ElementID `json:"SPDXID"`
// 6.4: Document Name
// Cardinality: mandatory, one
DocumentName string `json:"name"`
// 6.5: Document Namespace
// Cardinality: mandatory, one
DocumentNamespace string `json:"documentNamespace"`
// 6.6: External Document References
// Cardinality: optional, one or many
ExternalDocumentReferences []ExternalDocumentRef `json:"externalDocumentRefs,omitempty"`
// 6.11: Document Comment
// Cardinality: optional, one
DocumentComment string `json:"comment,omitempty"`
CreationInfo *CreationInfo `json:"creationInfo"`
Packages []*Package `json:"packages,omitempty"`
Files []*File `json:"files,omitempty"`
OtherLicenses []*OtherLicense `json:"hasExtractedLicensingInfos,omitempty"`
Relationships []*Relationship `json:"relationships,omitempty"`
Annotations []*Annotation `json:"annotations,omitempty"`
Snippets []Snippet `json:"snippets,omitempty"`
// DEPRECATED in version 2.0 of spec
Reviews []*Review `json:"-" yaml:"-"`
}
func (d *Document) ConvertFrom(_ interface{}) error {
d.SPDXVersion = Version
return nil
}
var _ converter.ConvertFrom = (*Document)(nil)
func (d *Document) UnmarshalJSON(b []byte) error {
type doc Document
type extras struct {
DocumentDescribes []common.DocElementID `json:"documentDescribes"`
}
var d2 doc
if err := json.Unmarshal(b, &d2); err != nil {
return err
}
var e extras
if err := json.Unmarshal(b, &e); err != nil {
return err
}
*d = Document(d2)
relationshipExists := map[string]bool{}
serializeRel := func(r *Relationship) string {
refA := r.RefA
refB := r.RefB
rel := r.Relationship
// we need to serialize the opposite for CONTAINED_BY and DESCRIBED_BY
// so that it will match when we try to de-duplicate during deserialization.
switch r.Relationship {
case common.TypeRelationshipContainedBy:
rel = common.TypeRelationshipContains
refA = r.RefB
refB = r.RefA
case common.TypeRelationshipDescribeBy:
rel = common.TypeRelationshipDescribe
refA = r.RefB
refB = r.RefA
}
return fmt.Sprintf("%v-%v->%v", common.RenderDocElementID(refA), rel, common.RenderDocElementID(refB))
}
// remove null relationships
for i := 0; i < len(d.Relationships); i++ {
if d.Relationships[i] == nil {
d.Relationships = append(d.Relationships[0:i], d.Relationships[i+1:]...)
i--
}
}
// index current list of relationships to ensure no duplication
for _, r := range d.Relationships {
relationshipExists[serializeRel(r)] = true
}
// build relationships for documentDescribes field
for _, id := range e.DocumentDescribes {
r := &Relationship{
RefA: common.DocElementID{
ElementRefID: d.SPDXIdentifier,
},
RefB: id,
Relationship: common.TypeRelationshipDescribe,
}
if !relationshipExists[serializeRel(r)] {
d.Relationships = append(d.Relationships, r)
relationshipExists[serializeRel(r)] = true
}
}
// build relationships for package hasFiles field
// build relationships for package hasFiles field
for _, p := range d.Packages {
for _, f := range p.hasFiles {
r := &Relationship{
RefA: common.DocElementID{
ElementRefID: p.PackageSPDXIdentifier,
},
RefB: f,
Relationship: common.TypeRelationshipContains,
}
if !relationshipExists[serializeRel(r)] {
d.Relationships = append(d.Relationships, r)
relationshipExists[serializeRel(r)] = true
}
}
p.hasFiles = nil
}
return nil
}
var _ json.Unmarshaler = (*Document)(nil)
|