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