gosuki/tree/tree.go

252 lines
5.2 KiB
Go

package tree
import (
"fmt"
"git.sp4ke.xyz/sp4ke/gomark/bookmarks"
"git.sp4ke.xyz/sp4ke/gomark/index"
"git.sp4ke.xyz/sp4ke/gomark/logging"
"git.sp4ke.xyz/sp4ke/gomark/utils"
"github.com/kr/pretty"
"github.com/xlab/treeprint"
)
var log = logging.GetLogger("TREE")
type Bookmark = bookmarks.Bookmark
type NodeType int
const (
RootNode NodeType = iota
URLNode
FolderNode
TagNode
)
type Node struct {
Name string
Type NodeType // folder, tag, url
URL string
Tags []string
Desc string
HasChanged bool
NameHash uint64 // hash of the metadata
Parent *Node
Children []*Node
}
func (node *Node) GetRoot() *Node {
nodePtr := node
for nodePtr.Name != "root" {
nodePtr = nodePtr.Parent
}
return nodePtr
}
// Returns the ancestor of this node
func Ancestor(node *Node) *Node {
if node.Parent == nil {
return node
} else {
return Ancestor(node.Parent)
}
}
func (node *Node) DirectChildOf(parent *Node) bool {
return node.Parent == parent
}
// Finds a node and the tree starting at root
func FindNode(node *Node, root *Node) bool {
if node == root {
return true
} else {
for _, child := range root.Children {
found := FindNode(node, child)
if found { return true }
}
}
return false
}
func FindNodeByName(name string, root *Node) bool {
if name == root.Name {
return true
} else {
for _, child := range root.Children {
found := FindNodeByName(name, child)
if found { return true }
}
}
return false
}
// Inserts child node into parent node. Parent will point to child
// and child will point to parent EXCEPT when parent is a TAG node.
// If parent is a Tag node, child should not point back to parent
// as URL nodes should always point to folder parent nodes only.
func AddChild(parent *Node, child *Node) {
log.Debugf("adding child %v: <%s>", child.Type, child.Name)
if len(parent.Children) == 0 {
parent.Children = []*Node{child}
// Do not point back to TAG parent node from child
if parent.Type != TagNode {
child.Parent = parent
}
return
}
for _, n := range parent.Children {
if child == n {
// log.Errorf("<%s> Node already exists", child)
log.Info(pretty.Sprintf("skipping node <%s>, already exists", child.Name))
return
}
}
parent.Children = append(parent.Children, child)
if parent.Type != TagNode {
child.Parent = parent
}
}
// Return all parent folder nodes for a given URL node
func (node *Node) getParentFolders() []*Node{
var parents []*Node
var walk func(node *Node)
var nodePtr *Node
// breadth first algorithm from lead url node back to root
//FIX: change to depth first algorithm and find all parent folders
// for bookmark ?? There must be a more efficient way to do it ?
walk = func(n *Node) {
nodePtr = n
if nodePtr.Type == RootNode {
return
}
if nodePtr.Type == FolderNode {
parents = append(parents, nodePtr)
}
walk(n.Parent)
}
walk(node)
return parents
}
// Returns all parent tag nodes for a given URL node
func (node *Node) getParentTags() []*Node {
var parents []*Node
var walk func(node *Node)
var nodePtr *Node
root := node.GetRoot()
walk = func(n *Node) {
nodePtr = n
if nodePtr.Type == URLNode {
return
}
if len(nodePtr.Children) == 0 {
return
}
for _, v := range nodePtr.Children {
if v.URL == node.URL &&
nodePtr.Type == TagNode {
parents = append(parents, nodePtr)
}
walk(v)
}
}
walk(root)
return parents
}
func PrintTree(root *Node) {
fmt.Println("---")
fmt.Println("PrintTree")
var walk func(node *Node, tree treeprint.Tree)
tree := treeprint.New()
walk = func(node *Node, t treeprint.Tree) {
if len(node.Children) > 0 {
t = t.AddBranch(fmt.Sprintf("%#v <%s>", node.Type, node.Name))
for _, child := range node.Children {
walk(child, t)
}
} else {
t.AddNode(fmt.Sprintf("%#v <%s>", node.Type, node.Name))
}
}
walk(root, tree)
fmt.Println(tree.String())
fmt.Println("---")
}
// Rebuilds the memory url index after parsing all bookmarks.
// Keeps memory index in sync with last known state of browser bookmarks
func WalkBuildIndex(node *Node, index index.HashTree) {
if node.Type == URLNode {
index.Insert(node.URL, node)
//log.Debugf("Inserted URL: %s and Hash: %v", node.URL, node.NameHash)
}
if len(node.Children) > 0 {
for _, node := range node.Children {
WalkBuildIndex(node, index)
}
}
}
// Get all possible tags for this node
func (node *Node) getTags() []string {
var tags []string
// get all parent tag nodes
parentTags := node.getParentTags()
for _, tagNode := range parentTags {
tags = utils.Extends(tags, tagNode.Name)
}
//FIX: get parent folders and add them as tags T
parentFolders := node.getParentFolders()
for _, fNode := range parentFolders {
tags = utils.Extends(tags, fNode.Name)
}
return tags
}
func (node *Node) GetBookmark() *Bookmark {
return &Bookmark{
URL: node.URL,
Metadata: node.Name,
Desc: node.Desc,
Tags: node.getTags(),
}
}