Skip to content

Commit

Permalink
Merge pull request #56 from RishabhBhatnagar/master
Browse files Browse the repository at this point in the history
Enable Caching And Parse Cyclic Dependent Objects
  • Loading branch information
swinslow committed Nov 22, 2020
2 parents b584a7d + f71fa04 commit a16d50e
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 3 deletions.
19 changes: 19 additions & 0 deletions rdfloader/parser2v2/parse_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions rdfloader/parser2v2/parse_file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
23 changes: 23 additions & 0 deletions rdfloader/parser2v2/parse_license.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package parser2v2

import (
"errors"
"fmt"
gordfParser "github.com/spdx/gordf/rdfloader/parser"
"github.com/spdx/gordf/rdfwriter"
Expand All @@ -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.
Expand Down
18 changes: 18 additions & 0 deletions rdfloader/parser2v2/parse_license_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
20 changes: 20 additions & 0 deletions rdfloader/parser2v2/parse_package.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
34 changes: 33 additions & 1 deletion rdfloader/parser2v2/parse_package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
20 changes: 20 additions & 0 deletions rdfloader/parser2v2/parse_relationship.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
28 changes: 27 additions & 1 deletion rdfloader/parser2v2/parse_relationship_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions rdfloader/parser2v2/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
17 changes: 16 additions & 1 deletion rdfloader/parser2v2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit a16d50e

Please sign in to comment.