1*ba677afaSXin Li// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 2*ba677afaSXin Li 3*ba677afaSXin Lipackage parser2v3 4*ba677afaSXin Li 5*ba677afaSXin Liimport ( 6*ba677afaSXin Li "fmt" 7*ba677afaSXin Li "strconv" 8*ba677afaSXin Li 9*ba677afaSXin Li gordfParser "github.com/spdx/gordf/rdfloader/parser" 10*ba677afaSXin Li "github.com/spdx/gordf/rdfwriter" 11*ba677afaSXin Li "github.com/spdx/tools-golang/spdx/common" 12*ba677afaSXin Li "github.com/spdx/tools-golang/spdx/v2_3" 13*ba677afaSXin Li) 14*ba677afaSXin Li 15*ba677afaSXin Li// Snippet Information 16*ba677afaSXin Li// Cardinality: Optional, Many 17*ba677afaSXin Lifunc (parser *rdfParser2_3) getSnippetInformationFromNode2_3(node *gordfParser.Node) (si *v2_3.Snippet, err error) { 18*ba677afaSXin Li si = &v2_3.Snippet{} 19*ba677afaSXin Li 20*ba677afaSXin Li err = setSnippetID(node.ID, si) 21*ba677afaSXin Li if err != nil { 22*ba677afaSXin Li return nil, err 23*ba677afaSXin Li } 24*ba677afaSXin Li 25*ba677afaSXin Li for _, siTriple := range parser.nodeToTriples(node) { 26*ba677afaSXin Li switch siTriple.Predicate.ID { 27*ba677afaSXin Li case RDF_TYPE: 28*ba677afaSXin Li // cardinality: exactly 1 29*ba677afaSXin Li case SPDX_SNIPPET_FROM_FILE: 30*ba677afaSXin Li // cardinality: exactly 1 31*ba677afaSXin Li // file which is associated with the snippet 32*ba677afaSXin Li _, err := parser.getFileFromNode(siTriple.Object) 33*ba677afaSXin Li if err != nil { 34*ba677afaSXin Li return nil, err 35*ba677afaSXin Li } 36*ba677afaSXin Li docElemID, err := ExtractDocElementID(getLastPartOfURI(siTriple.Object.ID)) 37*ba677afaSXin Li si.SnippetFromFileSPDXIdentifier = docElemID.ElementRefID 38*ba677afaSXin Li case SPDX_RANGE: 39*ba677afaSXin Li // cardinality: min 1 40*ba677afaSXin Li err = parser.setSnippetRangeFromNode(siTriple.Object, si) 41*ba677afaSXin Li if err != nil { 42*ba677afaSXin Li return nil, err 43*ba677afaSXin Li } 44*ba677afaSXin Li case SPDX_LICENSE_INFO_IN_SNIPPET: 45*ba677afaSXin Li // license info in snippet can be NONE, NOASSERTION or SimpleLicensingInfo 46*ba677afaSXin Li // using AnyLicenseInfo because it can redirect the request and 47*ba677afaSXin Li // can handle NONE & NOASSERTION 48*ba677afaSXin Li var anyLicense AnyLicenseInfo 49*ba677afaSXin Li anyLicense, err = parser.getAnyLicenseFromNode(siTriple.Object) 50*ba677afaSXin Li if err != nil { 51*ba677afaSXin Li return nil, fmt.Errorf("error parsing license info in snippet: %v", err) 52*ba677afaSXin Li } 53*ba677afaSXin Li si.LicenseInfoInSnippet = append(si.LicenseInfoInSnippet, anyLicense.ToLicenseString()) 54*ba677afaSXin Li case SPDX_NAME: 55*ba677afaSXin Li si.SnippetName = siTriple.Object.ID 56*ba677afaSXin Li case SPDX_COPYRIGHT_TEXT: 57*ba677afaSXin Li si.SnippetCopyrightText = siTriple.Object.ID 58*ba677afaSXin Li case SPDX_LICENSE_COMMENTS: 59*ba677afaSXin Li si.SnippetLicenseComments = siTriple.Object.ID 60*ba677afaSXin Li case RDFS_COMMENT: 61*ba677afaSXin Li si.SnippetComment = siTriple.Object.ID 62*ba677afaSXin Li case SPDX_LICENSE_CONCLUDED: 63*ba677afaSXin Li var anyLicense AnyLicenseInfo 64*ba677afaSXin Li anyLicense, err = parser.getAnyLicenseFromNode(siTriple.Object) 65*ba677afaSXin Li if err != nil { 66*ba677afaSXin Li return nil, fmt.Errorf("error parsing license info in snippet: %v", err) 67*ba677afaSXin Li } 68*ba677afaSXin Li si.SnippetLicenseConcluded = anyLicense.ToLicenseString() 69*ba677afaSXin Li default: 70*ba677afaSXin Li return nil, fmt.Errorf("unknown predicate %v", siTriple.Predicate.ID) 71*ba677afaSXin Li } 72*ba677afaSXin Li } 73*ba677afaSXin Li return si, nil 74*ba677afaSXin Li} 75*ba677afaSXin Li 76*ba677afaSXin Li// given is the id of the file, sets the snippet to the file in parser. 77*ba677afaSXin Lifunc (parser *rdfParser2_3) setSnippetToFileWithID(snippet *v2_3.Snippet, fileID common.ElementID) error { 78*ba677afaSXin Li if parser.files[fileID] == nil { 79*ba677afaSXin Li return fmt.Errorf("snippet refers to an undefined file with ID: %s", fileID) 80*ba677afaSXin Li } 81*ba677afaSXin Li 82*ba677afaSXin Li // initializing snippet of the files if it is not defined already 83*ba677afaSXin Li if parser.files[fileID].Snippets == nil { 84*ba677afaSXin Li parser.files[fileID].Snippets = map[common.ElementID]*v2_3.Snippet{} 85*ba677afaSXin Li } 86*ba677afaSXin Li 87*ba677afaSXin Li // setting the snippet to the file. 88*ba677afaSXin Li parser.files[fileID].Snippets[snippet.SnippetSPDXIdentifier] = snippet 89*ba677afaSXin Li 90*ba677afaSXin Li return nil 91*ba677afaSXin Li} 92*ba677afaSXin Li 93*ba677afaSXin Lifunc (parser *rdfParser2_3) setSnippetRangeFromNode(node *gordfParser.Node, si *v2_3.Snippet) error { 94*ba677afaSXin Li // for a range object, we can have only 3 associated triples: 95*ba677afaSXin Li // node -> RDF_TYPE -> Object 96*ba677afaSXin Li // node -> startPointer -> Object 97*ba677afaSXin Li // node -> endPointer -> Object 98*ba677afaSXin Li associatedTriples := parser.nodeToTriples(node) 99*ba677afaSXin Li if len(associatedTriples) != 3 { 100*ba677afaSXin Li return fmt.Errorf("range should be associated with exactly 3 triples, got %d", len(associatedTriples)) 101*ba677afaSXin Li } 102*ba677afaSXin Li 103*ba677afaSXin Li // Triple 1: Predicate=RDF_TYPE 104*ba677afaSXin Li typeTriple := rdfwriter.FilterTriples(associatedTriples, &node.ID, &RDF_TYPE, nil) 105*ba677afaSXin Li if len(typeTriple) != 1 { 106*ba677afaSXin Li // we had 3 associated triples. out of which 2 is start and end pointer, 107*ba677afaSXin Li // if we do not have the rdf:type triple as the third one, 108*ba677afaSXin Li // we have either extra or undefined predicate. 109*ba677afaSXin Li return fmt.Errorf("every object node must be associated with exactly one rdf:type triple, found: %d", len(typeTriple)) 110*ba677afaSXin Li } 111*ba677afaSXin Li 112*ba677afaSXin Li // getting start pointer 113*ba677afaSXin Li startPointerTriples := rdfwriter.FilterTriples(associatedTriples, &node.ID, &PTR_START_POINTER, nil) 114*ba677afaSXin Li if len(startPointerTriples) != 1 { 115*ba677afaSXin Li return fmt.Errorf("range object must be associated with exactly 1 startPointer, got %d", len(startPointerTriples)) 116*ba677afaSXin Li } 117*ba677afaSXin Li startRangeType, start, err := parser.getPointerFromNode(startPointerTriples[0].Object, si) 118*ba677afaSXin Li if err != nil { 119*ba677afaSXin Li return fmt.Errorf("error parsing startPointer: %v", err) 120*ba677afaSXin Li } 121*ba677afaSXin Li 122*ba677afaSXin Li // getting end pointer 123*ba677afaSXin Li endPointerTriples := rdfwriter.FilterTriples(associatedTriples, &node.ID, &PTR_END_POINTER, nil) 124*ba677afaSXin Li if len(startPointerTriples) != 1 { 125*ba677afaSXin Li return fmt.Errorf("range object must be associated with exactly 1 endPointer, got %d", len(endPointerTriples)) 126*ba677afaSXin Li } 127*ba677afaSXin Li endRangeType, end, err := parser.getPointerFromNode(endPointerTriples[0].Object, si) 128*ba677afaSXin Li if err != nil { 129*ba677afaSXin Li return fmt.Errorf("error parsing endPointer: %v", err) 130*ba677afaSXin Li } 131*ba677afaSXin Li 132*ba677afaSXin Li // return error when start and end pointer type is not same. 133*ba677afaSXin Li if startRangeType != endRangeType { 134*ba677afaSXin Li return fmt.Errorf("start and end range type doesn't match") 135*ba677afaSXin Li } 136*ba677afaSXin Li 137*ba677afaSXin Li si.Ranges = []common.SnippetRange{{ 138*ba677afaSXin Li StartPointer: common.SnippetRangePointer{FileSPDXIdentifier: si.SnippetFromFileSPDXIdentifier}, 139*ba677afaSXin Li EndPointer: common.SnippetRangePointer{FileSPDXIdentifier: si.SnippetFromFileSPDXIdentifier}, 140*ba677afaSXin Li }} 141*ba677afaSXin Li 142*ba677afaSXin Li if startRangeType == LINE_RANGE { 143*ba677afaSXin Li si.Ranges[0].StartPointer.LineNumber = start 144*ba677afaSXin Li si.Ranges[0].EndPointer.LineNumber = end 145*ba677afaSXin Li } else { 146*ba677afaSXin Li si.Ranges[0].StartPointer.Offset = start 147*ba677afaSXin Li si.Ranges[0].EndPointer.Offset = end 148*ba677afaSXin Li } 149*ba677afaSXin Li return nil 150*ba677afaSXin Li} 151*ba677afaSXin Li 152*ba677afaSXin Lifunc (parser *rdfParser2_3) getPointerFromNode(node *gordfParser.Node, si *v2_3.Snippet) (rt RangeType, number int, err error) { 153*ba677afaSXin Li for _, triple := range parser.nodeToTriples(node) { 154*ba677afaSXin Li switch triple.Predicate.ID { 155*ba677afaSXin Li case RDF_TYPE: 156*ba677afaSXin Li case PTR_REFERENCE: 157*ba677afaSXin Li err = parser.parseRangeReference(triple.Object, si) 158*ba677afaSXin Li case PTR_OFFSET: 159*ba677afaSXin Li number, err = strconv.Atoi(triple.Object.ID) 160*ba677afaSXin Li rt = BYTE_RANGE 161*ba677afaSXin Li case PTR_LINE_NUMBER: 162*ba677afaSXin Li number, err = strconv.Atoi(triple.Object.ID) 163*ba677afaSXin Li rt = LINE_RANGE 164*ba677afaSXin Li default: 165*ba677afaSXin Li err = fmt.Errorf("undefined predicate (%s) for a pointer", triple.Predicate) 166*ba677afaSXin Li } 167*ba677afaSXin Li if err != nil { 168*ba677afaSXin Li return 169*ba677afaSXin Li } 170*ba677afaSXin Li } 171*ba677afaSXin Li if rt == "" { 172*ba677afaSXin Li err = fmt.Errorf("range type not defined for a pointer") 173*ba677afaSXin Li } 174*ba677afaSXin Li return 175*ba677afaSXin Li} 176*ba677afaSXin Li 177*ba677afaSXin Lifunc (parser *rdfParser2_3) parseRangeReference(node *gordfParser.Node, snippet *v2_3.Snippet) error { 178*ba677afaSXin Li // reference is supposed to be either a resource reference to an already 179*ba677afaSXin Li // defined or a new file. Unfortunately, I didn't find field where this can be set in the tools-golang data model. 180*ba677afaSXin Li // todo: set this reference to the snippet 181*ba677afaSXin Li associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil) 182*ba677afaSXin Li if len(associatedTriples) == 0 { 183*ba677afaSXin Li return nil 184*ba677afaSXin Li } 185*ba677afaSXin Li _, err := parser.getFileFromNode(node) 186*ba677afaSXin Li if err != nil { 187*ba677afaSXin Li return fmt.Errorf("error parsing a new file in a reference: %v", err) 188*ba677afaSXin Li } 189*ba677afaSXin Li return nil 190*ba677afaSXin Li} 191*ba677afaSXin Li 192*ba677afaSXin Lifunc setSnippetID(uri string, si *v2_3.Snippet) (err error) { 193*ba677afaSXin Li fragment := getLastPartOfURI(uri) 194*ba677afaSXin Li si.SnippetSPDXIdentifier, err = ExtractElementID(fragment) 195*ba677afaSXin Li if err != nil { 196*ba677afaSXin Li return fmt.Errorf("error setting snippet identifier: %v", uri) 197*ba677afaSXin Li } 198*ba677afaSXin Li return nil 199*ba677afaSXin Li} 200