|
@@ -0,0 +1,359 @@
|
|
|
+package node
|
|
|
+
|
|
|
+import (
|
|
|
+ "encoding/json"
|
|
|
+ "fmt"
|
|
|
+ "reflect"
|
|
|
+)
|
|
|
+
|
|
|
+type Node struct {
|
|
|
+ name string
|
|
|
+ data any
|
|
|
+ parent *Node
|
|
|
+ children []*Node
|
|
|
+}
|
|
|
+
|
|
|
+func NewNode() *Node {
|
|
|
+ return &Node{}
|
|
|
+}
|
|
|
+
|
|
|
+func NewNodeWithName(name string) *Node {
|
|
|
+ return &Node{name: name}
|
|
|
+}
|
|
|
+
|
|
|
+func NewNodeWithData(data any) *Node {
|
|
|
+ return &Node{data: data}
|
|
|
+}
|
|
|
+
|
|
|
+func NewNodeWithNameAndData(name string, data any) *Node {
|
|
|
+ return &Node{name: name, data: data}
|
|
|
+}
|
|
|
+
|
|
|
+func NewNodeWithDataAndName(data any, name string) *Node {
|
|
|
+ return &Node{name: name, data: data}
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) Name(name ...string) string {
|
|
|
+ if len(name) != 0 {
|
|
|
+ n.name = name[0]
|
|
|
+ }
|
|
|
+ return n.name
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) Data(data ...any) any {
|
|
|
+ if len(data) != 0 {
|
|
|
+ n.data = data[0]
|
|
|
+ }
|
|
|
+ return n.data
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) Len() int {
|
|
|
+ return len(n.children)
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) Parent(parent ...*Node) *Node {
|
|
|
+ if len(parent) != 0 {
|
|
|
+ n.parent = parent[0]
|
|
|
+ }
|
|
|
+ return n.parent
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) Root() *Node {
|
|
|
+ if n.Parent() == nil {
|
|
|
+ return n
|
|
|
+ }
|
|
|
+ at := n.Parent()
|
|
|
+ for at.Parent() != nil {
|
|
|
+ at = at.Parent()
|
|
|
+ }
|
|
|
+ return at
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) Depth() int {
|
|
|
+ if n.Parent() == nil {
|
|
|
+ return 0
|
|
|
+ }
|
|
|
+ var (
|
|
|
+ depth int = 1
|
|
|
+ at *Node = n.Parent()
|
|
|
+ )
|
|
|
+ for at.Parent() != nil {
|
|
|
+ depth++
|
|
|
+ at = at.Parent()
|
|
|
+ }
|
|
|
+ return depth
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) Kids() []*Node {
|
|
|
+ return n.children
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) AddKid(kid *Node) bool {
|
|
|
+ for _, k := range n.children {
|
|
|
+ if k == kid {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ kid.Parent(n)
|
|
|
+ n.children = append(n.children, kid)
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) NewKid() *Node {
|
|
|
+ k := &Node{}
|
|
|
+ n.AddKid(k)
|
|
|
+ return k
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) NewKidWithName(name string) *Node {
|
|
|
+ k := n.NewKid()
|
|
|
+ k.Name(name)
|
|
|
+ return k
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) NewKidWithData(data any) *Node {
|
|
|
+ k := n.NewKid()
|
|
|
+ k.Data(data)
|
|
|
+ return k
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) NewKidWithNameAndData(name string, data any) *Node {
|
|
|
+ k := n.NewKid()
|
|
|
+ k.Name(name)
|
|
|
+ k.Data(data)
|
|
|
+ return k
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) NewKidWithDataAndName(data any, name string) *Node {
|
|
|
+ k := n.NewKid()
|
|
|
+ k.Name(name)
|
|
|
+ k.Data(data)
|
|
|
+ return k
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) Index() int {
|
|
|
+ if n.Parent() == nil { // There is no parent, so it's not known
|
|
|
+ return -1
|
|
|
+ }
|
|
|
+ // Iterate thru the children of the parent to find ourselves
|
|
|
+ for i, kid := range n.Parent().Kids() {
|
|
|
+ if kid == n {
|
|
|
+ return i
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Ok we for some reason are not in that list, so it's not known
|
|
|
+ return -1
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) Kid(index int) *Node {
|
|
|
+ if n == nil || index > n.Len() || index < 0 {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return n.children[index]
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) KidByName(name string, recurse ...bool) *Node {
|
|
|
+ for _, kid := range n.children {
|
|
|
+ if kid.name == name {
|
|
|
+ return kid
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if len(recurse) != 0 {
|
|
|
+ if recurse[0] {
|
|
|
+ for _, kid := range n.children {
|
|
|
+ r := kid.KidByName(name, recurse...)
|
|
|
+ if r != nil {
|
|
|
+ return r
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) RemoveKid(index int) bool {
|
|
|
+ if index > n.Len() || index < 0 {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ n.children[index].Parent(nil)
|
|
|
+ _, ok := arrayDelete(&n.children, index)
|
|
|
+ return ok
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) RemoveAllKids() {
|
|
|
+ for _, kid := range n.children {
|
|
|
+ kid.Parent(nil)
|
|
|
+ }
|
|
|
+ n.children = []*Node{}
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) Detach() bool {
|
|
|
+ if n.Parent() != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ index := n.Index()
|
|
|
+ if index == -1 {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return n.Parent().RemoveKid(index)
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) Destroy() {
|
|
|
+ if n.Parent() != nil {
|
|
|
+ n.Detach()
|
|
|
+ }
|
|
|
+ n.Data(nil)
|
|
|
+ n.Name("")
|
|
|
+ for _, kid := range n.children {
|
|
|
+ kid.Destroy()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Checks if the children of this node are better represented as a map or an array
|
|
|
+//
|
|
|
+// This is determined by:
|
|
|
+//
|
|
|
+// * Totaling number of children without names
|
|
|
+//
|
|
|
+// * Comparing the percent of children without names to total children (if less than 50% use a map, if more than 50% use an array)
|
|
|
+func (n *Node) IsMapLike() bool {
|
|
|
+ if n.Len() == 0 {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ var (
|
|
|
+ total int = n.Len() // number of children
|
|
|
+ noname int = 0 // number of children with no name
|
|
|
+ )
|
|
|
+ for _, kid := range n.Kids() {
|
|
|
+ if len(kid.Name()) == 0 { // When it has no name it's not map-like
|
|
|
+ noname += 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Form a percent of no name children and compare it to less than 50%
|
|
|
+ return int((float32(noname)/float32(total))*100.0) <= 50
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) from(A any) {
|
|
|
+ switch reflect.TypeOf(A).Kind() {
|
|
|
+ case reflect.Map:
|
|
|
+ // Ok, key, value
|
|
|
+ // key == name, value if not a map, array, slice is data
|
|
|
+ m := A.(map[string]any)
|
|
|
+ for k, v := range m {
|
|
|
+ switch reflect.TypeOf(v).Kind() {
|
|
|
+ case reflect.Map, reflect.Array, reflect.Slice:
|
|
|
+ // New kid, with name, and recurse
|
|
|
+ kid := n.NewKidWithName(k)
|
|
|
+ kid.from(v)
|
|
|
+ default:
|
|
|
+ // New kid, with name, and data
|
|
|
+ n.NewKidWithNameAndData(k, v)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case reflect.Array, reflect.Slice:
|
|
|
+ // Ok, values
|
|
|
+ // no name (use index), value if not a map, array, slice is data
|
|
|
+ a := A.([]any)
|
|
|
+ for idx, v := range a {
|
|
|
+ switch reflect.TypeOf(v).Kind() {
|
|
|
+ case reflect.Map, reflect.Array, reflect.Slice:
|
|
|
+ kid := n.NewKidWithName(fmt.Sprintf("%d", idx))
|
|
|
+ kid.from(v)
|
|
|
+ default:
|
|
|
+ n.NewKidWithNameAndData(fmt.Sprintf("%d", idx), v)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ // Ok it's just a single value (most likely)
|
|
|
+ n.Data(A)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) ToMap() any {
|
|
|
+ if len(n.Name()) != 0 && n.Len() != 0 { // Name + Children
|
|
|
+ if n.IsMapLike() {
|
|
|
+ // Map
|
|
|
+ m := map[string]any{}
|
|
|
+ for i, kid := range n.children {
|
|
|
+ if len(kid.Name()) != 0 {
|
|
|
+ m[kid.Name()] = kid.ToMap()
|
|
|
+ } else {
|
|
|
+ m[fmt.Sprint(i)] = kid.ToMap()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if n.Depth() != 0 {
|
|
|
+ return m
|
|
|
+ } else {
|
|
|
+ return map[string]any{
|
|
|
+ n.Name(): m,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // Array
|
|
|
+ a := []any{}
|
|
|
+ for _, kid := range n.children {
|
|
|
+ a = append(a, kid.ToMap())
|
|
|
+ }
|
|
|
+ if n.Depth() != 0 {
|
|
|
+ return a
|
|
|
+ } else {
|
|
|
+ return map[string]any{
|
|
|
+ n.Name(): a,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if len(n.Name()) == 0 && n.Len() != 0 { // No Name + Children
|
|
|
+ if n.IsMapLike() {
|
|
|
+ // Map
|
|
|
+ m := map[string]any{}
|
|
|
+ for i, kid := range n.children {
|
|
|
+ if len(kid.Name()) != 0 {
|
|
|
+ m[kid.Name()] = kid.ToMap()
|
|
|
+ } else {
|
|
|
+ m[fmt.Sprint(i)] = kid.ToMap()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return m
|
|
|
+ } else {
|
|
|
+ // Array
|
|
|
+ a := []any{}
|
|
|
+ for _, kid := range n.children {
|
|
|
+ a = append(a, kid.ToMap())
|
|
|
+ }
|
|
|
+ if n.Depth() != 0 {
|
|
|
+ return a
|
|
|
+ } else {
|
|
|
+ return map[string]any{
|
|
|
+ fmt.Sprint(n.Index()): a,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if n.Data() != nil {
|
|
|
+ if n.Depth() != 0 || len(n.Name()) == 0 {
|
|
|
+ return n.Data()
|
|
|
+ } else {
|
|
|
+ return map[string]any{
|
|
|
+ n.Name(): n.Data(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// Marshals the results from ToMap
|
|
|
+func (n *Node) MarshalJSON() ([]byte, error) {
|
|
|
+ return json.Marshal(n.ToMap())
|
|
|
+}
|
|
|
+
|
|
|
+// Unmarshal function
|
|
|
+func (n *Node) UnmarshalJSON(pay []byte) error {
|
|
|
+ var m map[string]any
|
|
|
+ err := json.Unmarshal(pay, &m)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ n.Destroy()
|
|
|
+ // In this case, the struct isn't in a invalid state, it's just a reset
|
|
|
+ n.from(m)
|
|
|
+ return nil
|
|
|
+}
|