aboutsummaryrefslogtreecommitdiff
path: root/rdfloader
diff options
context:
space:
mode:
authorRishabh Bhatnagar <bhatnagarrishabh4@gmail.com>2020-11-17 20:45:14 +0530
committerRishabh Bhatnagar <bhatnagarrishabh4@gmail.com>2020-11-18 09:27:08 +0530
commitf71fa04c39232d41b1f9f30d5e981c96f943df7d (patch)
treeb3acc0d6282ca1def503bf4f99dfaa3e2bcbc3e3 /rdfloader
parent302f983e7b61d30e81294efb1637e82056e81732 (diff)
downloadspdx-tools-f71fa04c39232d41b1f9f30d5e981c96f943df7d.tar.gz
Enable Caching And Parse Cyclic Dependent Objects
- File, Package, Relationship can have cyclic dependent objects. - Cyclic dependent License will raise an error. - Double computation for File, Package, Relationship and License is enabled. Signed-off-by: Rishabh Bhatnagar <bhatnagarrishabh4@gmail.com>
Diffstat (limited to 'rdfloader')
-rw-r--r--rdfloader/parser2v2/parse_file.go19
-rw-r--r--rdfloader/parser2v2/parse_file_test.go19
-rw-r--r--rdfloader/parser2v2/parse_license.go23
-rw-r--r--rdfloader/parser2v2/parse_license_test.go18
-rw-r--r--rdfloader/parser2v2/parse_package.go20
-rw-r--r--rdfloader/parser2v2/parse_package_test.go34
-rw-r--r--rdfloader/parser2v2/parse_relationship.go20
-rw-r--r--rdfloader/parser2v2/parse_relationship_test.go28
-rw-r--r--rdfloader/parser2v2/parser.go1
-rw-r--r--rdfloader/parser2v2/types.go17
10 files changed, 196 insertions, 3 deletions
diff --git a/rdfloader/parser2v2/parse_file.go b/rdfloader/parser2v2/parse_file.go
index 38a95ea..647cef9 100644
--- a/rdfloader/parser2v2/parse_file.go
+++ b/rdfloader/parser2v2/parse_file.go
@@ -13,6 +13,25 @@ import (
func (parser *rdfParser2_2) getFileFromNode(fileNode *gordfParser.Node) (file *spdx.File2_2, err error) {
file = &spdx.File2_2{}
+ currState := parser.cache[fileNode.ID]
+ if currState == nil {
+ // this is the first time we are seeing this node.
+ parser.cache[fileNode.ID] = &nodeState{
+ object: file,
+ Color: WHITE,
+ }
+ } else if currState.Color == GREY {
+ // we have already started parsing this file node and we needn't parse it again.
+ return currState.object.(*spdx.File2_2), nil
+ }
+
+ // setting color to grey to indicate that we've started parsing this node.
+ parser.cache[fileNode.ID].Color = GREY;
+
+ // setting color to black just before function returns to the caller to
+ // indicate that parsing current node is complete.
+ defer func() { parser.cache[fileNode.ID].Color = BLACK }()
+
err = setFileIdentifier(fileNode.ID, file) // 4.2
if err != nil {
return nil, err
diff --git a/rdfloader/parser2v2/parse_file_test.go b/rdfloader/parser2v2/parse_file_test.go
index 48db28f..4d7c8b3 100644
--- a/rdfloader/parser2v2/parse_file_test.go
+++ b/rdfloader/parser2v2/parse_file_test.go
@@ -494,6 +494,25 @@ func Test_rdfParser2_2_getFileFromNode(t *testing.T) {
t.Errorf("expected %s, found %s", expectedLicenseInfoInFile, file.LicenseInfoInFile[0])
}
+
+ // TestCase 12: checking if recursive dependencies are resolved.
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="#SPDXRef-ParentFile">
+ <spdx:fileType rdf:resource="http://spdx.org/rdf/terms#fileType_source"/>
+ <spdx:fileDependency>
+ <spdx:File rdf:about="#SPDXRef-ChildFile">
+ <spdx:fileDependency>
+ <spdx:File rdf:about="#SPDXRef-ParentFile">
+ <spdx:fileName>ParentFile</spdx:fileName>
+ </spdx:File>
+ </spdx:fileDependency>
+ </spdx:File>
+ </spdx:fileDependency>
+ </spdx:File>
+ `)
+ fileNode = gordfWriter.FilterTriples(parser.gordfParserObj.Triples, nil, &RDF_TYPE, &SPDX_FILE)[0].Subject
+ file, err = parser.getFileFromNode(fileNode)
+
// TestCase 11: all valid attribute and it's values.
parser, _ = parserFromBodyContent(`
<spdx:File rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9.tar.gz_1535120734-spdx.rdf#SPDXRef-item177">
diff --git a/rdfloader/parser2v2/parse_license.go b/rdfloader/parser2v2/parse_license.go
index 643543d..4b4ad46 100644
--- a/rdfloader/parser2v2/parse_license.go
+++ b/rdfloader/parser2v2/parse_license.go
@@ -3,6 +3,7 @@
package parser2v2
import (
+ "errors"
"fmt"
gordfParser "github.com/spdx/gordf/rdfloader/parser"
"github.com/spdx/gordf/rdfwriter"
@@ -16,6 +17,28 @@ import (
// decides which type of license it is and passes control to that type of
// license parser to parse the given input.
func (parser *rdfParser2_2) getAnyLicenseFromNode(node *gordfParser.Node) (AnyLicenseInfo, error) {
+
+ currState := parser.cache[node.ID]
+ if currState == nil {
+ // there is no entry about the state of current package node.
+ // this is the first time we're seeing this node.
+ parser.cache[node.ID] = &nodeState{
+ object: nil, // not storing the object as we won't retrieve it later.
+ Color: WHITE,
+ }
+ } else if currState.Color == GREY {
+ // we have already started parsing this license node.
+ // We have a cyclic dependency!
+ return nil, errors.New("Couldn't parse license: found a cyclic dependency on " + node.ID)
+ }
+
+ // setting color of the state to grey to indicate that we've started to
+ // parse this node once.
+ parser.cache[node.ID].Color = GREY
+
+ // setting state color to black when we're done parsing this node.
+ defer func(){parser.cache[node.ID].Color = BLACK}()
+
associatedTriples := rdfwriter.FilterTriples(parser.gordfParserObj.Triples, &node.ID, nil, nil)
if len(associatedTriples) == 0 {
// just a license uri string was found.
diff --git a/rdfloader/parser2v2/parse_license_test.go b/rdfloader/parser2v2/parse_license_test.go
index e3207ec..b519195 100644
--- a/rdfloader/parser2v2/parse_license_test.go
+++ b/rdfloader/parser2v2/parse_license_test.go
@@ -196,6 +196,24 @@ func Test_rdfParser2_2_getAnyLicenseFromNode(t *testing.T) {
if err == nil {
t.Errorf("should've raised an error for invalid input")
}
+
+ // TestCase 8: cyclic dependent license must raise an error.
+ parser, _ = parserFromBodyContent(`
+ <spdx:ConjunctiveLicenseSet rdf:about="#SPDXRef-RecursiveLicense">
+ <spdx:member rdf:resource="http://spdx.org/licenses/GPL-2.0-or-later"/>
+ <spdx:member>
+ <spdx:ConjunctiveLicenseSet rdf:about="#SPDXRef-RecursiveLicense">
+ <spdx:member rdf:resource="http://spdx.org/licenses/LGPL-2.0"/>
+ <spdx:member rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-RecursiveLicense"/>
+ </spdx:ConjunctiveLicenseSet>
+ </spdx:member>
+ </spdx:ConjunctiveLicenseSet>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ _, err = parser.getAnyLicenseFromNode(node)
+ if err == nil {
+ t.Errorf("expected an error due to cyclic dependent license. found %v", err)
+ }
}
func Test_rdfParser2_2_getConjunctiveLicenseSetFromNode(t *testing.T) {
diff --git a/rdfloader/parser2v2/parse_package.go b/rdfloader/parser2v2/parse_package.go
index 5be9631..dde6e70 100644
--- a/rdfloader/parser2v2/parse_package.go
+++ b/rdfloader/parser2v2/parse_package.go
@@ -12,6 +12,26 @@ import (
func (parser *rdfParser2_2) getPackageFromNode(packageNode *gordfParser.Node) (pkg *spdx.Package2_2, err error) {
pkg = &spdx.Package2_2{} // new package which will be returned
+ currState := parser.cache[packageNode.ID]
+ if currState == nil {
+ // there is no entry about the state of current package node.
+ // this is the first time we're seeing this node.
+ parser.cache[packageNode.ID] = &nodeState{
+ object: pkg,
+ Color: WHITE,
+ }
+ } else if currState.Color == GREY {
+ // we have already started parsing this package node and we needn't parse it again.
+ return currState.object.(*spdx.Package2_2), nil
+ }
+
+ // setting color of the state to grey to indicate that we've started to
+ // parse this node once.
+ parser.cache[packageNode.ID].Color = GREY
+
+ // setting state color to black to indicate when we're done parsing this node.
+ defer func(){parser.cache[packageNode.ID].Color = BLACK}();
+
// setting the SPDXIdentifier for the package.
eId, err := ExtractElementID(getLastPartOfURI(packageNode.ID))
if err != nil {
diff --git a/rdfloader/parser2v2/parse_package_test.go b/rdfloader/parser2v2/parse_package_test.go
index 35a67dc..2269826 100644
--- a/rdfloader/parser2v2/parse_package_test.go
+++ b/rdfloader/parser2v2/parse_package_test.go
@@ -459,7 +459,39 @@ func Test_rdfParser2_2_getPackageFromNode(t *testing.T) {
t.Errorf("expected package name: %s, got %s", expectedPkgFileName, pkg.PackageName)
}
- // TestCase 8: everything valid
+ // TestCase 8: Checking if packages can handle cyclic dependencies:
+ // Simulating a smallest possible cycle: package related to itself.
+ parser, _ = parserFromBodyContent(`
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:name>Test Package</spdx:name>
+ <spdx:relationship>
+ <spdx:Relationship>
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_describes" />
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
+ <spdx:versionInfo>1.1.1</spdx:versionInfo>
+ </spdx:Package>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:Package>
+ `)
+ node = parser.gordfParserObj.Triples[0].Subject
+ pkg, err = parser.getPackageFromNode(node)
+ if err != nil {
+ t.Errorf("error parsing a valid package: %v", err)
+ }
+ // checking if both the attributes of the packages are set.
+ expectedVersionInfo := "1.1.1"
+ expectedPackageName := "Test Package"
+ if pkg.PackageVersion != expectedVersionInfo {
+ t.Errorf("Expected %s, found %s", expectedVersionInfo, pkg.PackageVersion)
+ }
+ if pkg.PackageName != expectedPackageName {
+ t.Errorf("Expected %s, found %s", expectedPackageName, pkg.PackageName)
+ }
+
+ // TestCase 9: everything valid
parser, _ = parserFromBodyContent(`
<spdx:Package rdf:about="http://anupam-VirtualBox/repo/SPDX2_time-1.9#SPDXRef-upload2">
<spdx:name>Test Package</spdx:name>
diff --git a/rdfloader/parser2v2/parse_relationship.go b/rdfloader/parser2v2/parse_relationship.go
index beba838..c4e8540 100644
--- a/rdfloader/parser2v2/parse_relationship.go
+++ b/rdfloader/parser2v2/parse_relationship.go
@@ -22,6 +22,26 @@ func (parser *rdfParser2_2) parseRelationship(triple *gordfParser.Triple) (err e
return err
}
+ currState := parser.cache[triple.Object.ID]
+ if currState == nil {
+ // there is no entry about the state of current package node.
+ // this is the first time we're seeing this node.
+ parser.cache[triple.Object.ID] = &nodeState{
+ object: reln,
+ Color: WHITE,
+ }
+ } else if currState.Color == GREY {
+ // we have already started parsing this relationship node and we needn't parse it again.
+ return nil
+ }
+
+ // setting color of the state to grey to indicate that we've started to
+ // parse this node once.
+ parser.cache[triple.Object.ID].Color = GREY
+
+ // setting state color to black to indicate when we're done parsing this node.
+ defer func(){parser.cache[triple.Object.ID].Color = BLACK}();
+
for _, subTriple := range parser.nodeToTriples(triple.Object) {
switch subTriple.Predicate.ID {
case SPDX_RELATIONSHIP_TYPE:
diff --git a/rdfloader/parser2v2/parse_relationship_test.go b/rdfloader/parser2v2/parse_relationship_test.go
index c02f15b..14f4c12 100644
--- a/rdfloader/parser2v2/parse_relationship_test.go
+++ b/rdfloader/parser2v2/parse_relationship_test.go
@@ -320,7 +320,33 @@ func Test_rdfParser2_2_parseRelationship(t *testing.T) {
t.Errorf("should've raised an error due to unknown predicate in a relationship")
}
- // TestCase 6: completely valid example:
+ // TestCase 8: Recursive relationships mustn't raise any error:
+ parser, _ = parserFromBodyContent(`
+ <spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File">
+ <spdx:relationship>
+ <spdx:Relationship rdf:about="#SPDXRef-reln">
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_describes"/>
+ <spdx:relatedSpdxElement>
+ <spdx:Package rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-Saxon">
+ <spdx:relationship>
+ <spdx:Relationship rdf:about="#SPDXRef-reln">
+ <spdx:relationshipType rdf:resource="http://spdx.org/rdf/terms#relationshipType_describes"/>
+ <spdx:relatedSpdxElement rdf:resource="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File"/>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:Package>
+ </spdx:relatedSpdxElement>
+ </spdx:Relationship>
+ </spdx:relationship>
+ </spdx:File>
+ `)
+ triple = rdfwriter.FilterTriples(parser.gordfParserObj.Triples, nil, &SPDX_RELATIONSHIP, nil)[0]
+ err = parser.parseRelationship(triple)
+ if err != nil {
+ t.Errorf("error parsing a valid example")
+ }
+
+ // TestCase 7: completely valid example:
parser, _ = parserFromBodyContent(`
<spdx:File rdf:about="http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#SPDXRef-File">
<spdx:relationship>
diff --git a/rdfloader/parser2v2/parser.go b/rdfloader/parser2v2/parser.go
index a97c4a4..ec4f7ff 100644
--- a/rdfloader/parser2v2/parser.go
+++ b/rdfloader/parser2v2/parser.go
@@ -28,6 +28,7 @@ func NewParser2_2(gordfParserObj *gordfParser.Parser, nodeToTriples map[string][
},
files: map[spdx.ElementID]*spdx.File2_2{},
assocWithPackage: map[spdx.ElementID]bool{},
+ cache: map[string]*nodeState{},
}
return &parser
}
diff --git a/rdfloader/parser2v2/types.go b/rdfloader/parser2v2/types.go
index 5d17771..4d16344 100644
--- a/rdfloader/parser2v2/types.go
+++ b/rdfloader/parser2v2/types.go
@@ -21,7 +21,22 @@ type rdfParser2_2 struct {
assocWithPackage map[spdx.ElementID]bool
// mapping of nodeStrings to parsed object to save double computation.
- cache map[string]interface{}
+ cache map[string]*nodeState
+}
+
+type Color int
+
+const (
+ GREY Color = iota // represents that the node is being visited
+ WHITE // unvisited node
+ BLACK // visited node
+)
+
+type nodeState struct {
+ // object will be pointer to the parsed or element being parsed.
+ object interface{}
+ // color of a state represents if the node is visited/unvisited/being-visited.
+ Color Color
}
type AnyLicenseInfo interface {