Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor ReplaceFrom() #113

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 108 additions & 19 deletions node/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
editUpsert editStrategy = iota + 1
editInsert
editUpdate
editReplace
)

type editor struct {
Expand Down Expand Up @@ -51,7 +52,6 @@ func (e editor) enter(from *Selection, to *Selection, new bool, strategy editStr
} else {
ml := newContainerMetaList(from)
m := ml.nextMeta()
//fmt.Printf("Begin %s\n", meta.SchemaPath(from.Meta()))
for m != nil {
var err error
if meta.IsLeaf(m) {
Expand All @@ -64,7 +64,6 @@ func (e editor) enter(from *Selection, to *Selection, new bool, strategy editStr
}
m = ml.nextMeta()
}
//fmt.Printf("Ended %s\n", meta.SchemaPath(from.Meta()))
}
return nil
}
Expand All @@ -84,19 +83,24 @@ func (e editor) leaf(from *Selection, to *Selection, m meta.Leafable, new bool,
return err
}

r.Selection = to
r.From = from
if hnd.Val != nil {
// If there is a different choice selected, need to clear it
// first if in upsert mode
if strategy == editUpsert {
if strategy == editUpsert || strategy == editReplace {
if err := e.clearOnDifferentChoiceCase(to, m); err != nil {
return err
}
}
r.Selection = to
r.From = from
if err := to.set(&r, &hnd); err != nil {
return err
}
} else if strategy == editReplace {
r.Clear = true
if err := to.set(&r, &ValueHandle{}); err != nil {
return err
}
}
return nil
}
Expand Down Expand Up @@ -147,6 +151,24 @@ func (e editor) clearChoiceCase(sel *Selection, c *meta.ChoiceCase) error {
func (e editor) node(from *Selection, to *Selection, m meta.HasDataDefinitions, new bool, strategy editStrategy) error {
var newChild bool
var err error
// this ensures that even on panic we release any selections created in this func and it's loop
var fromChild *Selection
var toChild *Selection
releaseFromChild := func() {
if fromChild != nil {
fromChild.Release()
fromChild = nil
}
}
defer releaseFromChild()
releaseToChild := func() {
if toChild != nil {
toChild.Release()
toChild = nil
}
}
defer releaseToChild()

fromRequest := ChildRequest{
Request: Request{
Selection: from,
Expand All @@ -155,11 +177,13 @@ func (e editor) node(from *Selection, to *Selection, m meta.HasDataDefinitions,
},
Meta: m,
}
fromChild, err := from.selekt(&fromRequest)
if fromChild == nil || err != nil {
fromChild, err = from.selekt(&fromRequest)
if err != nil {
return err
}
defer fromChild.Release()
if fromChild == nil && strategy != editReplace {
return nil
}
toRequest := ChildRequest{
Request: Request{
Selection: to,
Expand All @@ -171,23 +195,34 @@ func (e editor) node(from *Selection, to *Selection, m meta.HasDataDefinitions,
toRequest.New = false
toRequest.Selection = to

toChild, err := to.selekt(&toRequest)
toChild, err = to.selekt(&toRequest)
if err != nil {
return err
}
if toChild != nil {
defer toChild.Release()
}
toRequest.New = true

switch strategy {
case editInsert:
if toChild != nil {
return fmt.Errorf("%w. item '%s' found in '%s'. ", fc.ConflictError, m.Ident(), fromRequest.Path)
}
toRequest.New = true
if toChild, err = to.selekt(&toRequest); err != nil {
return err
}
newChild = true
case editReplace:
if toChild != nil {
if err := toChild.delete(); err != nil {
return err
}
}
if fromChild == nil {
return nil
}
toRequest.New = true
if toChild, err = to.selekt(&toRequest); err != nil {
return err
}
defer toChild.Release()
newChild = true
case editUpsert:

Expand All @@ -198,12 +233,10 @@ func (e editor) node(from *Selection, to *Selection, m meta.HasDataDefinitions,
}

if toChild == nil {
toRequest.New = true
if toChild, err = to.selekt(&toRequest); err != nil {
return err
}
if toChild != nil {
defer toChild.Release()
}
newChild = true
}
case editUpdate:
Expand All @@ -225,7 +258,6 @@ func (e editor) node(from *Selection, to *Selection, m meta.HasDataDefinitions,
}

func (e editor) list(from *Selection, to *Selection, m *meta.List, new bool, strategy editStrategy) error {

// this ensures that even on panic we release any selections created in this func and it's loop
var fromChild *Selection
var toChild *Selection
Expand Down Expand Up @@ -257,7 +289,7 @@ func (e editor) list(from *Selection, to *Selection, m *meta.List, new bool, str
var key []val.Value
var err error
fromChild, key, err = from.selectVisibleListItem(fromRequest)
if err != nil || fromChild == nil {
if err != nil {
return err
}
p.Key = key
Expand Down Expand Up @@ -310,6 +342,11 @@ func (e editor) list(from *Selection, to *Selection, m *meta.List, new bool, str
return err
}
newItem = true
case editReplace:
if toChild, _, _, err = to.selectListItem(&toRequest); err != nil {
return err
}
newItem = true
default:
return strategyNotImplemented
}
Expand All @@ -330,5 +367,57 @@ func (e editor) list(from *Selection, to *Selection, m *meta.List, new bool, str
return err
}
}
if strategy == editReplace {
// Iterate through "to" list, look if such element is in "from" list, delete otherwise
p = *to.Path
toRequest = ListRequest{
Request: Request{
Selection: to,
Path: &p,
Base: e.basePath,
},
First: true,
Meta: m,
}
fromRequest = &ListRequest{
Request: Request{
Selection: from,
Path: &p,
Base: e.basePath,
},
First: true,
Meta: m,
}
for {
if toChild, _, key, err = to.selectListItem(&toRequest); err != nil {
return err
}
if toChild == nil {
break
}

fromRequest.First = true
fromRequest.SetRow(toRequest.Row64)
fromRequest.Selection = to
fromRequest.From = toChild
fromRequest.Key = key
p.Key = key
if fromChild, _, _, err = from.selectListItem(fromRequest); err != nil {
return err
}
if fromChild == nil {
fmt.Printf("|||||delete fromChild=%v\n", toChild.Path)
if err = toChild.delete(); err != nil {
return err
}
toChild = nil
}

releaseToChild()
releaseFromChild()

toRequest.IncrementRow()
}
}
return nil
}
13 changes: 8 additions & 5 deletions node/selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,11 @@ func (sel *Selection) Delete() (err error) {
}
}()

err = sel.delete()
return
}

func (sel *Selection) delete() (err error) {
if sel.InsideList {
r := ListRequest{
Request: Request{
Expand Down Expand Up @@ -468,12 +473,10 @@ func (sel *Selection) UpdateInto(toNode Node) error {
return e.edit(sel, sel.Split(toNode), editUpdate)
}

// Replace current tree with given one
func (sel *Selection) ReplaceFrom(fromNode Node) error {
parent := sel.parent
if err := sel.Delete(); err != nil {
return err
}
return parent.InsertFrom(fromNode)
e := editor{basePath: sel.Path}
return e.edit(sel.Split(fromNode), sel, editReplace)
}

// Copy given node into current node. There must be matching containers of list
Expand Down
16 changes: 13 additions & 3 deletions node/selection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,25 +84,35 @@ func TestReplaceFrom(t *testing.T) {
sel, err := root.Find("bird=robin/species")
fc.RequireEqual(t, nil, err)
n, _ := nodeutil.ReadJSON(`
{"species":{"name":"dragon"}}
{"class":"dragon"}
`)
err = sel.ReplaceFrom(n)
fc.AssertEqual(t, nil, err)
sel, err = root.Find("bird=robin")
fc.RequireEqual(t, nil, err)
actual, err := js.JSON(sel)
fc.AssertEqual(t, nil, err)
fc.AssertEqual(t, `{"name":"robin","wingspan":10,"species":{"name":"dragon"}}`, actual)
fc.AssertEqual(t, `{"name":"robin","wingspan":10,"species":{"class":"dragon"}}`, actual)

// list item
sel, err = root.Find("bird=robin")
fc.RequireEqual(t, nil, err)
n, _ = nodeutil.ReadJSON(`
{"bird":[{"name": "robin", "wingspan":11}]}
{"name": "robin", "wingspan":11}
`)
sel.ReplaceFrom(n)
fc.AssertEqual(t, nil, err)
actual, err = js.JSON(root)
fc.AssertEqual(t, nil, err)
fc.AssertEqual(t, `{"bird":[{"name":"blue jay","wingspan":0},{"name":"robin","wingspan":11}]}`, actual)

// whole list
n, _ = nodeutil.ReadJSON(`
{"bird":[{"name":"blue jay"},{"name":"malak","species":{"class":"jedi"}}]}
`)
root.ReplaceFrom(n)
fc.AssertEqual(t, nil, err)
actual, err = js.JSON(root)
fc.AssertEqual(t, nil, err)
fc.AssertEqual(t, `{"bird":[{"name":"blue jay","wingspan":0},{"name":"malak","wingspan":0,"species":{"class":"jedi"}}]}`, actual)
}
Loading