200 lines
6.9 KiB
Go
200 lines
6.9 KiB
Go
|
|
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
||
|
|
|
||
|
|
package parser2v3
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"strconv"
|
||
|
|
|
||
|
|
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"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Snippet Information
|
||
|
|
// Cardinality: Optional, Many
|
||
|
|
func (parser *rdfParser2_3) getSnippetInformationFromNode2_3(node *gordfParser.Node) (si *v2_3.Snippet, err error) {
|
||
|
|
si = &v2_3.Snippet{}
|
||
|
|
|
||
|
|
err = setSnippetID(node.ID, si)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, siTriple := range parser.nodeToTriples(node) {
|
||
|
|
switch siTriple.Predicate.ID {
|
||
|
|
case RDF_TYPE:
|
||
|
|
// cardinality: exactly 1
|
||
|
|
case SPDX_SNIPPET_FROM_FILE:
|
||
|
|
// cardinality: exactly 1
|
||
|
|
// file which is associated with the snippet
|
||
|
|
_, err := parser.getFileFromNode(siTriple.Object)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
docElemID, err := ExtractDocElementID(getLastPartOfURI(siTriple.Object.ID))
|
||
|
|
si.SnippetFromFileSPDXIdentifier = docElemID.ElementRefID
|
||
|
|
case SPDX_RANGE:
|
||
|
|
// cardinality: min 1
|
||
|
|
err = parser.setSnippetRangeFromNode(siTriple.Object, si)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
case SPDX_LICENSE_INFO_IN_SNIPPET:
|
||
|
|
// license info in snippet can be NONE, NOASSERTION or SimpleLicensingInfo
|
||
|
|
// using AnyLicenseInfo because it can redirect the request and
|
||
|
|
// can handle NONE & NOASSERTION
|
||
|
|
var anyLicense AnyLicenseInfo
|
||
|
|
anyLicense, err = parser.getAnyLicenseFromNode(siTriple.Object)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("error parsing license info in snippet: %v", err)
|
||
|
|
}
|
||
|
|
si.LicenseInfoInSnippet = append(si.LicenseInfoInSnippet, anyLicense.ToLicenseString())
|
||
|
|
case SPDX_NAME:
|
||
|
|
si.SnippetName = siTriple.Object.ID
|
||
|
|
case SPDX_COPYRIGHT_TEXT:
|
||
|
|
si.SnippetCopyrightText = siTriple.Object.ID
|
||
|
|
case SPDX_LICENSE_COMMENTS:
|
||
|
|
si.SnippetLicenseComments = siTriple.Object.ID
|
||
|
|
case RDFS_COMMENT:
|
||
|
|
si.SnippetComment = siTriple.Object.ID
|
||
|
|
case SPDX_LICENSE_CONCLUDED:
|
||
|
|
var anyLicense AnyLicenseInfo
|
||
|
|
anyLicense, err = parser.getAnyLicenseFromNode(siTriple.Object)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("error parsing license info in snippet: %v", err)
|
||
|
|
}
|
||
|
|
si.SnippetLicenseConcluded = anyLicense.ToLicenseString()
|
||
|
|
default:
|
||
|
|
return nil, fmt.Errorf("unknown predicate %v", siTriple.Predicate.ID)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return si, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// given is the id of the file, sets the snippet to the file in parser.
|
||
|
|
func (parser *rdfParser2_3) setSnippetToFileWithID(snippet *v2_3.Snippet, fileID common.ElementID) error {
|
||
|
|
if parser.files[fileID] == nil {
|
||
|
|
return fmt.Errorf("snippet refers to an undefined file with ID: %s", fileID)
|
||
|
|
}
|
||
|
|
|
||
|
|
// initializing snippet of the files if it is not defined already
|
||
|
|
if parser.files[fileID].Snippets == nil {
|
||
|
|
parser.files[fileID].Snippets = map[common.ElementID]*v2_3.Snippet{}
|
||
|
|
}
|
||
|
|
|
||
|
|
// setting the snippet to the file.
|
||
|
|
parser.files[fileID].Snippets[snippet.SnippetSPDXIdentifier] = snippet
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (parser *rdfParser2_3) setSnippetRangeFromNode(node *gordfParser.Node, si *v2_3.Snippet) error {
|
||
|
|
// for a range object, we can have only 3 associated triples:
|
||
|
|
// node -> RDF_TYPE -> Object
|
||
|
|
// node -> startPointer -> Object
|
||
|
|
// node -> endPointer -> Object
|
||
|
|
associatedTriples := parser.nodeToTriples(node)
|
||
|
|
if len(associatedTriples) != 3 {
|
||
|
|
return fmt.Errorf("range should be associated with exactly 3 triples, got %d", len(associatedTriples))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Triple 1: Predicate=RDF_TYPE
|
||
|
|
typeTriple := rdfwriter.FilterTriples(associatedTriples, &node.ID, &RDF_TYPE, nil)
|
||
|
|
if len(typeTriple) != 1 {
|
||
|
|
// we had 3 associated triples. out of which 2 is start and end pointer,
|
||
|
|
// if we do not have the rdf:type triple as the third one,
|
||
|
|
// we have either extra or undefined predicate.
|
||
|
|
return fmt.Errorf("every object node must be associated with exactly one rdf:type triple, found: %d", len(typeTriple))
|
||
|
|
}
|
||
|
|
|
||
|
|
// getting start pointer
|
||
|
|
startPointerTriples := rdfwriter.FilterTriples(associatedTriples, &node.ID, &PTR_START_POINTER, nil)
|
||
|
|
if len(startPointerTriples) != 1 {
|
||
|
|
return fmt.Errorf("range object must be associated with exactly 1 startPointer, got %d", len(startPointerTriples))
|
||
|
|
}
|
||
|
|
startRangeType, start, err := parser.getPointerFromNode(startPointerTriples[0].Object, si)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("error parsing startPointer: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// getting end pointer
|
||
|
|
endPointerTriples := rdfwriter.FilterTriples(associatedTriples, &node.ID, &PTR_END_POINTER, nil)
|
||
|
|
if len(startPointerTriples) != 1 {
|
||
|
|
return fmt.Errorf("range object must be associated with exactly 1 endPointer, got %d", len(endPointerTriples))
|
||
|
|
}
|
||
|
|
endRangeType, end, err := parser.getPointerFromNode(endPointerTriples[0].Object, si)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("error parsing endPointer: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// return error when start and end pointer type is not same.
|
||
|
|
if startRangeType != endRangeType {
|
||
|
|
return fmt.Errorf("start and end range type doesn't match")
|
||
|
|
}
|
||
|
|
|
||
|
|
si.Ranges = []common.SnippetRange{{
|
||
|
|
StartPointer: common.SnippetRangePointer{FileSPDXIdentifier: si.SnippetFromFileSPDXIdentifier},
|
||
|
|
EndPointer: common.SnippetRangePointer{FileSPDXIdentifier: si.SnippetFromFileSPDXIdentifier},
|
||
|
|
}}
|
||
|
|
|
||
|
|
if startRangeType == LINE_RANGE {
|
||
|
|
si.Ranges[0].StartPointer.LineNumber = start
|
||
|
|
si.Ranges[0].EndPointer.LineNumber = end
|
||
|
|
} else {
|
||
|
|
si.Ranges[0].StartPointer.Offset = start
|
||
|
|
si.Ranges[0].EndPointer.Offset = end
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (parser *rdfParser2_3) getPointerFromNode(node *gordfParser.Node, si *v2_3.Snippet) (rt RangeType, number int, err error) {
|
||
|
|
for _, triple := range parser.nodeToTriples(node) {
|
||
|
|
switch triple.Predicate.ID {
|
||
|
|
case RDF_TYPE:
|
||
|
|
case PTR_REFERENCE:
|
||
|
|
err = parser.parseRangeReference(triple.Object, si)
|
||
|
|
case PTR_OFFSET:
|
||
|
|
number, err = strconv.Atoi(triple.Object.ID)
|
||
|
|
rt = BYTE_RANGE
|
||
|
|
case PTR_LINE_NUMBER:
|
||
|
|
number, err = strconv.Atoi(triple.Object.ID)
|
||
|
|
rt = LINE_RANGE
|
||
|
|
default:
|
||
|
|
err = fmt.Errorf("undefined predicate (%s) for a pointer", triple.Predicate)
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if rt == "" {
|
||
|
|
err = fmt.Errorf("range type not defined for a pointer")
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func (parser *rdfParser2_3) parseRangeReference(node *gordfParser.Node, snippet *v2_3.Snippet) error {
|
||
|
|
// reference is supposed to be either a resource reference to an already
|
||
|
|
// defined or a new file. Unfortunately, I didn't find field where this can be set in the tools-golang data model.
|
||
|
|
// todo: set this reference to the snippet
|
||
|
|
associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil)
|
||
|
|
if len(associatedTriples) == 0 {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
_, err := parser.getFileFromNode(node)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("error parsing a new file in a reference: %v", err)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func setSnippetID(uri string, si *v2_3.Snippet) (err error) {
|
||
|
|
fragment := getLastPartOfURI(uri)
|
||
|
|
si.SnippetSPDXIdentifier, err = ExtractElementID(fragment)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("error setting snippet identifier: %v", uri)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|