• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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