1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
package reader
import (
"fmt"
"strconv"
gordfParser "github.com/spdx/gordf/rdfloader/parser"
"github.com/spdx/gordf/rdfwriter"
"github.com/spdx/tools-golang/spdx/v2/common"
"github.com/spdx/tools-golang/spdx/v2/v2_2"
)
// Snippet Information
// Cardinality: Optional, Many
func (parser *rdfParser2_2) getSnippetInformationFromNode2_2(node *gordfParser.Node) (si *v2_2.Snippet, err error) {
si = &v2_2.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))
if err != nil {
return nil, err
}
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_2) setSnippetToFileWithID(snippet *v2_2.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_2.Snippet{}
}
// setting the snippet to the file.
parser.files[fileID].Snippets[snippet.SnippetSPDXIdentifier] = snippet
return nil
}
func (parser *rdfParser2_2) setSnippetRangeFromNode(node *gordfParser.Node, si *v2_2.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_2) getPointerFromNode(node *gordfParser.Node, si *v2_2.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_2) parseRangeReference(node *gordfParser.Node, snippet *v2_2.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_2.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
}
|