diff --git a/rdfloader/parser/node.go b/rdfloader/parser/node.go index 934854d..13716ca 100644 --- a/rdfloader/parser/node.go +++ b/rdfloader/parser/node.go @@ -1,6 +1,9 @@ package parser -import "fmt" +import ( + "fmt" + "sync/atomic" +) type NODETYPE string @@ -22,14 +25,18 @@ func (node *Node) String() string { } type BlankNodeGetter struct { - lastid int + lastid atomic.Int64 +} + +func (getter *BlankNodeGetter) set(i int) { + getter.lastid.Store(int64(i)) } func (getter *BlankNodeGetter) Get() Node { - getter.lastid += 1 + lastid := getter.lastid.Add(1) return Node{ NodeType: BLANK, - ID: fmt.Sprintf("N%v", getter.lastid), + ID: fmt.Sprintf("N%v", lastid), } } diff --git a/rdfloader/parser/node_test.go b/rdfloader/parser/node_test.go index 409755f..34c7196 100644 --- a/rdfloader/parser/node_test.go +++ b/rdfloader/parser/node_test.go @@ -23,10 +23,10 @@ func TestBlankNodeGetter_Get(t *testing.T) { } // blank node getter with custom lastid. - blankNodeGetter = BlankNodeGetter{ - lastid: -1, - } + blankNodeGetter = BlankNodeGetter{} + // last id -1 means that first node should start from N0 + blankNodeGetter.set(-1) firstBlankNode = blankNodeGetter.Get() if firstBlankNode.ID != "N0" { t.Errorf("Expected node to be %v, found %v", "N0", firstBlankNode.ID) diff --git a/rdfloader/parser/parser.go b/rdfloader/parser/parser.go index 4a235c6..289895a 100644 --- a/rdfloader/parser/parser.go +++ b/rdfloader/parser/parser.go @@ -90,17 +90,19 @@ func getLastURI(tag xmlreader.Tag, lastURI string) string { func New() (parser *Parser) { // creates a new parser object rdfNS, _ := uri.NewURIRef(RDFNS) - return &Parser{ + parser = &Parser{ setTriples: map[string]*Triple{}, setNodes: map[string]*Node{}, Triples: []*Triple{}, writeLock: sync.RWMutex{}, nodesWriteLock: sync.RWMutex{}, SchemaDefinition: map[string]uri.URIRef{"": uri.URIRef{}}, - blankNodeGetter: BlankNodeGetter{-1}, + blankNodeGetter: BlankNodeGetter{}, wg: sync.WaitGroup{}, rdfNS: rdfNS, } + parser.blankNodeGetter.set(-1) + return parser } func (parser *Parser) parseBlock(currBlock *xmlreader.Block, node *Node, lastURI string, errp *error) { @@ -133,13 +135,23 @@ func (parser *Parser) parseBlock(currBlock *xmlreader.Block, node *Node, lastURI */ node = parser.resolveNode(node) defer parser.wg.Done() + setError := func(err error) { + parser.writeLock.Lock() + defer parser.writeLock.Unlock() + *errp = err + } + getError := func() error { + parser.writeLock.RLock() + defer parser.writeLock.RUnlock() + return *errp + } lastURI = getLastURI(currBlock.OpeningTag, lastURI) if len(currBlock.Children) == 0 { // adding only one triple which identifies the type of the current block. predicateURI := parser.rdfNS.AddFragment("type") openingTagUri, newErr := parser.uriFromPair(currBlock.OpeningTag.SchemaName, currBlock.OpeningTag.Name) if newErr != nil { - *errp = newErr + setError(newErr) return } parser.appendTriple(&Triple{ @@ -154,14 +166,14 @@ func (parser *Parser) parseBlock(currBlock *xmlreader.Block, node *Node, lastURI // according to https://www.w3.org/TR/rdf-concepts/#dfn-predicate predicateURI, newErr := parser.uriFromPair(predicateBlock.OpeningTag.SchemaName, predicateBlock.OpeningTag.Name) if newErr != nil { - *errp = fmt.Errorf("error creating a reference URI link for the predicate block. %v", newErr) + setError(fmt.Errorf("error creating a reference URI link for the predicate block. %v", newErr)) return } predicateNode := &Node{NodeType: IRI, ID: predicateURI.String()} openingTagUri, newErr := parser.uriFromPair(currBlock.OpeningTag.SchemaName, currBlock.OpeningTag.Name) if newErr != nil { - *errp = newErr + setError(newErr) return } @@ -180,13 +192,13 @@ func (parser *Parser) parseBlock(currBlock *xmlreader.Block, node *Node, lastURI Object: nil, } resIdx, newErr := parser.getRDFAttributeIndex(predicateBlock.OpeningTag, "resource") - *errp = newErr - if *errp != nil { + if newErr != nil{ + setError(newErr) return } nodeidIdx, newErr := parser.getRDFAttributeIndex(predicateBlock.OpeningTag, "nodeID") - *errp = newErr - if *errp != nil { + if newErr != nil{ + setError(newErr) return } @@ -220,7 +232,7 @@ func (parser *Parser) parseBlock(currBlock *xmlreader.Block, node *Node, lastURI for _, objectBlock := range predicateBlock.Children { objectNode, newErr := parser.nodeFromTag(objectBlock.OpeningTag, lastURI) if newErr != nil { - *errp = newErr + setError(newErr) return } @@ -231,7 +243,7 @@ func (parser *Parser) parseBlock(currBlock *xmlreader.Block, node *Node, lastURI }) parser.wg.Add(1) go parser.parseBlock(objectBlock, objectNode, lastURI, errp) - if *errp != nil { + if getError() != nil { return } } @@ -246,19 +258,25 @@ func (parser *Parser) Parse(rootBlock xmlreader.Block) (err error) { } parser.SchemaDefinition = schemaDefinition + getError := func() error { + parser.writeLock.RLock() + defer parser.writeLock.RUnlock() + return err + } // root tag is set now. var childNode *Node + var lerr error xmlns := schemaDefinition[""] xmlnsString := xmlns.String() for _, child := range rootBlock.Children { - childNode, err = parser.nodeFromTag(child.OpeningTag, xmlnsString) - if err != nil { - return err + childNode, lerr = parser.nodeFromTag(child.OpeningTag, xmlnsString) + if lerr != nil { + return lerr } parser.wg.Add(1) go parser.parseBlock(child, childNode, xmlnsString, &err) - if err != nil { - return err + if lerr := getError(); lerr != nil { + return lerr } } parser.wg.Wait() // wait for all the go routines to finish executing. diff --git a/rdfwriter/utils.go b/rdfwriter/utils.go index 39070a2..b8dcadc 100644 --- a/rdfwriter/utils.go +++ b/rdfwriter/utils.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/spdx/gordf/rdfloader/parser" "github.com/spdx/gordf/uri" + "slices" "strings" ) @@ -80,6 +81,16 @@ func getUniqueTriples(triples []*parser.Triple) []*parser.Triple { for key := range set { retList = append(retList, set[key]) } + slices.SortFunc(retList, func(a, b *parser.Triple) int { + c := strings.Compare(a.Subject.String(), b.Subject.String()) + if c == 0 { + c = strings.Compare(a.Predicate.String(), b.Predicate.String()) + } + if c == 0 { + c = strings.Compare(a.Object.String(), b.Predicate.String()) + } + return c + }) return retList } diff --git a/rdfwriter/utils_test.go b/rdfwriter/utils_test.go index 3c49a5e..a7e19c7 100644 --- a/rdfwriter/utils_test.go +++ b/rdfwriter/utils_test.go @@ -136,6 +136,18 @@ func TestTopologicalSortTriples(t *testing.T) { {Subject: nodes[0], Predicate: nodes[2], Object: nodes[3]}, {Subject: nodes[3], Predicate: nodes[4], Object: nodes[0]}, } + t.Logf("Got %d sortedTriples:", len(sortedTriples)) + for k, v := range sortedTriples { + t.Logf(" sortedTriples[%v]=%s", k, v) + } + t.Logf("Want %d expectedTriples", len(expectedTriples)) + for k, v := range expectedTriples { + t.Logf(" expectedTriples[%v]=%s", k, v) + } + t.Logf("Or %d anotherConfig", len(anotherConfig)) + for k, v := range anotherConfig { + t.Logf(" anotherConfig[%v]=%s", k, v) + } if !reflect.DeepEqual(sortedTriples, expectedTriples) && !reflect.DeepEqual(sortedTriples, anotherConfig) { t.Errorf("sorted triples are not in correct order") }