blob: 4a922085f2d52793dba078638f046c2ab8306082 [file] [log] [blame]
khenaidoobf6e7bb2018-08-14 22:27:29 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040016package model
17
Stephane Barbariee16186c2018-09-11 10:46:34 -040018// TODO: proper error handling
19// TODO: proper logging
20
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040021import (
22 "fmt"
23 "github.com/golang/protobuf/proto"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040024 "github.com/opencord/voltha-go/common/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040025 "reflect"
26 "strings"
27)
28
29const (
30 NONE string = "none"
31)
32
Stephane Barbarie06c4a742018-10-01 11:09:32 -040033type Node interface {
34 MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple)
35
36 // CRUD functions
37 Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision
38 Get(path string, hash string, depth int, deep bool, txid string) interface{}
39 Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision
40 Remove(path string, txid string, makeBranch MakeBranchFunction) Revision
41
42 MakeBranch(txid string) *Branch
43 DeleteBranch(txid string)
44 MergeBranch(txid string, dryRun bool) (Revision, error)
45
46 MakeTxBranch() string
47 DeleteTxBranch(txid string)
48 FoldTxBranch(txid string)
49
50 GetProxy(path string, exclusive bool) *Proxy
Stephane Barbarie126101e2018-10-11 16:18:48 -040051
52 ExecuteCallbacks()
53 AddCallback(callback CallbackFunction, args ...interface{})
54 AddNotificationCallback(callback CallbackFunction, args ...interface{})
Stephane Barbarie06c4a742018-10-01 11:09:32 -040055}
56
57type node struct {
Stephane Barbarie126101e2018-10-11 16:18:48 -040058 Root *root
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040059 Type interface{}
60 Branches map[string]*Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -040061 Tags map[string]Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040062 Proxy *Proxy
63 EventBus *EventBus
64 AutoPrune bool
65}
66
Stephane Barbarie694e2b92018-09-07 12:17:36 -040067type ChangeTuple struct {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -040068 Type CallbackType
69 PreviousData interface{}
70 LatestData interface{}
Stephane Barbarie694e2b92018-09-07 12:17:36 -040071}
72
Stephane Barbarie06c4a742018-10-01 11:09:32 -040073func NewNode(root *root, initialData interface{}, autoPrune bool, txid string) *node {
74 n := &node{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040075
Stephane Barbarie126101e2018-10-11 16:18:48 -040076 n.Root = root
Stephane Barbarieec0919b2018-09-05 14:14:29 -040077 n.Branches = make(map[string]*Branch)
78 n.Tags = make(map[string]Revision)
79 n.Proxy = nil
80 n.EventBus = nil
81 n.AutoPrune = autoPrune
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040082
83 if IsProtoMessage(initialData) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040084 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040085 dataCopy := proto.Clone(initialData.(proto.Message))
Stephane Barbarieec0919b2018-09-05 14:14:29 -040086 n.initialize(dataCopy, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040087 } else if reflect.ValueOf(initialData).IsValid() {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040088 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040089 } else {
90 // not implemented error
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -040091 log.Errorf("cannot process initial data - %+v", initialData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040092 }
93
Stephane Barbarieec0919b2018-09-05 14:14:29 -040094 return n
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040095}
96
Stephane Barbarie06c4a742018-10-01 11:09:32 -040097func (n *node) MakeNode(data interface{}, txid string) *node {
Stephane Barbarie126101e2018-10-11 16:18:48 -040098 return NewNode(n.Root, data, true, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040099}
100
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400101func (n *node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400102 if n.Root.RevisionClass.(reflect.Type) == reflect.TypeOf(PersistedRevision{}) {
103 return NewPersistedRevision(branch, data, children)
104 }
105
106 return NewNonPersistedRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400107}
108
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400109func (n *node) MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
110 n.makeLatest(branch, revision, changeAnnouncement)
111}
112func (n *node) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400113 if _, ok := branch.Revisions[revision.GetHash()]; !ok {
114 branch.Revisions[revision.GetHash()] = revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400115 }
116
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400117 if branch.Latest == nil || revision.GetHash() != branch.Latest.GetHash() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400118 branch.Latest = revision
119 }
120
121 if changeAnnouncement != nil && branch.Txid == "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400122 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400123 for _, change := range changeAnnouncement {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400124 log.Debugf("invoking callback - changeType: %+v, previous:%+v, latest: %+v",
125 change.Type,
126 change.PreviousData,
127 change.LatestData)
128 n.Root.AddCallback(
129 n.Proxy.InvokeCallbacks,
130 change.Type,
131 true,
132 change.PreviousData,
133 change.LatestData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400134 }
135 }
136
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400137 for _, change := range changeAnnouncement {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400138 log.Debugf("sending notification - changeType: %+v, previous:%+v, latest: %+v",
139 change.Type,
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400140 change.PreviousData,
141 change.LatestData)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400142 n.Root.AddNotificationCallback(
143 n.makeEventBus().Advertise,
144 change.Type,
145 revision.GetHash(),
146 change.PreviousData,
147 change.LatestData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400148 }
149 }
150}
151
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400152func (n *node) Latest(txid ...string) Revision {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400153 var branch *Branch
154 var exists bool
155
156 if len(txid) > 0 && txid[0] != "" {
157 if branch, exists = n.Branches[txid[0]]; exists {
158 return branch.Latest
159 }
160 } else if branch, exists = n.Branches[NONE]; exists {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400161 return branch.Latest
162 }
163 return nil
164}
165
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400166func (n *node) GetHash(hash string) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400167 return n.Branches[NONE].Revisions[hash]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400168}
169
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400170func (n *node) initialize(data interface{}, txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400171 var children map[string][]Revision
172 children = make(map[string][]Revision)
173 for fieldName, field := range ChildrenFields(n.Type) {
174 _, fieldValue := GetAttributeValue(data, fieldName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400175
176 if fieldValue.IsValid() {
177 if field.IsContainer {
178 if field.Key != "" {
179 var keysSeen []string
180
181 for i := 0; i < fieldValue.Len(); i++ {
182 v := fieldValue.Index(i)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400183
184 rev := n.MakeNode(v.Interface(), txid).Latest(txid)
185
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400186 _, key := GetAttributeValue(v.Interface(), field.Key, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400187 for _, k := range keysSeen {
188 if k == key.String() {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400189 log.Errorf("duplicate key - %s", k)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400190 }
191 }
192 children[fieldName] = append(children[fieldName], rev)
193 keysSeen = append(keysSeen, key.String())
194 }
195
196 } else {
197 for i := 0; i < fieldValue.Len(); i++ {
198 v := fieldValue.Index(i)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400199 children[fieldName] = append(children[fieldName], n.MakeNode(v.Interface(), txid).Latest())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400200 }
201 }
202 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400203 children[fieldName] = append(children[fieldName], n.MakeNode(fieldValue.Interface(), txid).Latest())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400204 }
205 } else {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400206 log.Errorf("field is invalid - %+v", fieldValue)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400207 }
208 }
209 // FIXME: ClearField??? No such method in go protos. Reset?
210 //data.ClearField(field_name)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400211 branch := NewBranch(n, "", nil, n.AutoPrune)
212 rev := n.MakeRevision(branch, data, children)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400213 n.makeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400214
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400215 if txid == "" {
216 n.Branches[NONE] = branch
217 } else {
218 n.Branches[txid] = branch
219 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400220}
221
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400222func (n *node) findRevByKey(revs []Revision, keyName string, value interface{}) (int, Revision) {
223 for i, rev := range revs {
224 dataValue := reflect.ValueOf(rev.GetData())
225 dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
226
227 fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
228
Stephane Barbarie126101e2018-10-11 16:18:48 -0400229 //log.Debugf("fieldValue: %+v, type: %+v, value: %+v", fieldValue.Interface(), fieldValue.Type(), value)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400230 a := fmt.Sprintf("%s", fieldValue.Interface())
231 b := fmt.Sprintf("%s", value)
232 if a == b {
233 return i, rev
234 }
235 }
236
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400237 log.Errorf("key %s=%s not found", keyName, value)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400238
239 return -1, nil
240}
241
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400242//
243// Get operation
244//
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400245func (n *node) Get(path string, hash string, depth int, deep bool, txid string) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400246 if deep {
247 depth = -1
248 }
249
250 for strings.HasPrefix(path, "/") {
251 path = path[1:]
252 }
253
254 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400255 var rev Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400256
257 // FIXME: should empty txid be cleaned up?
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400258 if branch = n.Branches[txid]; txid == "" || branch == nil {
259 branch = n.Branches[NONE]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400260 }
261
262 if hash != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400263 rev = branch.Revisions[hash]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400264 } else {
265 rev = branch.Latest
266 }
267
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400268 return n.getPath(rev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400269}
270
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400271func (n *node) getPath(rev Revision, path string, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400272 if path == "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400273 return n.getData(rev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400274 }
275
276 partition := strings.SplitN(path, "/", 2)
277 name := partition[0]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400278
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400279 if len(partition) < 2 {
280 path = ""
281 } else {
282 path = partition[1]
283 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400284
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400285 names := ChildrenFields(n.Type)
286 field := names[name]
287
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400288 if field.IsContainer {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400289 if field.Key != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400290 children := rev.GetChildren()[name]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400291 if path != "" {
292 partition = strings.SplitN(path, "/", 2)
293 key := partition[0]
294 path = ""
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400295 keyValue := field.KeyFromStr(key)
296 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev == nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400297 return nil
298 } else {
299 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400300 return childNode.getPath(childRev, path, depth)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400301 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400302 } else {
303 var response []interface{}
304 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400305 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400306 value := childNode.getData(childRev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400307 response = append(response, value)
308 }
309 return response
310 }
311 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400312 var response []interface{}
313 if path != "" {
314 // TODO: raise error
315 return response
316 }
317 for _, childRev := range rev.GetChildren()[name] {
318 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400319 value := childNode.getData(childRev, depth)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400320 response = append(response, value)
321 }
322 return response
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400323 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400324 } else {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400325 childRev := rev.GetChildren()[name][0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400326 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400327 return childNode.getPath(childRev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400328 }
329 return nil
330}
331
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400332func (n *node) getData(rev Revision, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400333 msg := rev.Get(depth)
334
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400335 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400336 log.Debug("invoking proxy GET Callbacks")
Stephane Barbarie126101e2018-10-11 16:18:48 -0400337 msg = n.Proxy.InvokeCallbacks(GET, false, msg)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400338
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400339 }
340 return msg
341}
342
343//
344// Update operation
345//
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400346func (n *node) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400347 // FIXME: is this required ... a bit overkill to take out a "/"
348 for strings.HasPrefix(path, "/") {
349 path = path[1:]
350 }
351
352 var branch *Branch
353 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400354 if txid == "" {
355 branch = n.Branches[NONE]
356 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400357 branch = makeBranch(n)
358 }
359
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400360 log.Debugf("Branch data : %+v, Passed data: %+v", branch.Latest.GetData(), data)
361
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400362 if path == "" {
363 return n.doUpdate(branch, data, strict)
364 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400365
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400366 // TODO missing some code here...
367 rev := branch.Latest
368
369 partition := strings.SplitN(path, "/", 2)
370 name := partition[0]
371
372 if len(partition) < 2 {
373 path = ""
374 } else {
375 path = partition[1]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400376 }
377
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400378 field := ChildrenFields(n.Type)[name]
379 var children []Revision
380
381 if field.IsContainer {
382 if path == "" {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400383 log.Errorf("cannot update a list")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400384 } else if field.Key != "" {
385 partition := strings.SplitN(path, "/", 2)
386 key := partition[0]
387 if len(partition) < 2 {
388 path = ""
389 } else {
390 path = partition[1]
391 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400392 keyValue := field.KeyFromStr(key)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400393 // TODO. Est-ce que le copy ne fonctionne pas? dois-je plutôt faire un clone de chaque item?
394 for _, v := range rev.GetChildren()[name] {
395 revCopy := reflect.ValueOf(v).Interface().(Revision)
396 children = append(children, revCopy)
397 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400398 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400399 childNode := childRev.GetNode()
Stephane Barbarie126101e2018-10-11 16:18:48 -0400400 childNode.Proxy = n.Proxy
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400401 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400402
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400403 if newChildRev.GetHash() == childRev.GetHash() {
404 if newChildRev != childRev {
405 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
406 newChildRev.ClearHash()
407 }
408 return branch.Latest
409 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400410
411 _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0)
412 log.Debugf("newKey is %s", newKey.Interface())
413 _newKeyType := fmt.Sprintf("%s", newKey)
414 _keyValueType := fmt.Sprintf("%s", keyValue)
415 if _newKeyType != _keyValueType {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400416 log.Errorf("cannot change key field")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400417 }
418 children[idx] = newChildRev
419 rev = rev.UpdateChildren(name, children, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400420 branch.Latest.Drop(txid, false)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400421 n.MakeLatest(branch, rev, nil)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400422 return rev
423 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400424 log.Errorf("cannot index into container with no keys")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400425 }
426 } else {
427 childRev := rev.GetChildren()[name][0]
428 childNode := childRev.GetNode()
429 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
430 rev = rev.UpdateChildren(name, []Revision{newChildRev}, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400431 branch.Latest.Drop(txid, false)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400432 n.MakeLatest(branch, rev, nil)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400433 return rev
434 }
435 return nil
436}
437
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400438func (n *node) doUpdate(branch *Branch, data interface{}, strict bool) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400439 log.Debugf("Comparing types - expected: %+v, actual: %+v", reflect.ValueOf(n.Type).Type(), reflect.TypeOf(data))
440
441 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
442 // TODO raise error
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400443 log.Errorf("data does not match type: %+v", n.Type)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400444 return nil
445 }
446
447 // TODO: validate that this actually works
448 //if n.hasChildren(data) {
449 // return nil
450 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400451
452 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400453 log.Debug("invoking proxy PRE_UPDATE Callbacks")
Stephane Barbarie126101e2018-10-11 16:18:48 -0400454 n.Proxy.InvokeCallbacks(PRE_UPDATE, false, branch.Latest.GetData(), data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400455 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400456 if !reflect.DeepEqual(branch.Latest.GetData(), data) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400457 if strict {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400458 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400459 log.Debugf("checking access violations")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400460 }
461 rev := branch.Latest.UpdateData(data, branch)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400462 changes := []ChangeTuple{{POST_UPDATE, branch.Latest.GetData(), rev.GetData()}}
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400463 branch.Latest.Drop(branch.Txid, true)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400464 n.MakeLatest(branch, rev, changes)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400465 return rev
466 } else {
467 return branch.Latest
468 }
469}
470
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400471//
472// Add operation
473//
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400474func (n *node) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400475 for strings.HasPrefix(path, "/") {
476 path = path[1:]
477 }
478 if path == "" {
479 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400480 log.Errorf("cannot add for non-container mode")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400481 }
482
483 var branch *Branch
484 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400485 if txid == "" {
486 branch = n.Branches[NONE]
487 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400488 branch = makeBranch(n)
489 }
490
491 rev := branch.Latest
492
493 partition := strings.SplitN(path, "/", 2)
494 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400495
496 if len(partition) < 2 {
497 path = ""
498 } else {
499 path = partition[1]
500 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400501
502 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400503 var children []Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400504
505 if field.IsContainer {
506 if path == "" {
507 if field.Key != "" {
508 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400509 log.Debug("invoking proxy PRE_ADD Callbacks")
Stephane Barbarie126101e2018-10-11 16:18:48 -0400510 n.Proxy.InvokeCallbacks(PRE_ADD, false, data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400511 }
512
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400513 for _, v := range rev.GetChildren()[name] {
514 revCopy := reflect.ValueOf(v).Interface().(Revision)
515 children = append(children, revCopy)
516 }
517 _, key := GetAttributeValue(data, field.Key, 0)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400518 if _, exists := n.findRevByKey(children, field.Key, key.String()); exists != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400519 // TODO raise error
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400520 log.Errorf("duplicate key found: %s", key.String())
Stephane Barbarie126101e2018-10-11 16:18:48 -0400521 } else {
522 childRev := n.MakeNode(data, txid).Latest(txid)
523 children = append(children, childRev)
524 rev := rev.UpdateChildren(name, children, branch)
525 changes := []ChangeTuple{{POST_ADD, nil, rev.GetData()}}
526 branch.Latest.Drop(txid, false)
527 n.MakeLatest(branch, rev, changes)
528 return rev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400529 }
530
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400531 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400532 log.Errorf("cannot add to non-keyed container")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400533 }
534 } else if field.Key != "" {
535 partition := strings.SplitN(path, "/", 2)
536 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400537 if len(partition) < 2 {
538 path = ""
539 } else {
540 path = partition[1]
541 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400542 keyValue := field.KeyFromStr(key)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400543 copy(children, rev.GetChildren()[name])
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400544 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400545 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400546 newChildRev := childNode.Add(path, data, txid, makeBranch)
547 children[idx] = newChildRev
548 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400549 branch.Latest.Drop(txid, false)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400550 n.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400551 return rev
552 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400553 log.Errorf("cannot add to non-keyed container")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400554 }
555 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400556 log.Errorf("cannot add to non-container field")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400557 }
558 return nil
559}
560
561//
562// Remove operation
563//
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400564func (n *node) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400565 for strings.HasPrefix(path, "/") {
566 path = path[1:]
567 }
568 if path == "" {
569 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400570 log.Errorf("cannot remove for non-container mode")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400571 }
572 var branch *Branch
573 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400574 if txid == "" {
575 branch = n.Branches[NONE]
576 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400577 branch = makeBranch(n)
578 }
579
580 rev := branch.Latest
581
582 partition := strings.SplitN(path, "/", 2)
583 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400584 if len(partition) < 2 {
585 path = ""
586 } else {
587 path = partition[1]
588 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400589
590 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400591 var children []Revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400592 postAnnouncement := []ChangeTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400593
594 if field.IsContainer {
595 if path == "" {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400596 log.Errorf("cannot remove without a key")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400597 } else if field.Key != "" {
598 partition := strings.SplitN(path, "/", 2)
599 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400600 if len(partition) < 2 {
601 path = ""
602 } else {
603 path = partition[1]
604 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400605 keyValue := field.KeyFromStr(key)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400606 if path != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400607 for _, v := range rev.GetChildren()[name] {
608 newV := reflect.ValueOf(v).Interface().(Revision)
609 children = append(children, newV)
610 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400611 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400612 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400613 newChildRev := childNode.Remove(path, txid, makeBranch)
614 children[idx] = newChildRev
615 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400616 branch.Latest.Drop(txid, false)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400617 n.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400618 return rev
619 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400620 for _, v := range rev.GetChildren()[name] {
621 newV := reflect.ValueOf(v).Interface().(Revision)
622 children = append(children, newV)
623 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400624 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400625 if n.Proxy != nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400626 data := childRev.GetData()
Stephane Barbarie126101e2018-10-11 16:18:48 -0400627 n.Proxy.InvokeCallbacks(PRE_REMOVE, false, data)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400628 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data, nil})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400629 } else {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400630 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData(), nil})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400631 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400632 childRev.Drop(txid, true)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400633 children = append(children[:idx], children[idx+1:]...)
634 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400635 branch.Latest.Drop(txid, false)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400636 n.MakeLatest(branch, rev, postAnnouncement)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400637 return rev
638 }
639 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400640 log.Errorf("cannot add to non-keyed container")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400641 }
642 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400643 log.Errorf("cannot add to non-container field")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400644 }
645
646 return nil
647}
648
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400649// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
650
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400651type MakeBranchFunction func(*node) *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400652
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400653func (n *node) MakeBranch(txid string) *Branch {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400654 branchPoint := n.Branches[NONE].Latest
655 branch := NewBranch(n, txid, branchPoint, true)
656 n.Branches[txid] = branch
657 return branch
658}
659
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400660func (n *node) DeleteBranch(txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400661 delete(n.Branches, txid)
662}
663
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400664func (n *node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400665 f := func(rev Revision) Revision {
666 childBranch := rev.GetBranch()
667
668 if childBranch.Txid == txid {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400669 rev, _ = childBranch.Node.MergeBranch(txid, dryRun)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400670 }
671
672 return rev
673 }
674 return f
675}
676
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400677func (n *node) MergeBranch(txid string, dryRun bool) (Revision, error) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400678 srcBranch := n.Branches[txid]
679 dstBranch := n.Branches[NONE]
680
681 forkRev := srcBranch.Origin
682 srcRev := srcBranch.Latest
683 dstRev := dstBranch.Latest
684
685 rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
686
687 if !dryRun {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400688 n.MakeLatest(dstBranch, rev, changes)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400689 delete(n.Branches, txid)
690 }
691
Stephane Barbariee16186c2018-09-11 10:46:34 -0400692 // TODO: return proper error when one occurs
693 return rev, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400694}
695
696// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
697
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400698//func (n *node) diff(hash1, hash2, txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400699// branch := n.Branches[txid]
700// rev1 := branch.get(hash1)
701// rev2 := branch.get(hash2)
702//
703// if rev1.GetHash() == rev2.GetHash() {
704// // empty patch
705// } else {
706// // translate data to json and generate patch
707// patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
708// patch.
709// }
710//}
711
712// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
713
714// TODO: is tag mgmt used in the python implementation? Need to validate
715
716// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
717
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400718func (n *node) hasChildren(data interface{}) bool {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400719 for fieldName, field := range ChildrenFields(n.Type) {
720 _, fieldValue := GetAttributeValue(data, fieldName, 0)
721
722 if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
723 log.Error("cannot update external children")
724 return true
725 }
726 }
727
728 return false
729}
730
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400731// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400732
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400733func (n *node) GetProxy(path string, exclusive bool) *Proxy {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400734 //r := NewRoot(n.Type, n.KvStore)
735 //r.node = n
736 //r.KvStore = n.KvStore
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400737
Stephane Barbarie126101e2018-10-11 16:18:48 -0400738 return n.getProxy(path, path, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400739}
Stephane Barbarie126101e2018-10-11 16:18:48 -0400740func (n *node) getProxy(path string, fullPath string, exclusive bool) *Proxy {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400741 for strings.HasPrefix(path, "/") {
742 path = path[1:]
743 }
744 if path == "" {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400745 return n.makeProxy(path, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400746 }
747
748 rev := n.Branches[NONE].Latest
749 partition := strings.SplitN(path, "/", 2)
750 name := partition[0]
Stephane Barbarie126101e2018-10-11 16:18:48 -0400751 if len(partition) < 2 {
752 path = ""
753 } else {
754 path = partition[1]
755 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400756
757 field := ChildrenFields(n.Type)[name]
Stephane Barbarie126101e2018-10-11 16:18:48 -0400758 if field != nil && field.IsContainer {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400759 if path == "" {
760 log.Error("cannot proxy a container field")
Stephane Barbarie126101e2018-10-11 16:18:48 -0400761 } else if field.Key != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400762 partition := strings.SplitN(path, "/", 2)
763 key := partition[0]
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400764 if len(partition) < 2 {
765 path = ""
766 } else {
767 path = partition[1]
768 }
769 keyValue := field.KeyFromStr(key)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400770 var children []Revision
771 for _, v := range rev.GetChildren()[name] {
772 newV := reflect.ValueOf(v).Interface().(Revision)
773 children = append(children, newV)
774 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400775 _, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400776 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400777
Stephane Barbarie126101e2018-10-11 16:18:48 -0400778 return childNode.getProxy(path, fullPath, exclusive)
779 } else {
780 log.Error("cannot index into container with no keys")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400781 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400782 } else {
783 childRev := rev.GetChildren()[name][0]
784 childNode := childRev.GetNode()
Stephane Barbarie126101e2018-10-11 16:18:48 -0400785 return childNode.getProxy(path, fullPath, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400786 }
787
788 return nil
789}
790
Stephane Barbarie126101e2018-10-11 16:18:48 -0400791func (n *node) makeProxy(fullPath string, exclusive bool) *Proxy {
792 r := &root{
793 node: n,
794 Callbacks: n.Root.Callbacks,
795 NotificationCallbacks: n.Root.NotificationCallbacks,
796 DirtyNodes: n.Root.DirtyNodes,
797 KvStore: n.Root.KvStore,
798 Loading: n.Root.Loading,
799 RevisionClass: n.Root.RevisionClass,
800 }
801
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400802 if n.Proxy == nil {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400803 n.Proxy = NewProxy(r, fullPath, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400804 } else {
805 if n.Proxy.Exclusive {
806 log.Error("node is already owned exclusively")
807 }
808 }
809 return n.Proxy
810}
811
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400812func (n *node) makeEventBus() *EventBus {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400813 if n.EventBus == nil {
814 n.EventBus = NewEventBus()
815 }
816 return n.EventBus
817}
818
819// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Persistence Loading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
820
Stephane Barbarie126101e2018-10-11 16:18:48 -0400821func (n *node) LoadLatest(hash string) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400822 branch := NewBranch(n, "", nil, n.AutoPrune)
823 pr := &PersistedRevision{}
Stephane Barbarie126101e2018-10-11 16:18:48 -0400824 rev := pr.Load(branch, n.Root.KvStore, n.Type, hash)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400825 n.makeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400826 n.Branches[NONE] = branch
827}
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400828
Stephane Barbarie126101e2018-10-11 16:18:48 -0400829func (n *node) ExecuteCallbacks() {
830 n.Root.ExecuteCallbacks()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400831}