xref: /aosp_15_r20/external/spdx-tools/rdfloader/parser2v2/parse_relationship.go (revision ba677afa8f67bb56cbc794f4d0e378e0da058e16)
1*ba677afaSXin Li// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2*ba677afaSXin Li
3*ba677afaSXin Lipackage parser2v2
4*ba677afaSXin Li
5*ba677afaSXin Liimport (
6*ba677afaSXin Li	"fmt"
7*ba677afaSXin Li	"strings"
8*ba677afaSXin Li
9*ba677afaSXin Li	gordfParser "github.com/spdx/gordf/rdfloader/parser"
10*ba677afaSXin Li	"github.com/spdx/gordf/rdfwriter"
11*ba677afaSXin Li	"github.com/spdx/tools-golang/spdx/common"
12*ba677afaSXin Li	"github.com/spdx/tools-golang/spdx/v2_2"
13*ba677afaSXin Li)
14*ba677afaSXin Li
15*ba677afaSXin Li// parsing the relationship that exists in the rdf document.
16*ba677afaSXin Li// Relationship is of type RefA relationType RefB.
17*ba677afaSXin Li// parsing the relationship appends the relationship to the current document's
18*ba677afaSXin Li// Relationships Slice.
19*ba677afaSXin Lifunc (parser *rdfParser2_2) parseRelationship(triple *gordfParser.Triple) (err error) {
20*ba677afaSXin Li	reln := v2_2.Relationship{}
21*ba677afaSXin Li
22*ba677afaSXin Li	reln.RefA, err = getReferenceFromURI(triple.Subject.ID)
23*ba677afaSXin Li	if err != nil {
24*ba677afaSXin Li		return err
25*ba677afaSXin Li	}
26*ba677afaSXin Li
27*ba677afaSXin Li	currState := parser.cache[triple.Object.ID]
28*ba677afaSXin Li	if currState == nil {
29*ba677afaSXin Li		// there is no entry about the state of current package node.
30*ba677afaSXin Li		// this is the first time we're seeing this node.
31*ba677afaSXin Li		parser.cache[triple.Object.ID] = &nodeState{
32*ba677afaSXin Li			object: reln,
33*ba677afaSXin Li			Color:  WHITE,
34*ba677afaSXin Li		}
35*ba677afaSXin Li	} else if currState.Color == GREY {
36*ba677afaSXin Li		// we have already started parsing this relationship node and we needn't parse it again.
37*ba677afaSXin Li		return nil
38*ba677afaSXin Li	}
39*ba677afaSXin Li
40*ba677afaSXin Li	// setting color of the state to grey to indicate that we've started to
41*ba677afaSXin Li	// parse this node once.
42*ba677afaSXin Li	parser.cache[triple.Object.ID].Color = GREY
43*ba677afaSXin Li
44*ba677afaSXin Li	// setting state color to black to indicate when we're done parsing this node.
45*ba677afaSXin Li	defer func() { parser.cache[triple.Object.ID].Color = BLACK }()
46*ba677afaSXin Li
47*ba677afaSXin Li	for _, subTriple := range parser.nodeToTriples(triple.Object) {
48*ba677afaSXin Li		switch subTriple.Predicate.ID {
49*ba677afaSXin Li		case SPDX_RELATIONSHIP_TYPE:
50*ba677afaSXin Li			// cardinality: exactly 1
51*ba677afaSXin Li			reln.Relationship, err = getRelationshipTypeFromURI(subTriple.Object.ID)
52*ba677afaSXin Li		case RDF_TYPE:
53*ba677afaSXin Li			// cardinality: exactly 1
54*ba677afaSXin Li			continue
55*ba677afaSXin Li		case SPDX_RELATED_SPDX_ELEMENT:
56*ba677afaSXin Li			// cardinality: exactly 1
57*ba677afaSXin Li			// assumes: spdx-element is a uri
58*ba677afaSXin Li			reln.RefB, err = getReferenceFromURI(subTriple.Object.ID)
59*ba677afaSXin Li			if err != nil {
60*ba677afaSXin Li				return err
61*ba677afaSXin Li			}
62*ba677afaSXin Li
63*ba677afaSXin Li			relatedSpdxElementTriples := parser.nodeToTriples(subTriple.Object)
64*ba677afaSXin Li			if len(relatedSpdxElementTriples) == 0 {
65*ba677afaSXin Li				continue
66*ba677afaSXin Li			}
67*ba677afaSXin Li
68*ba677afaSXin Li			typeTriples := rdfwriter.FilterTriples(relatedSpdxElementTriples, &subTriple.Object.ID, &RDF_TYPE, nil)
69*ba677afaSXin Li			if len(typeTriples) != 1 {
70*ba677afaSXin Li				return fmt.Errorf("expected %s to have exactly one rdf:type triple. found %d triples", subTriple.Object, len(typeTriples))
71*ba677afaSXin Li			}
72*ba677afaSXin Li			err = parser.parseRelatedElementFromTriple(&reln, typeTriples[0])
73*ba677afaSXin Li			if err != nil {
74*ba677afaSXin Li				return err
75*ba677afaSXin Li			}
76*ba677afaSXin Li		case RDFS_COMMENT:
77*ba677afaSXin Li			// cardinality: max 1
78*ba677afaSXin Li			reln.RelationshipComment = subTriple.Object.ID
79*ba677afaSXin Li		default:
80*ba677afaSXin Li			return fmt.Errorf("unexpected predicate id: %s", subTriple.Predicate.ID)
81*ba677afaSXin Li		}
82*ba677afaSXin Li		if err != nil {
83*ba677afaSXin Li			return err
84*ba677afaSXin Li		}
85*ba677afaSXin Li	}
86*ba677afaSXin Li	parser.doc.Relationships = append(parser.doc.Relationships, &reln)
87*ba677afaSXin Li	return nil
88*ba677afaSXin Li}
89*ba677afaSXin Li
90*ba677afaSXin Lifunc (parser *rdfParser2_2) parseRelatedElementFromTriple(reln *v2_2.Relationship, triple *gordfParser.Triple) error {
91*ba677afaSXin Li	// iterate over relatedElement Type and check which SpdxElement it is.
92*ba677afaSXin Li	var err error
93*ba677afaSXin Li	switch triple.Object.ID {
94*ba677afaSXin Li	case SPDX_FILE:
95*ba677afaSXin Li		file, err := parser.getFileFromNode(triple.Subject)
96*ba677afaSXin Li		if err != nil {
97*ba677afaSXin Li			return fmt.Errorf("error setting a file: %v", err)
98*ba677afaSXin Li		}
99*ba677afaSXin Li		reln.RefB = common.DocElementID{
100*ba677afaSXin Li			DocumentRefID: "",
101*ba677afaSXin Li			ElementRefID:  file.FileSPDXIdentifier,
102*ba677afaSXin Li		}
103*ba677afaSXin Li
104*ba677afaSXin Li	case SPDX_PACKAGE:
105*ba677afaSXin Li		pkg, err := parser.getPackageFromNode(triple.Subject)
106*ba677afaSXin Li		if err != nil {
107*ba677afaSXin Li			return fmt.Errorf("error setting a package inside a relationship: %v", err)
108*ba677afaSXin Li		}
109*ba677afaSXin Li		reln.RefB = common.DocElementID{
110*ba677afaSXin Li			DocumentRefID: "",
111*ba677afaSXin Li			ElementRefID:  pkg.PackageSPDXIdentifier,
112*ba677afaSXin Li		}
113*ba677afaSXin Li
114*ba677afaSXin Li	case SPDX_SPDX_ELEMENT:
115*ba677afaSXin Li		// it shouldn't be associated with any other triple.
116*ba677afaSXin Li		// it must be a uri reference.
117*ba677afaSXin Li		reln.RefB, err = ExtractDocElementID(getLastPartOfURI(triple.Subject.ID))
118*ba677afaSXin Li		if err != nil {
119*ba677afaSXin Li			return err
120*ba677afaSXin Li		}
121*ba677afaSXin Li	default:
122*ba677afaSXin Li		return fmt.Errorf("undefined relatedElement %s found while parsing relationship", triple.Object.ID)
123*ba677afaSXin Li	}
124*ba677afaSXin Li	return nil
125*ba677afaSXin Li}
126*ba677afaSXin Li
127*ba677afaSXin Li// references like RefA and RefB of any relationship
128*ba677afaSXin Lifunc getReferenceFromURI(uri string) (common.DocElementID, error) {
129*ba677afaSXin Li	fragment := getLastPartOfURI(uri)
130*ba677afaSXin Li	switch strings.ToLower(strings.TrimSpace(fragment)) {
131*ba677afaSXin Li	case "noassertion", "none":
132*ba677afaSXin Li		return common.DocElementID{
133*ba677afaSXin Li			DocumentRefID: "",
134*ba677afaSXin Li			ElementRefID:  common.ElementID(strings.ToUpper(fragment)),
135*ba677afaSXin Li		}, nil
136*ba677afaSXin Li	}
137*ba677afaSXin Li	return ExtractDocElementID(fragment)
138*ba677afaSXin Li}
139*ba677afaSXin Li
140*ba677afaSXin Li// note: relationshipType is case sensitive.
141*ba677afaSXin Lifunc getRelationshipTypeFromURI(relnTypeURI string) (string, error) {
142*ba677afaSXin Li	relnTypeURI = strings.TrimSpace(relnTypeURI)
143*ba677afaSXin Li	lastPart := getLastPartOfURI(relnTypeURI)
144*ba677afaSXin Li	if !strings.HasPrefix(lastPart, PREFIX_RELATIONSHIP_TYPE) {
145*ba677afaSXin Li		return "", fmt.Errorf("relationshipType must start with %s. found %s", PREFIX_RELATIONSHIP_TYPE, lastPart)
146*ba677afaSXin Li	}
147*ba677afaSXin Li	lastPart = strings.TrimPrefix(lastPart, PREFIX_RELATIONSHIP_TYPE)
148*ba677afaSXin Li
149*ba677afaSXin Li	lastPart = strings.TrimSpace(lastPart)
150*ba677afaSXin Li	for _, validRelationshipType := range AllRelationshipTypes() {
151*ba677afaSXin Li		if lastPart == validRelationshipType {
152*ba677afaSXin Li			return lastPart, nil
153*ba677afaSXin Li		}
154*ba677afaSXin Li	}
155*ba677afaSXin Li	return "", fmt.Errorf("unknown relationshipType: '%s'", lastPart)
156*ba677afaSXin Li}
157