VOL-1027 : Initial commit of voltha 2.0 data model

Change-Id: Ib8006de1af2166281ccf1c9d7c2b9156991bf4e4
diff --git a/db/model/node.go b/db/model/node.go
new file mode 100644
index 0000000..936a79a
--- /dev/null
+++ b/db/model/node.go
@@ -0,0 +1,456 @@
+package model
+
+import (
+	"fmt"
+	"github.com/golang/protobuf/proto"
+	"reflect"
+	"strings"
+)
+
+const (
+	NONE string = "none"
+)
+
+type Node struct {
+	root      *Root
+	Type      interface{}
+	Branches  map[string]*Branch
+	Tags      map[string]*Revision
+	Proxy     *Proxy
+	EventBus  *EventBus
+	AutoPrune bool
+}
+
+func NewNode(root *Root, initialData interface{}, autoPrune bool, txid string) *Node {
+	cn := &Node{}
+
+	cn.root = root
+	cn.Branches = make(map[string]*Branch)
+	cn.Tags = make(map[string]*Revision)
+	cn.Proxy = nil
+	cn.EventBus = nil
+	cn.AutoPrune = autoPrune
+
+	if IsProtoMessage(initialData) {
+		cn.Type = reflect.ValueOf(initialData).Interface()
+		dataCopy := proto.Clone(initialData.(proto.Message))
+		cn.initialize(dataCopy, txid)
+	} else if reflect.ValueOf(initialData).IsValid() {
+		cn.Type = reflect.ValueOf(initialData).Interface()
+	} else {
+		// not implemented error
+		fmt.Errorf("cannot process initial data - %+v", initialData)
+	}
+
+	return cn
+}
+
+func (cn *Node) makeNode(data interface{}, txid string) *Node {
+	return NewNode(cn.root, data, true, txid)
+}
+
+func (cn *Node) makeRevision(branch *Branch, data interface{}, children map[string][]*Revision) *Revision {
+	return cn.root.makeRevision(branch, data, children)
+}
+
+func (cn *Node) makeLatest(branch *Branch, revision *Revision, changeAnnouncement map[string]interface{}) {
+	if _, ok := branch.revisions[revision.Hash]; !ok {
+		branch.revisions[revision.Hash] = revision
+	}
+
+	if branch.Latest == nil || revision.Hash != branch.Latest.Hash {
+		branch.Latest = revision
+	}
+
+	if changeAnnouncement != nil && branch.Txid == "" {
+		if cn.Proxy != nil {
+			for changeType, data := range changeAnnouncement {
+				// TODO: Invoke callback
+				fmt.Printf("invoking callback - changeType: %+v, data:%+v\n", changeType, data)
+			}
+		}
+
+		for changeType, data := range changeAnnouncement {
+			// TODO: send notifications
+			fmt.Printf("sending notification - changeType: %+v, data:%+v\n", changeType, data)
+		}
+	}
+}
+
+func (cn *Node) Latest() *Revision {
+	if branch, exists := cn.Branches[NONE]; exists {
+		return branch.Latest
+	}
+	return nil
+}
+
+func (cn *Node) GetHash(hash string) *Revision {
+	return cn.Branches[NONE].revisions[hash]
+}
+
+func (cn *Node) initialize(data interface{}, txid string) {
+	var children map[string][]*Revision
+	children = make(map[string][]*Revision)
+	for fieldName, field := range ChildrenFields(cn.Type) {
+		fieldValue := GetAttributeValue(data, fieldName, 0)
+
+		if fieldValue.IsValid() {
+			if field.IsContainer {
+				if field.Key != "" {
+					var keysSeen []string
+
+					for i := 0; i < fieldValue.Len(); i++ {
+						v := fieldValue.Index(i)
+						rev := cn.makeNode(v.Interface(), txid).Latest()
+						key := GetAttributeValue(v.Interface(), field.Key, 0)
+						for _, k := range keysSeen {
+							if k == key.String() {
+								fmt.Errorf("duplicate key - %s", k)
+							}
+						}
+						children[fieldName] = append(children[fieldName], rev)
+						keysSeen = append(keysSeen, key.String())
+					}
+
+				} else {
+					for i := 0; i < fieldValue.Len(); i++ {
+						v := fieldValue.Index(i)
+						children[fieldName] = append(children[fieldName], cn.makeNode(v.Interface(), txid).Latest())
+					}
+				}
+			} else {
+				children[fieldName] = append(children[fieldName], cn.makeNode(fieldValue.Interface(), txid).Latest())
+			}
+		} else {
+			fmt.Errorf("field is invalid - %+v", fieldValue)
+		}
+	}
+	// FIXME: ClearField???  No such method in go protos.  Reset?
+	//data.ClearField(field_name)
+	branch := NewBranch(cn, "", nil, cn.AutoPrune)
+	rev := cn.makeRevision(branch, data, children)
+	cn.makeLatest(branch, rev, nil)
+	cn.Branches[txid] = branch
+}
+
+func (cn *Node) makeTxBranch(txid string) *Branch {
+	branchPoint := cn.Branches[NONE].Latest
+	branch := NewBranch(cn, txid, branchPoint, true)
+	cn.Branches[txid] = branch
+	return branch
+}
+
+func (cn *Node) deleteTxBranch(txid string) {
+	delete(cn.Branches, txid)
+}
+
+type t_makeBranch func(*Node) *Branch
+
+//
+// Get operation
+//
+func (cn *Node) Get(path string, hash string, depth int, deep bool, txid string) interface{} {
+	if deep {
+		depth = -1
+	}
+
+	for strings.HasPrefix(path, "/") {
+		path = path[1:]
+	}
+
+	var branch *Branch
+	var rev *Revision
+
+	// FIXME: should empty txid be cleaned up?
+	if branch = cn.Branches[txid]; txid == "" || branch == nil {
+		branch = cn.Branches[NONE]
+	}
+
+	if hash != "" {
+		rev = branch.revisions[hash]
+	} else {
+		rev = branch.Latest
+	}
+
+	return cn.get(rev, path, depth)
+}
+
+func (cn *Node) findRevByKey(revs []*Revision, keyName string, value string) (int, *Revision) {
+	for i, rev := range revs {
+		dataValue := reflect.ValueOf(rev.Config.Data)
+		dataStruct := GetAttributeStructure(rev.Config.Data, keyName, 0)
+
+		fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
+
+		if fieldValue.Interface().(string) == value {
+			return i, rev
+		}
+	}
+
+	fmt.Errorf("key %s=%s not found", keyName, value)
+
+	return -1, nil
+}
+
+func (cn *Node) get(rev *Revision, path string, depth int) interface{} {
+	if path == "" {
+		return cn.doGet(rev, depth)
+	}
+
+	partition := strings.SplitN(path, "/", 2)
+	name := partition[0]
+	path = partition[1]
+
+	field := ChildrenFields(cn.Type)[name]
+
+	if field.IsContainer {
+		if field.Key != "" {
+			children := rev.Children[name]
+			if path != "" {
+				partition = strings.SplitN(path, "/", 2)
+				key := partition[0]
+				path = ""
+				key = field.KeyFromStr(key).(string)
+				_, childRev := cn.findRevByKey(children, field.Key, key)
+				childNode := childRev.getNode()
+				return childNode.get(childRev, path, depth)
+			} else {
+				var response []interface{}
+				for _, childRev := range children {
+					childNode := childRev.getNode()
+					value := childNode.doGet(childRev, depth)
+					response = append(response, value)
+				}
+				return response
+			}
+		} else {
+			childRev := rev.Children[name][0]
+			childNode := childRev.getNode()
+			return childNode.get(childRev, path, depth)
+		}
+	}
+	return nil
+}
+
+func (cn *Node) doGet(rev *Revision, depth int) interface{} {
+	msg := rev.Get(depth)
+
+	if cn.Proxy != nil {
+		// TODO: invoke GET callback
+		fmt.Println("invoking proxy GET Callbacks")
+	}
+	return msg
+}
+
+//
+// Update operation
+//
+func (n *Node) Update(path string, data interface{}, strict bool, txid string, makeBranch t_makeBranch) *Revision {
+	// FIXME: is this required ... a bit overkill to take out a "/"
+	for strings.HasPrefix(path, "/") {
+		path = path[1:]
+	}
+
+	var branch *Branch
+	var ok bool
+	if branch, ok = n.Branches[txid]; !ok {
+		branch = makeBranch(n)
+	}
+
+	if path == "" {
+		return n.doUpdate(branch, data, strict)
+	}
+	return &Revision{}
+}
+
+func (n *Node) doUpdate(branch *Branch, data interface{}, strict bool) *Revision {
+	if reflect.TypeOf(data) != n.Type {
+		// TODO raise error
+		fmt.Errorf("data does not match type: %+v", n.Type)
+	}
+
+	// TODO: noChildren?
+
+	if n.Proxy != nil {
+		// TODO: n.proxy.InvokeCallbacks(CallbackType.PRE_UPDATE, data)
+		fmt.Println("invoking proxy PRE_UPDATE Callbacks")
+	}
+	if branch.Latest.getData() != data {
+		if strict {
+			// TODO: checkAccessViolations(data, branch.GetLatest.data)
+			fmt.Println("checking access violations")
+		}
+		rev := branch.Latest.UpdateData(data, branch)
+		n.makeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.POST_UPDATE, rev.data)
+		return rev
+	} else {
+		return branch.Latest
+	}
+}
+
+// TODO: the python implementation has a method to check if the data has no children
+//func (n *SomeNode) noChildren(data interface{}) bool {
+//	for fieldName, field := range ChildrenFields(n.Type) {
+//		fieldValue := GetAttributeValue(data, fieldName)
+//
+//		if fieldValue.IsValid() {
+//			if field.IsContainer {
+//				if len(fieldValue) > 0 {
+//
+//				}
+//			} else {
+//
+//			}
+//
+//		}
+//	}
+//}
+
+//
+// Add operation
+//
+func (n *Node) Add(path string, data interface{}, txid string, makeBranch t_makeBranch) *Revision {
+	for strings.HasPrefix(path, "/") {
+		path = path[1:]
+	}
+	if path == "" {
+		// TODO raise error
+		fmt.Errorf("cannot add for non-container mode\n")
+	}
+
+	var branch *Branch
+	var ok bool
+	if branch, ok = n.Branches[txid]; !ok {
+		branch = makeBranch(n)
+	}
+
+	rev := branch.Latest
+
+	partition := strings.SplitN(path, "/", 2)
+	name := partition[0]
+	path = partition[1]
+
+	field := ChildrenFields(n.Type)[name]
+	var children []*Revision
+
+	if field.IsContainer {
+		if path == "" {
+			if field.Key != "" {
+				if n.Proxy != nil {
+					// TODO -> n.proxy.InvokeCallbacks(PRE_ADD, data)
+					fmt.Println("invoking proxy PRE_ADD Callbacks")
+				}
+
+				copy(children, rev.Children[name])
+				key := GetAttributeValue(data, field.Key, 0)
+				if _, rev := n.findRevByKey(children, field.Key, key.String()); rev != nil {
+					// TODO raise error
+					fmt.Errorf("duplicate key found: %s", key.String())
+				}
+
+				childRev := n.makeNode(data, "").Latest()
+				children = append(children, childRev)
+				rev := rev.UpdateChildren(name, children, branch)
+				n.makeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.POST_ADD, rev.data)
+				return rev
+			} else {
+				fmt.Errorf("cannot add to non-keyed container\n")
+			}
+		} else if field.Key != "" {
+			partition := strings.SplitN(path, "/", 2)
+			key := partition[0]
+			path = partition[1]
+			key = field.KeyFromStr(key).(string)
+			copy(children, rev.Children[name])
+			idx, childRev := n.findRevByKey(children, field.Key, key)
+			childNode := childRev.getNode()
+			newChildRev := childNode.Add(path, data, txid, makeBranch)
+			children[idx] = newChildRev
+			rev := rev.UpdateChildren(name, children, branch)
+			n.makeLatest(branch, rev, nil)
+			return rev
+		} else {
+			fmt.Errorf("cannot add to non-keyed container\n")
+		}
+	} else {
+		fmt.Errorf("cannot add to non-container field\n")
+	}
+	return nil
+}
+
+//
+// Remove operation
+//
+func (n *Node) Remove(path string, txid string, makeBranch t_makeBranch) *Revision {
+	for strings.HasPrefix(path, "/") {
+		path = path[1:]
+	}
+	if path == "" {
+		// TODO raise error
+		fmt.Errorf("cannot remove for non-container mode\n")
+	}
+	var branch *Branch
+	var ok bool
+	if branch, ok = n.Branches[txid]; !ok {
+		branch = makeBranch(n)
+	}
+
+	rev := branch.Latest
+
+	partition := strings.SplitN(path, "/", 2)
+	name := partition[0]
+	path = partition[1]
+
+	field := ChildrenFields(n.Type)[name]
+	var children []*Revision
+	post_anno := make(map[string]interface{})
+
+	if field.IsContainer {
+		if path == "" {
+			fmt.Errorf("cannot remove without a key\n")
+		} else if field.Key != "" {
+			partition := strings.SplitN(path, "/", 2)
+			key := partition[0]
+			path = partition[1]
+			key = field.KeyFromStr(key).(string)
+			if path != "" {
+				copy(children, rev.Children[name])
+				idx, childRev := n.findRevByKey(children, field.Key, key)
+				childNode := childRev.getNode()
+				newChildRev := childNode.Remove(path, txid, makeBranch)
+				children[idx] = newChildRev
+				rev := rev.UpdateChildren(name, children, branch)
+				n.makeLatest(branch, rev, nil)
+				return rev
+			} else {
+				copy(children, rev.Children[name])
+				idx, childRev := n.findRevByKey(children, field.Key, key)
+				if n.Proxy != nil {
+					data := childRev.getData()
+					fmt.Println("invoking proxy PRE_REMOVE Callbacks")
+					fmt.Printf("setting POST_REMOVE Callbacks : %+v\n", data)
+				} else {
+					fmt.Println("setting POST_REMOVE Callbacks")
+				}
+				children = append(children[:idx], children[idx+1:]...)
+				rev := rev.UpdateChildren(name, children, branch)
+				n.makeLatest(branch, rev, post_anno)
+				return rev
+			}
+		} else {
+			fmt.Errorf("cannot add to non-keyed container\n")
+		}
+	} else {
+		fmt.Errorf("cannot add to non-container field\n")
+	}
+
+	return nil
+}
+
+func (n *Node) LoadLatest(kvStore *Backend, hash string) {
+	branch := NewBranch(n, "", nil, n.AutoPrune)
+	pr := &PersistedRevision{}
+	rev := pr.load(branch, kvStore, n.Type, hash)
+	n.makeLatest(branch, rev.Revision, nil)
+	n.Branches[NONE] = branch
+}