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