Skip to content

Commit a705bb0

Browse files
authored
Add targetXPath to edi.NewReader so we can filter target nodes with xpath on FINAL_OUTPUT. (#104)
Add `targetXPath` to edi.NewReader so we can filter target nodes with `xpath` on `FINAL_OUTPUT`.
1 parent d06a570 commit a705bb0

File tree

4 files changed

+89
-14
lines changed

4 files changed

+89
-14
lines changed

extensions/omniv21/fileformat/edi/bench_test.go

+20-4
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,11 @@ var (
377377
// BenchmarkGetUnprocessedRawSeg_NoCompNoReleaseChar-8 10000 103945 ns/op 27080 B/op 515 allocs/op
378378
func BenchmarkGetUnprocessedRawSeg_NoCompNoReleaseChar(b *testing.B) {
379379
for i := 0; i < b.N; i++ {
380-
reader := NewReader("test", strings.NewReader(benchInputNoCompNoReleaseChar), benchDeclNoCompNoReleaseChar)
380+
reader, err := NewReader(
381+
"test", strings.NewReader(benchInputNoCompNoReleaseChar), benchDeclNoCompNoReleaseChar, "")
382+
if err != nil {
383+
b.FailNow()
384+
}
381385
for {
382386
_, err := reader.getUnprocessedRawSeg()
383387
if err == io.EOF {
@@ -511,7 +515,11 @@ var (
511515
// BenchmarkGetUnprocessedRawSeg_WithCompAndRelease-8 14174 84115 ns/op 102880 B/op 385 allocs/op
512516
func BenchmarkGetUnprocessedRawSeg_WithCompAndRelease(b *testing.B) {
513517
for i := 0; i < b.N; i++ {
514-
reader := NewReader("test", strings.NewReader(benchInputWithCompAndRelease), benchDeclWithCompAndRelease)
518+
reader, err := NewReader(
519+
"test", strings.NewReader(benchInputWithCompAndRelease), benchDeclWithCompAndRelease, "")
520+
if err != nil {
521+
b.FailNow()
522+
}
515523
for {
516524
_, err := reader.getUnprocessedRawSeg()
517525
if err == io.EOF {
@@ -566,7 +574,11 @@ func BenchmarkRawSegToNode(b *testing.B) {
566574
// BenchmarkRead_NoCompNoReleaseChar-8 6943 177692 ns/op 29620 B/op 766 allocs/op
567575
func BenchmarkRead_NoCompNoReleaseChar(b *testing.B) {
568576
for i := 0; i < b.N; i++ {
569-
reader := NewReader("test", strings.NewReader(benchInputNoCompNoReleaseChar), benchDeclNoCompNoReleaseChar)
577+
reader, err := NewReader(
578+
"test", strings.NewReader(benchInputNoCompNoReleaseChar), benchDeclNoCompNoReleaseChar, "")
579+
if err != nil {
580+
b.FailNow()
581+
}
570582
for {
571583
_, err := reader.Read()
572584
if err == io.EOF {
@@ -582,7 +594,11 @@ func BenchmarkRead_NoCompNoReleaseChar(b *testing.B) {
582594
// BenchmarkRead_WithCompAndRelease-8 9795 120079 ns/op 107591 B/op 464 allocs/op
583595
func BenchmarkRead_WithCompAndRelease(b *testing.B) {
584596
for i := 0; i < b.N; i++ {
585-
reader := NewReader("test", strings.NewReader(benchInputWithCompAndRelease), benchDeclWithCompAndRelease)
597+
reader, err := NewReader(
598+
"test", strings.NewReader(benchInputWithCompAndRelease), benchDeclWithCompAndRelease, "")
599+
if err != nil {
600+
b.FailNow()
601+
}
586602
for {
587603
_, err := reader.Read()
588604
if err == io.EOF {

extensions/omniv21/fileformat/edi/reader.go

+25-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"io"
88
"unicode/utf8"
99

10+
"github.com/antchfx/xpath"
11+
"github.com/jf-tech/go-corelib/caches"
1012
"github.com/jf-tech/go-corelib/ios"
1113
"github.com/jf-tech/go-corelib/strs"
1214

@@ -96,6 +98,7 @@ type ediReader struct {
9698
releaseChar strPtrByte
9799
stack []stackEntry
98100
target *idr.Node
101+
targetXPath *xpath.Expr
99102
runeBegin, runeEnd int
100103
unprocessedRawSeg rawSeg
101104
}
@@ -258,14 +261,14 @@ func (r *ediReader) rawSegToNode(segDecl *segDecl) (*idr.Node, error) {
258261
}
259262
}
260263
if rawElemIndex < len(rawElems) || elemDecl.EmptyIfMissing {
264+
elemN := idr.CreateNode(idr.ElementNode, elemDecl.Name)
265+
idr.AddChild(n, elemN)
261266
data := ""
262267
if rawElemIndex < len(rawElems) {
263268
data = string(strs.ByteUnescape(rawElems[rawElemIndex].data, r.releaseChar.b, true))
264269
rawElems[rawElemIndex].dateUnescaped = true
265270
rawElemIndex++
266271
}
267-
elemN := idr.CreateNode(idr.ElementNode, elemDecl.Name)
268-
idr.AddChild(n, elemN)
269272
elemV := idr.CreateNode(idr.TextNode, data)
270273
idr.AddChild(elemN, elemV)
271274
continue
@@ -293,7 +296,12 @@ func (r *ediReader) segDone() {
293296
if cur.segNode == nil {
294297
panic("cur.segNode == nil")
295298
}
296-
r.target = cur.segNode
299+
if r.targetXPath == nil || idr.MatchAny(cur.segNode, r.targetXPath) {
300+
r.target = cur.segNode
301+
} else {
302+
idr.RemoveAndReleaseTree(cur.segNode)
303+
cur.segNode = nil
304+
}
297305
}
298306
if cur.occurred < cur.segDecl.maxOccurs() {
299307
return
@@ -415,19 +423,30 @@ var (
415423
)
416424

417425
// NewReader creates an FormatReader for EDI file format.
418-
func NewReader(inputName string, r io.Reader, decl *fileDecl) *ediReader {
426+
func NewReader(inputName string, r io.Reader, decl *fileDecl, targetXPath string) (*ediReader, error) {
419427
segDelim := newStrPtrByte(&decl.SegDelim)
420428
elemDelim := newStrPtrByte(&decl.ElemDelim)
421429
compDelim := newStrPtrByte(decl.CompDelim)
422430
releaseChar := newStrPtrByte(decl.ReleaseChar)
431+
scanner := ios.NewScannerByDelim3(r, segDelim.b, releaseChar.b, scannerFlags, make([]byte, ReaderBufSize))
432+
targetXPathExpr, err := func() (*xpath.Expr, error) {
433+
if targetXPath == "" || targetXPath == "." {
434+
return nil, nil
435+
}
436+
return caches.GetXPathExpr(targetXPath)
437+
}()
438+
if err != nil {
439+
return nil, fmt.Errorf("invalid target xpath '%s', err: %s", targetXPath, err.Error())
440+
}
423441
reader := &ediReader{
424442
inputName: inputName,
425-
scanner: ios.NewScannerByDelim3(r, segDelim.b, releaseChar.b, scannerFlags, make([]byte, ReaderBufSize)),
443+
scanner: scanner,
426444
segDelim: segDelim,
427445
elemDelim: elemDelim,
428446
compDelim: compDelim,
429447
releaseChar: releaseChar,
430448
stack: newStack(),
449+
targetXPath: targetXPathExpr,
431450
runeBegin: 1,
432451
runeEnd: 1,
433452
unprocessedRawSeg: newRawSeg(),
@@ -446,5 +465,5 @@ func NewReader(inputName string, r io.Reader, decl *fileDecl) *ediReader {
446465
segDecl: decl.SegDecls[0],
447466
})
448467
}
449-
return reader
468+
return reader, nil
450469
}

extensions/omniv21/fileformat/edi/reader_test.go

+44-4
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ func TestGetUnprocessedRawSeg(t *testing.T) {
283283
},
284284
} {
285285
t.Run(test.name, func(t *testing.T) {
286-
reader := NewReader("test", test.input, &test.decl)
286+
reader, err := NewReader("test", test.input, &test.decl, "")
287+
assert.NoError(t, err)
287288
for {
288289
if len(test.expected) == 0 {
289290
assert.FailNow(t, "reader has more content than expected")
@@ -585,7 +586,23 @@ func TestRead(t *testing.T) {
585586
name string
586587
input string
587588
declJSON string
589+
xpath string
590+
err string
588591
}{
592+
{
593+
name: "invalid target xpath; failure",
594+
input: "",
595+
declJSON: `
596+
{
597+
"segment_delimiter": "\n",
598+
"element_delimiter": "*",
599+
"segment_declarations": [
600+
{ "name": "ISA", "min": 0 }
601+
]
602+
}`,
603+
xpath: "[",
604+
err: `invalid target xpath '[', err: expression must evaluate to a node-set`,
605+
},
589606
{
590607
name: "empty input; success",
591608
input: "",
@@ -597,6 +614,8 @@ func TestRead(t *testing.T) {
597614
{ "name": "ISA", "min": 0 }
598615
]
599616
}`,
617+
xpath: "",
618+
err: "",
600619
},
601620
{
602621
name: "single seg decl; multiple seg instances; success",
@@ -618,6 +637,8 @@ func TestRead(t *testing.T) {
618637
}
619638
]
620639
}`,
640+
xpath: "",
641+
err: "",
621642
},
622643
{
623644
name: "2 seg decls; success",
@@ -645,10 +666,12 @@ func TestRead(t *testing.T) {
645666
}
646667
]
647668
}`,
669+
xpath: "",
670+
err: "",
648671
},
649672
{
650-
name: "2 seg groups; success",
651-
input: "ISA*0*1*2\nISA*3*4*5\nIEA*6\n",
673+
name: "2 seg groups; filtered target; success",
674+
input: "ISA*0*1*2\nISA*3*4*5\nISA*6*7*8\nIEA*6\n",
652675
declJSON: `
653676
{
654677
"segment_delimiter": "\n",
@@ -684,6 +707,8 @@ func TestRead(t *testing.T) {
684707
}
685708
]
686709
}`,
710+
xpath: ".[e1 != '6']",
711+
err: "",
687712
},
688713
{
689714
name: "seg min not satisfied before EOF; failure",
@@ -711,6 +736,8 @@ func TestRead(t *testing.T) {
711736
}
712737
]
713738
}`,
739+
xpath: "",
740+
err: "",
714741
},
715742
{
716743
name: "missing raw seg name; failure",
@@ -731,6 +758,8 @@ func TestRead(t *testing.T) {
731758
}
732759
]
733760
}`,
761+
xpath: "",
762+
err: "",
734763
},
735764
{
736765
name: "raw seg processing wrong; failure",
@@ -751,6 +780,8 @@ func TestRead(t *testing.T) {
751780
}
752781
]
753782
}`,
783+
xpath: "",
784+
err: "",
754785
},
755786
{
756787
name: "seg min not satisfied before next seg appearance; failure",
@@ -775,13 +806,22 @@ func TestRead(t *testing.T) {
775806
}
776807
]
777808
}`,
809+
xpath: "",
810+
err: "",
778811
},
779812
} {
780813
t.Run(test.name, func(t *testing.T) {
781814
var decl fileDecl
782815
err := json.Unmarshal([]byte(test.declJSON), &decl)
783816
assert.NoError(t, err)
784-
reader := NewReader("test", strings.NewReader(test.input), &decl)
817+
reader, err := NewReader("test", strings.NewReader(test.input), &decl, test.xpath)
818+
if test.err != "" {
819+
assert.Error(t, err)
820+
assert.Equal(t, test.err, err.Error())
821+
assert.Nil(t, reader)
822+
return
823+
}
824+
assert.NoError(t, err)
785825
var records []string
786826
var finalErr error
787827
for {

0 commit comments

Comments
 (0)