xref: /aosp_15_r20/external/spdx-tools/spdxlib/described_elements.go (revision ba677afa8f67bb56cbc794f4d0e378e0da058e16)
1// Package spdxlib contains convenience and utility functions for working
2// with an SPDX document that has already been created in memory.
3// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
4package spdxlib
5
6import (
7	"fmt"
8
9	"github.com/spdx/tools-golang/spdx/common"
10	"github.com/spdx/tools-golang/spdx/v2_1"
11	"github.com/spdx/tools-golang/spdx/v2_2"
12	"github.com/spdx/tools-golang/spdx/v2_3"
13)
14
15// GetDescribedPackageIDs2_1 returns a slice of ElementIDs for all Packages
16// in this Document that it "describes," according to SPDX rules:
17// - If the document has only one Package, its ID is returned.
18// - If the document has 2+ Packages, it returns the IDs of those that have
19//   a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT.
20func GetDescribedPackageIDs2_1(doc *v2_1.Document) ([]common.ElementID, error) {
21	// if nil Packages map or zero packages in it, return empty slice
22	if doc.Packages == nil {
23		return nil, fmt.Errorf("Packages map is nil")
24	}
25	if len(doc.Packages) == 0 {
26		return nil, fmt.Errorf("no Packages in Document")
27	}
28	if len(doc.Packages) == 1 {
29		// get first (only) one and return its ID
30		for _, pkg := range doc.Packages {
31			return []common.ElementID{pkg.PackageSPDXIdentifier}, nil
32		}
33	}
34
35	// two or more packages, so we need to go through the relationships,
36	// find DESCRIBES or DESCRIBED_BY for this DOCUMENT, verify they are
37	// valid IDs in this document's packages, and return them
38	if doc.Relationships == nil {
39		return nil, fmt.Errorf("multiple Packages in Document but Relationships slice is nil")
40	}
41
42	eIDs, err := FilterRelationships2_1(doc, func(relationship *v2_1.Relationship) *common.ElementID {
43		refDocument := common.MakeDocElementID("", "DOCUMENT")
44
45		if relationship.Relationship == "DESCRIBES" && relationship.RefA == refDocument {
46			return &relationship.RefB.ElementRefID
47		} else if relationship.Relationship == "DESCRIBED_BY" && relationship.RefB == refDocument {
48			return &relationship.RefA.ElementRefID
49		}
50
51		return nil
52	})
53	if err != nil {
54		return nil, err
55	}
56
57	if len(eIDs) == 0 {
58		return nil, fmt.Errorf("no DESCRIBES or DESCRIBED_BY relationships found for this Document")
59	}
60
61	eIDs = SortElementIDs(eIDs)
62
63	return eIDs, nil
64}
65
66// GetDescribedPackageIDs2_2 returns a slice of ElementIDs for all Packages
67// in this Document that it "describes," according to SPDX rules:
68// - If the document has only one Package, its ID is returned.
69// - If the document has 2+ Packages, it returns the IDs of those that have
70//   a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT.
71func GetDescribedPackageIDs2_2(doc *v2_2.Document) ([]common.ElementID, error) {
72	// if nil Packages map or zero packages in it, return empty slice
73	if doc.Packages == nil {
74		return nil, fmt.Errorf("Packages map is nil")
75	}
76	if len(doc.Packages) == 0 {
77		return nil, fmt.Errorf("no Packages in Document")
78	}
79	if len(doc.Packages) == 1 {
80		// get first (only) one and return its ID
81		for _, pkg := range doc.Packages {
82			return []common.ElementID{pkg.PackageSPDXIdentifier}, nil
83		}
84	}
85
86	// two or more packages, so we need to go through the relationships,
87	// find DESCRIBES or DESCRIBED_BY for this DOCUMENT, verify they are
88	// valid IDs in this document's packages, and return them
89	if doc.Relationships == nil {
90		return nil, fmt.Errorf("multiple Packages in Document but Relationships slice is nil")
91	}
92
93	eIDs, err := FilterRelationships2_2(doc, func(relationship *v2_2.Relationship) *common.ElementID {
94		refDocument := common.MakeDocElementID("", "DOCUMENT")
95
96		if relationship.Relationship == "DESCRIBES" && relationship.RefA == refDocument {
97			return &relationship.RefB.ElementRefID
98		} else if relationship.Relationship == "DESCRIBED_BY" && relationship.RefB == refDocument {
99			return &relationship.RefA.ElementRefID
100		}
101
102		return nil
103	})
104	if err != nil {
105		return nil, err
106	}
107
108	if len(eIDs) == 0 {
109		return nil, fmt.Errorf("no DESCRIBES or DESCRIBED_BY relationships found for this Document")
110	}
111
112	eIDs = SortElementIDs(eIDs)
113
114	return eIDs, nil
115}
116
117// GetDescribedPackageIDs2_3 returns a slice of ElementIDs for all Packages
118// in this Document that it "describes," according to SPDX rules:
119// - If the document has only one Package, its ID is returned.
120// - If the document has 2+ Packages, it returns the IDs of those that have
121//   a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT.
122func GetDescribedPackageIDs2_3(doc *v2_3.Document) ([]common.ElementID, error) {
123	// if nil Packages map or zero packages in it, return empty slice
124	if doc.Packages == nil {
125		return nil, fmt.Errorf("Packages map is nil")
126	}
127	if len(doc.Packages) == 0 {
128		return nil, fmt.Errorf("no Packages in Document")
129	}
130	if len(doc.Packages) == 1 {
131		// get first (only) one and return its ID
132		for _, pkg := range doc.Packages {
133			return []common.ElementID{pkg.PackageSPDXIdentifier}, nil
134		}
135	}
136
137	// two or more packages, so we need to go through the relationships,
138	// find DESCRIBES or DESCRIBED_BY for this DOCUMENT, verify they are
139	// valid IDs in this document's packages, and return them
140	if doc.Relationships == nil {
141		return nil, fmt.Errorf("multiple Packages in Document but Relationships slice is nil")
142	}
143
144	eIDs, err := FilterRelationships2_3(doc, func(relationship *v2_3.Relationship) *common.ElementID {
145		refDocument := common.MakeDocElementID("", "DOCUMENT")
146
147		if relationship.Relationship == "DESCRIBES" && relationship.RefA == refDocument {
148			return &relationship.RefB.ElementRefID
149		} else if relationship.Relationship == "DESCRIBED_BY" && relationship.RefB == refDocument {
150			return &relationship.RefA.ElementRefID
151		}
152
153		return nil
154	})
155	if err != nil {
156		return nil, err
157	}
158
159	if len(eIDs) == 0 {
160		return nil, fmt.Errorf("no DESCRIBES or DESCRIBED_BY relationships found for this Document")
161	}
162
163	eIDs = SortElementIDs(eIDs)
164
165	return eIDs, nil
166}
167