157 lines
4.8 KiB
Go
157 lines
4.8 KiB
Go
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
|
|
|
package parser2v3
|
|
|
|
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_3"
|
|
)
|
|
|
|
// 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_3) parseRelationship(triple *gordfParser.Triple) (err error) {
|
|
reln := v2_3.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_3) parseRelatedElementFromTriple(reln *v2_3.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)
|
|
}
|