134 lines
5.3 KiB
Go
134 lines
5.3 KiB
Go
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
|
|
|
package parser2v3
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
gordfParser "github.com/spdx/gordf/rdfloader/parser"
|
|
gordfWriter "github.com/spdx/gordf/rdfwriter"
|
|
"github.com/spdx/tools-golang/spdx/common"
|
|
"github.com/spdx/tools-golang/spdx/v2_3"
|
|
)
|
|
|
|
// returns a new instance of rdfParser2_3 given the gordf object and nodeToTriples mapping
|
|
func NewParser2_3(gordfParserObj *gordfParser.Parser, nodeToTriples map[string][]*gordfParser.Triple) *rdfParser2_3 {
|
|
parser := rdfParser2_3{
|
|
gordfParserObj: gordfParserObj,
|
|
nodeStringToTriples: nodeToTriples,
|
|
doc: &v2_3.Document{
|
|
ExternalDocumentReferences: []v2_3.ExternalDocumentRef{},
|
|
CreationInfo: &v2_3.CreationInfo{},
|
|
Packages: []*v2_3.Package{},
|
|
Files: []*v2_3.File{},
|
|
OtherLicenses: []*v2_3.OtherLicense{},
|
|
Relationships: []*v2_3.Relationship{},
|
|
Annotations: []*v2_3.Annotation{},
|
|
Reviews: []*v2_3.Review{},
|
|
},
|
|
files: map[common.ElementID]*v2_3.File{},
|
|
assocWithPackage: map[common.ElementID]bool{},
|
|
cache: map[string]*nodeState{},
|
|
}
|
|
return &parser
|
|
}
|
|
|
|
// main function which takes in a gordfParser and returns
|
|
// a spdxDocument model or the error encountered while parsing it
|
|
func LoadFromGoRDFParser(gordfParserObj *gordfParser.Parser) (*v2_3.Document, error) {
|
|
// nodeToTriples is a mapping from a node to list of triples.
|
|
// for every node in the set of subjects of all the triples,
|
|
// it provides a list of triples that are associated with that subject node.
|
|
nodeToTriples := gordfWriter.GetNodeToTriples(gordfParserObj.Triples)
|
|
parser := NewParser2_3(gordfParserObj, nodeToTriples)
|
|
|
|
spdxDocumentNode, err := parser.getSpdxDocNode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = parser.parseSpdxDocumentNode(spdxDocumentNode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// parsing other root elements
|
|
for _, rootNode := range gordfWriter.GetRootNodes(parser.gordfParserObj.Triples) {
|
|
typeTriples := gordfWriter.FilterTriples(gordfParserObj.Triples, &rootNode.ID, &RDF_TYPE, nil)
|
|
if len(typeTriples) != 1 {
|
|
return nil, fmt.Errorf("every node must be associated with exactly 1 type Triple. found %d type triples", len(typeTriples))
|
|
}
|
|
switch typeTriples[0].Object.ID {
|
|
case SPDX_SPDX_DOCUMENT_CAPITALIZED:
|
|
continue // it is already parsed.
|
|
case SPDX_SNIPPET:
|
|
snippet, err := parser.getSnippetInformationFromNode2_3(typeTriples[0].Subject)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error parsing a snippet: %v", err)
|
|
}
|
|
err = parser.setSnippetToFileWithID(snippet, snippet.SnippetFromFileSPDXIdentifier)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// todo: check other root node attributes.
|
|
default:
|
|
continue
|
|
// because in rdf it is quite possible that the root node is an
|
|
// element that has been used in the some other element as a child
|
|
}
|
|
}
|
|
|
|
// parsing packages and files sets the files to a files variable which is
|
|
// associated with the parser and not the document. following method is
|
|
// necessary to transfer the files which are not set in the packages to the
|
|
// Files attribute of the document
|
|
// WARNING: do not relocate following function call. It must be at the end of the function
|
|
parser.setUnpackagedFiles()
|
|
return parser.doc, nil
|
|
}
|
|
|
|
// from the given parser object, returns the SpdxDocument Node defined in the root elements.
|
|
// returns error if the document is associated with no SpdxDocument or
|
|
// associated with more than one SpdxDocument node.
|
|
func (parser *rdfParser2_3) getSpdxDocNode() (node *gordfParser.Node, err error) {
|
|
/* Possible Questions:
|
|
1. why are you traversing the root nodes only? why not directly filter out
|
|
all the triples with rdf:type=spdx:SpdxDocument?
|
|
Ans: It is quite possible that the relatedElement or any other attribute
|
|
to have dependency of another SpdxDocument. In that case, that
|
|
element will reference the dependency using SpdxDocument tag which will
|
|
cause false positives when direct filtering is done.
|
|
*/
|
|
// iterate over root nodes and find the node which has a property of rdf:type=spdx:SpdxDocument
|
|
var spdxDocNode *gordfParser.Node
|
|
for _, rootNode := range gordfWriter.GetRootNodes(parser.gordfParserObj.Triples) {
|
|
typeTriples := gordfWriter.FilterTriples(
|
|
parser.nodeToTriples(rootNode), // triples
|
|
&rootNode.ID, // Subject
|
|
&RDF_TYPE, // Predicate
|
|
nil, // Object
|
|
)
|
|
|
|
if typeTriples[0].Object.ID == SPDX_SPDX_DOCUMENT_CAPITALIZED {
|
|
// we found a SpdxDocument Node
|
|
|
|
// must be associated with exactly one rdf:type.
|
|
if len(typeTriples) != 1 {
|
|
return nil, fmt.Errorf("rootNode (%v) must be associated with exactly one"+
|
|
" triple of predicate rdf:type, found %d triples", rootNode, len(typeTriples))
|
|
}
|
|
|
|
// checking if we've already found a node and it is not same as the current one.
|
|
if spdxDocNode != nil && spdxDocNode.ID != typeTriples[0].Subject.ID {
|
|
return nil, fmt.Errorf("found more than one SpdxDocument Node (%v and %v)", spdxDocNode, typeTriples[0].Subject)
|
|
}
|
|
spdxDocNode = typeTriples[0].Subject
|
|
}
|
|
}
|
|
if spdxDocNode == nil {
|
|
return nil, errors.New("RDF files must be associated with a SpdxDocument tag. No tag found")
|
|
}
|
|
return spdxDocNode, nil
|
|
}
|