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