diff options
author | Rishabh Bhatnagar <bhatnagarrishabh4@gmail.com> | 2020-11-17 20:45:14 +0530 |
---|---|---|
committer | Rishabh Bhatnagar <bhatnagarrishabh4@gmail.com> | 2020-11-18 09:27:08 +0530 |
commit | f71fa04c39232d41b1f9f30d5e981c96f943df7d (patch) | |
tree | b3acc0d6282ca1def503bf4f99dfaa3e2bcbc3e3 /rdfloader | |
parent | 302f983e7b61d30e81294efb1637e82056e81732 (diff) | |
download | spdx-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.go | 19 | ||||
-rw-r--r-- | rdfloader/parser2v2/parse_file_test.go | 19 | ||||
-rw-r--r-- | rdfloader/parser2v2/parse_license.go | 23 | ||||
-rw-r--r-- | rdfloader/parser2v2/parse_license_test.go | 18 | ||||
-rw-r--r-- | rdfloader/parser2v2/parse_package.go | 20 | ||||
-rw-r--r-- | rdfloader/parser2v2/parse_package_test.go | 34 | ||||
-rw-r--r-- | rdfloader/parser2v2/parse_relationship.go | 20 | ||||
-rw-r--r-- | rdfloader/parser2v2/parse_relationship_test.go | 28 | ||||
-rw-r--r-- | rdfloader/parser2v2/parser.go | 1 | ||||
-rw-r--r-- | rdfloader/parser2v2/types.go | 17 |
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 { |