| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| |
| package parser2v2 |
| |
| import ( |
| "fmt" |
| "strings" |
| |
| gordfParser "github.com/spdx/gordf/rdfloader/parser" |
| "github.com/spdx/gordf/rdfwriter" |
| "github.com/spdx/tools-golang/spdx/common" |
| "github.com/spdx/tools-golang/spdx/v2_2" |
| ) |
| |
| // parsing the relationship that exists in the rdf document. |
| // Relationship is of type RefA relationType RefB. |
| // parsing the relationship appends the relationship to the current document's |
| // Relationships Slice. |
| func (parser *rdfParser2_2) parseRelationship(triple *gordfParser.Triple) (err error) { |
| reln := v2_2.Relationship{} |
| |
| reln.RefA, err = getReferenceFromURI(triple.Subject.ID) |
| if err != nil { |
| return err |
| } |
| |
| currState := parser.cache[triple.Object.ID] |
| if currState == nil { |
| // there is no entry about the state of current package node. |
| // this is the first time we're seeing this node. |
| parser.cache[triple.Object.ID] = &nodeState{ |
| object: reln, |
| Color: WHITE, |
| } |
| } else if currState.Color == GREY { |
| // we have already started parsing this relationship node and we needn't parse it again. |
| return nil |
| } |
| |
| // setting color of the state to grey to indicate that we've started to |
| // parse this node once. |
| parser.cache[triple.Object.ID].Color = GREY |
| |
| // setting state color to black to indicate when we're done parsing this node. |
| defer func() { parser.cache[triple.Object.ID].Color = BLACK }() |
| |
| for _, subTriple := range parser.nodeToTriples(triple.Object) { |
| switch subTriple.Predicate.ID { |
| case SPDX_RELATIONSHIP_TYPE: |
| // cardinality: exactly 1 |
| reln.Relationship, err = getRelationshipTypeFromURI(subTriple.Object.ID) |
| case RDF_TYPE: |
| // cardinality: exactly 1 |
| continue |
| case SPDX_RELATED_SPDX_ELEMENT: |
| // cardinality: exactly 1 |
| // assumes: spdx-element is a uri |
| reln.RefB, err = getReferenceFromURI(subTriple.Object.ID) |
| if err != nil { |
| return err |
| } |
| |
| relatedSpdxElementTriples := parser.nodeToTriples(subTriple.Object) |
| if len(relatedSpdxElementTriples) == 0 { |
| continue |
| } |
| |
| typeTriples := rdfwriter.FilterTriples(relatedSpdxElementTriples, &subTriple.Object.ID, &RDF_TYPE, nil) |
| if len(typeTriples) != 1 { |
| return fmt.Errorf("expected %s to have exactly one rdf:type triple. found %d triples", subTriple.Object, len(typeTriples)) |
| } |
| err = parser.parseRelatedElementFromTriple(&reln, typeTriples[0]) |
| if err != nil { |
| return err |
| } |
| case RDFS_COMMENT: |
| // cardinality: max 1 |
| reln.RelationshipComment = subTriple.Object.ID |
| default: |
| return fmt.Errorf("unexpected predicate id: %s", subTriple.Predicate.ID) |
| } |
| if err != nil { |
| return err |
| } |
| } |
| parser.doc.Relationships = append(parser.doc.Relationships, &reln) |
| return nil |
| } |
| |
| func (parser *rdfParser2_2) parseRelatedElementFromTriple(reln *v2_2.Relationship, triple *gordfParser.Triple) error { |
| // iterate over relatedElement Type and check which SpdxElement it is. |
| var err error |
| switch triple.Object.ID { |
| case SPDX_FILE: |
| file, err := parser.getFileFromNode(triple.Subject) |
| if err != nil { |
| return fmt.Errorf("error setting a file: %v", err) |
| } |
| reln.RefB = common.DocElementID{ |
| DocumentRefID: "", |
| ElementRefID: file.FileSPDXIdentifier, |
| } |
| |
| case SPDX_PACKAGE: |
| pkg, err := parser.getPackageFromNode(triple.Subject) |
| if err != nil { |
| return fmt.Errorf("error setting a package inside a relationship: %v", err) |
| } |
| reln.RefB = common.DocElementID{ |
| DocumentRefID: "", |
| ElementRefID: pkg.PackageSPDXIdentifier, |
| } |
| |
| case SPDX_SPDX_ELEMENT: |
| // it shouldn't be associated with any other triple. |
| // it must be a uri reference. |
| reln.RefB, err = ExtractDocElementID(getLastPartOfURI(triple.Subject.ID)) |
| if err != nil { |
| return err |
| } |
| default: |
| return fmt.Errorf("undefined relatedElement %s found while parsing relationship", triple.Object.ID) |
| } |
| return nil |
| } |
| |
| // references like RefA and RefB of any relationship |
| func getReferenceFromURI(uri string) (common.DocElementID, error) { |
| fragment := getLastPartOfURI(uri) |
| switch strings.ToLower(strings.TrimSpace(fragment)) { |
| case "noassertion", "none": |
| return common.DocElementID{ |
| DocumentRefID: "", |
| ElementRefID: common.ElementID(strings.ToUpper(fragment)), |
| }, nil |
| } |
| return ExtractDocElementID(fragment) |
| } |
| |
| // note: relationshipType is case sensitive. |
| func getRelationshipTypeFromURI(relnTypeURI string) (string, error) { |
| relnTypeURI = strings.TrimSpace(relnTypeURI) |
| lastPart := getLastPartOfURI(relnTypeURI) |
| if !strings.HasPrefix(lastPart, PREFIX_RELATIONSHIP_TYPE) { |
| return "", fmt.Errorf("relationshipType must start with %s. found %s", PREFIX_RELATIONSHIP_TYPE, lastPart) |
| } |
| lastPart = strings.TrimPrefix(lastPart, PREFIX_RELATIONSHIP_TYPE) |
| |
| lastPart = strings.TrimSpace(lastPart) |
| for _, validRelationshipType := range AllRelationshipTypes() { |
| if lastPart == validRelationshipType { |
| return lastPart, nil |
| } |
| } |
| return "", fmt.Errorf("unknown relationshipType: '%s'", lastPart) |
| } |