boltbrowser/bolt_model.go
Brian Buller 20338d4671 Things are progressing
Deleting and Editing are working
2015-05-04 13:50:41 -05:00

470 lines
10 KiB
Go

package main
import (
"errors"
"fmt"
"github.com/boltdb/bolt"
"strings"
)
// A Database, basically a collection of buckets
type BoltDB struct {
buckets []BoltBucket
}
type BoltBucket struct {
name string
path []string
pairs []BoltPair
buckets []BoltBucket
parent *BoltBucket
expanded bool
}
type BoltPair struct {
path []string
parent *BoltBucket
key string
val string
}
func (bd *BoltDB) getDepthFromPath(path []string) int {
depth := 0
b, p, err := bd.getGenericFromPath(path)
if err != nil {
return -1
}
if p != nil {
b = p.parent
depth += 1
}
for b != nil {
b = b.parent
depth += 1
}
return depth
}
func (bd *BoltDB) getGenericFromPath(path []string) (*BoltBucket, *BoltPair, error) {
// Check if 'path' leads to a pair
p, err := bd.getPairFromPath(path)
if err == nil {
return nil, p, nil
}
// Nope, check if it leads to a bucket
b, err := bd.getBucketFromPath(path)
if err == nil {
return b, nil, nil
}
// Nope, error
return nil, nil, errors.New("Invalid Path")
}
func (bd *BoltDB) removeGenericAtPath(path []string) error {
b, p, err := bd.getGenericFromPath(path)
if err != nil {
return err
}
if p != nil {
pb := p.parent
for i := range pb.pairs {
if pb.pairs[i-1].key == p.key {
pb.pairs = append(pb.pairs[:i-1], pb.pairs[i+1:]...)
}
}
}
if b != nil {
pb := b.parent
if pb == nil {
for i := range bd.buckets {
if bd.buckets[i-1].name == b.name {
bd.buckets = append(bd.buckets[:i-1], bd.buckets[i+1:]...)
}
}
} else {
for i := range pb.buckets {
if pb.buckets[i-1].name == b.name {
pb.buckets = append(pb.buckets[:i-1], pb.buckets[i+1:]...)
}
}
}
}
return err
}
func (bd *BoltDB) getBucketFromPath(path []string) (*BoltBucket, error) {
if len(path) > 0 {
// Find the BoltBucket with a path == path
var b *BoltBucket
var err error
// Find the root bucket
b, err = memBolt.getBucket(path[0])
if err != nil {
return nil, err
}
if len(path) > 1 {
for p := 1; p < len(path); p++ {
return b.getBucket(path[p])
}
}
return b, nil
}
return nil, errors.New("Invalid Path")
}
func (bd *BoltDB) getPairFromPath(path []string) (*BoltPair, error) {
b, err := memBolt.getBucketFromPath(path[:len(path)-1])
if err != nil {
return nil, err
}
// Found the bucket, pull out the pair
p, err := b.getPair(path[len(path)-1])
return p, err
}
func (bd *BoltDB) getVisibleItemCount(path []string) (int, error) {
vis := 0
var ret_err error
if len(path) == 0 {
for i := range bd.buckets {
n, err := bd.getVisibleItemCount(bd.buckets[i].path)
if err != nil {
return 0, err
}
vis += n
}
} else {
b, err := bd.getBucketFromPath(path)
if err != nil {
return 0, err
}
// 1 for the bucket
vis += 1
if b.expanded {
// This bucket is expanded, add up it's children
// * 1 for each pair
vis += len(b.pairs)
// * recurse for buckets
for i := range b.buckets {
n, err := bd.getVisibleItemCount(b.buckets[i].path)
if err != nil {
return 0, err
}
vis += n
}
}
}
return vis, ret_err
}
func (bd *BoltDB) buildVisiblePathSlice(path []string) ([]string, error) {
var ret_slice []string
var ret_err error
if len(path) == 0 {
for i := range bd.buckets {
n, err := bd.buildVisiblePathSlice(bd.buckets[i].path)
if err != nil {
return nil, err
}
ret_slice = append(ret_slice, n...)
}
} else {
b, err := bd.getBucketFromPath(path)
if err != nil {
return nil, err
}
// Add the bucket's path
ret_slice = append(ret_slice, strings.Join(b.path, "/"))
if b.expanded {
// This bucket is expanded, add up it's children
// * recurse for buckets
for i := range b.buckets {
n, err := bd.buildVisiblePathSlice(b.buckets[i].path)
if err != nil {
return nil, err
}
ret_slice = append(ret_slice, n...)
}
// * one path for each pair
for i := range b.pairs {
ret_slice = append(ret_slice, strings.Join(b.pairs[i].path, "/"))
}
}
}
return ret_slice, ret_err
}
func (bd *BoltDB) getPrevVisiblePath(path []string) []string {
vis_paths, err := bd.buildVisiblePathSlice(nil)
if path == nil {
return strings.Split(vis_paths[len(vis_paths)-1], "/")
}
if err == nil {
find_path := strings.Join(path, "/")
for i := range vis_paths {
if vis_paths[i] == find_path && i > 0 {
return strings.Split(vis_paths[i-1], "/")
}
}
}
return nil
}
func (bd *BoltDB) getNextVisiblePath(path []string) []string {
vis_paths, err := bd.buildVisiblePathSlice(nil)
if path == nil {
return strings.Split(vis_paths[0], "/")
}
if err == nil {
find_path := strings.Join(path, "/")
for i := range vis_paths {
if vis_paths[i] == find_path && i < len(vis_paths)-1 {
return strings.Split(vis_paths[i+1], "/")
}
}
}
return nil
}
func (bd *BoltDB) toggleOpenBucket(path []string) error {
// Find the BoltBucket with a path == path
b, err := bd.getBucketFromPath(path)
if err == nil {
b.expanded = !b.expanded
}
return err
}
func (bd *BoltDB) closeBucket(path []string) error {
// Find the BoltBucket with a path == path
b, err := bd.getBucketFromPath(path)
if err == nil {
b.expanded = false
}
return err
}
func (bd *BoltDB) openBucket(path []string) error {
// Find the BoltBucket with a path == path
b, err := bd.getBucketFromPath(path)
if err == nil {
b.expanded = true
}
return err
}
func (bd *BoltDB) getBucket(k string) (*BoltBucket, error) {
for i := range bd.buckets {
if bd.buckets[i].name == k {
return &bd.buckets[i], nil
}
}
return nil, errors.New("Bucket Not Found")
}
func (bd *BoltDB) syncOpenBuckets(shadow *BoltDB) {
// First test this bucket
for i := range bd.buckets {
for j := range shadow.buckets {
if bd.buckets[i].name == shadow.buckets[j].name {
bd.buckets[i].syncOpenBuckets(&shadow.buckets[j])
}
}
}
}
func (b *BoltBucket) syncOpenBuckets(shadow *BoltBucket) {
// First test this bucket
b.expanded = shadow.expanded
for i := range b.buckets {
for j := range shadow.buckets {
if b.buckets[i].name == shadow.buckets[j].name {
b.buckets[i].syncOpenBuckets(&shadow.buckets[j])
}
}
}
}
func (b *BoltBucket) getBucket(k string) (*BoltBucket, error) {
for i := range b.buckets {
if b.buckets[i].name == k {
return &b.buckets[i], nil
}
}
return nil, errors.New("Bucket Not Found")
}
func (b *BoltBucket) getPair(k string) (*BoltPair, error) {
for i := range b.pairs {
if b.pairs[i].key == k {
return &b.pairs[i], nil
}
}
return nil, errors.New("Pair Not Found")
}
func deleteKey(path []string) error {
err := db.Update(func(tx *bolt.Tx) error {
// len(b.path)-1 is the key we need to delete, the rest are buckets leading to that key
if len(path) == 1 {
// Deleting a root bucket
return tx.DeleteBucket([]byte(path[0]))
} else {
b := tx.Bucket([]byte(path[0]))
if b != nil {
if len(path) > 1 {
for i := range path[1 : len(path)-1] {
b = b.Bucket([]byte(path[i+1]))
if b == nil {
return errors.New("deleteKey: Invalid Path")
}
}
}
// Now delete the last key in the path
var err error
if delete_bkt := b.Bucket([]byte(path[len(path)-1])); delete_bkt == nil {
// Must be a pair
err = b.Delete([]byte(path[len(path)-1]))
} else {
err = b.DeleteBucket([]byte(path[len(path)-1]))
}
return err
} else {
return errors.New("deleteKey: Invalid Path")
}
}
})
return err
}
func refreshDatabase() *BoltDB {
// Reload the database into memBolt
memBolt = new(BoltDB)
db.View(func(tx *bolt.Tx) error {
return tx.ForEach(func(nm []byte, b *bolt.Bucket) error {
bb, err := readBucket(b)
if err == nil {
bb.name = string(nm)
bb.path = []string{bb.name}
bb.expanded = false
memBolt.buckets = append(memBolt.buckets, *bb)
updatePaths(bb)
return nil
}
return err
})
})
return memBolt
}
func readBucket(b *bolt.Bucket) (*BoltBucket, error) {
bb := new(BoltBucket)
b.ForEach(func(k, v []byte) error {
if v == nil {
tb, err := readBucket(b.Bucket(k))
tb.parent = bb
if err == nil {
tb.name = string(k)
tb.path = append(bb.path, tb.name)
bb.buckets = append(bb.buckets, *tb)
}
} else {
tp := BoltPair{key: string(k), val: string(v)}
tp.parent = bb
tp.path = append(bb.path, tp.key)
bb.pairs = append(bb.pairs, tp)
}
return nil
})
return bb, nil
}
func updatePaths(b *BoltBucket) {
for i := range b.buckets {
b.buckets[i].path = append(b.path, b.buckets[i].name)
updatePaths(&b.buckets[i])
}
for i := range b.pairs {
b.pairs[i].path = append(b.path, b.pairs[i].key)
}
}
/*
func renameBucket(path []string, name string) error {
err := db.Update(func(tx *bolt.Tx) error {
// len(b.path)-1 is the key we need to delete, the rest are buckets leading to that key
b := tx.Bucket([]byte(path[0]))
if b != nil {
if len(path) > 1 {
for i := range path[1 : len(path)-1] {
b = b.Bucket([]byte(path[i+1]))
if b == nil {
return errors.New("updatePairValue: Invalid Path")
}
}
}
// Now update the last key in the path
err := b.Put([]byte(path[len(path)-1]), []byte(v))
return err
} else {
return errors.New("renameBucket: Invalid Path")
}
})
refreshDatabase()
return err
}
*/
func updatePairValue(path []string, v string) error {
err := db.Update(func(tx *bolt.Tx) error {
// len(b.path)-1 is the key we need to delete, the rest are buckets leading to that key
b := tx.Bucket([]byte(path[0]))
if b != nil {
if len(path) > 1 {
for i := range path[1 : len(path)-1] {
b = b.Bucket([]byte(path[i+1]))
if b == nil {
return errors.New("updatePairValue: Invalid Path")
}
}
}
// Now update the last key in the path
err := b.Put([]byte(path[len(path)-1]), []byte(v))
return err
} else {
return errors.New("updatePairValue: Invalid Path")
}
})
return err
}
func insertBucket(path []string, n string) error {
// Inserts a new bucket named 'n' at 'path[len(path)-2]
err := db.Update(func(tx *bolt.Tx) error {
if len(path) == 1 {
// insert at root
_, err := tx.CreateBucket([]byte(n))
if err != nil {
return fmt.Errorf("insertBucket: %s", err)
}
} else if len(path) > 1 {
var err error
b := tx.Bucket([]byte(path[0]))
if b != nil {
if len(path) > 2 {
for i := range path[1 : len(path)-2] {
b = b.Bucket([]byte(path[i+1]))
if b == nil {
return fmt.Errorf("insertBucket: %s", err)
}
}
}
_, err := b.CreateBucket([]byte(n))
if err != nil {
return fmt.Errorf("insertBucket: %s", err)
}
}
}
return nil
})
return err
}