aboutsummaryrefslogtreecommitdiff
path: root/spdxlib/described_elements.go
blob: 6158f88fa0ab62706f8df551e2f7f3e1e87d0dff (plain)
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
// Package spdxlib contains convenience and utility functions for working
// with an SPDX document that has already been created in memory.
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
package spdxlib

import (
	"fmt"
	"sort"

	"github.com/spdx/tools-golang/spdx"
)

// GetDescribedPackageIDs2_1 returns a slice of ElementIDs for all Packages
// in this Document that it "describes," according to SPDX rules:
// - If the document has only one Package, its ID is returned.
// - If the document has 2+ Packages, it returns the IDs of those that have
//   a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT. If no
// -
func GetDescribedPackageIDs2_1(doc *spdx.Document2_1) ([]spdx.ElementID, error) {
	// if nil Packages map or zero packages in it, return empty slice
	if doc.Packages == nil {
		return nil, fmt.Errorf("Packages map is nil")
	}
	if len(doc.Packages) == 0 {
		return nil, fmt.Errorf("no Packages in Document")
	}
	if len(doc.Packages) == 1 {
		// get first (only) one and return its ID
		for i := range doc.Packages {
			return []spdx.ElementID{i}, nil
		}
	}

	// two or more packages, so we need to go through the relationships,
	// find DESCRIBES or DESCRIBED_BY for this DOCUMENT, verify they are
	// valid IDs in this document's packages, and return them
	if doc.Relationships == nil {
		return nil, fmt.Errorf("multiple Packages in Document but Relationships slice is nil")
	}
	// collect IDs as strings so we can sort them easily
	eIDStrs := []string{}
	for _, rln := range doc.Relationships {
		if rln.Relationship == "DESCRIBES" && rln.RefA == spdx.MakeDocElementID("", "DOCUMENT") {
			// confirm RefB is actually a package in this document
			if _, ok := doc.Packages[rln.RefB.ElementRefID]; !ok {
				// if it's an unpackaged file, that's valid (no error) but don't return it
				if _, ok2 := doc.UnpackagedFiles[rln.RefB.ElementRefID]; !ok2 {
					return nil, fmt.Errorf("Document DESCRIBES %s but no such Package or unpackaged File", string(rln.RefB.ElementRefID))
				}
			}
			eIDStrs = append(eIDStrs, string(rln.RefB.ElementRefID))
		}
		if rln.Relationship == "DESCRIBED_BY" && rln.RefB == spdx.MakeDocElementID("", "DOCUMENT") {
			// confirm RefA is actually a package in this document
			// if it's an unpackaged file, that's valid (no error) but don't return it
			if _, ok := doc.Packages[rln.RefA.ElementRefID]; !ok {
				// if it's an unpackaged file, that's valid (no error) but don't return it
				if _, ok2 := doc.UnpackagedFiles[rln.RefA.ElementRefID]; !ok2 {
					return nil, fmt.Errorf("%s DESCRIBED_BY Document but no such Package or unpackaged File", string(rln.RefA.ElementRefID))
				}
			}
			eIDStrs = append(eIDStrs, string(rln.RefA.ElementRefID))
		}
	}
	if len(eIDStrs) == 0 {
		return nil, fmt.Errorf("no DESCRIBES or DESCRIBED_BY relationships found for this Document")
	}
	// sort them, convert back to ElementIDs and return
	sort.Strings(eIDStrs)
	eIDs := []spdx.ElementID{}
	for _, eIDStr := range eIDStrs {
		eIDs = append(eIDs, spdx.ElementID(eIDStr))
	}
	return eIDs, nil
}

// GetDescribedPackageIDs2_2 returns a slice of ElementIDs for all Packages
// in this Document that it "describes," according to SPDX rules:
// - If the document has only one Package, its ID is returned.
// - If the document has 2+ Packages, it returns the IDs of those that have
//   a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT. If no
// -
func GetDescribedPackageIDs2_2(doc *spdx.Document2_2) ([]spdx.ElementID, error) {
	// if nil Packages map or zero packages in it, return empty slice
	if doc.Packages == nil {
		return nil, fmt.Errorf("Packages map is nil")
	}
	if len(doc.Packages) == 0 {
		return nil, fmt.Errorf("no Packages in Document")
	}
	if len(doc.Packages) == 1 {
		// get first (only) one and return its ID
		for i := range doc.Packages {
			return []spdx.ElementID{i}, nil
		}
	}

	// two or more packages, so we need to go through the relationships,
	// find DESCRIBES or DESCRIBED_BY for this DOCUMENT, verify they are
	// valid IDs in this document's packages, and return them
	if doc.Relationships == nil {
		return nil, fmt.Errorf("multiple Packages in Document but Relationships slice is nil")
	}
	// collect IDs as strings so we can sort them easily
	eIDStrs := []string{}
	for _, rln := range doc.Relationships {
		if rln.Relationship == "DESCRIBES" && rln.RefA == spdx.MakeDocElementID("", "DOCUMENT") {
			// confirm RefB is actually a package in this document
			if _, ok := doc.Packages[rln.RefB.ElementRefID]; !ok {
				// if it's an unpackaged file, that's valid (no error) but don't return it
				if _, ok2 := doc.UnpackagedFiles[rln.RefB.ElementRefID]; !ok2 {
					return nil, fmt.Errorf("Document DESCRIBES %s but no such Package or unpackaged File", string(rln.RefB.ElementRefID))
				}
			}
			eIDStrs = append(eIDStrs, string(rln.RefB.ElementRefID))
		}
		if rln.Relationship == "DESCRIBED_BY" && rln.RefB == spdx.MakeDocElementID("", "DOCUMENT") {
			// confirm RefA is actually a package in this document
			// if it's an unpackaged file, that's valid (no error) but don't return it
			if _, ok := doc.Packages[rln.RefA.ElementRefID]; !ok {
				// if it's an unpackaged file, that's valid (no error) but don't return it
				if _, ok2 := doc.UnpackagedFiles[rln.RefA.ElementRefID]; !ok2 {
					return nil, fmt.Errorf("%s DESCRIBED_BY Document but no such Package or unpackaged File", string(rln.RefA.ElementRefID))
				}
			}
			eIDStrs = append(eIDStrs, string(rln.RefA.ElementRefID))
		}
	}
	if len(eIDStrs) == 0 {
		return nil, fmt.Errorf("no DESCRIBES or DESCRIBED_BY relationships found for this Document")
	}
	// sort them, convert back to ElementIDs and return
	sort.Strings(eIDStrs)
	eIDs := []spdx.ElementID{}
	for _, eIDStr := range eIDStrs {
		eIDs = append(eIDs, spdx.ElementID(eIDStr))
	}
	return eIDs, nil
}