2018-01-29 19:44:18 +00:00
|
|
|
// Copyright (c) 2017 Arista Networks, Inc.
|
|
|
|
// Use of this source code is governed by the Apache License 2.0
|
|
|
|
// that can be found in the COPYING file.
|
|
|
|
|
|
|
|
package path
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
"github.com/aristanetworks/goarista/key"
|
|
|
|
)
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
// Map associates paths to values. It allows wildcards. A Map
|
|
|
|
// is primarily used to register handlers with paths that can
|
|
|
|
// be easily looked up each time a path is updated.
|
|
|
|
type Map struct {
|
2018-01-29 19:44:18 +00:00
|
|
|
val interface{}
|
2018-09-05 15:36:14 +00:00
|
|
|
ok bool
|
|
|
|
wildcard *Map
|
|
|
|
children map[key.Key]*Map
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
// VisitorFunc is a function that handles the value associated
|
|
|
|
// with a path in a Map. Note that only the value is passed in
|
|
|
|
// as an argument since the path can be stored inside the value
|
|
|
|
// if needed.
|
|
|
|
type VisitorFunc func(v interface{}) error
|
2018-01-29 19:44:18 +00:00
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
// Visit calls a function fn for every value in the Map
|
|
|
|
// that is registered with a match of a path p. In the
|
|
|
|
// general case, time complexity is linear with respect
|
|
|
|
// to the length of p but it can be as bad as O(2^len(p))
|
|
|
|
// if there are a lot of paths with wildcards registered.
|
|
|
|
//
|
|
|
|
// Example:
|
|
|
|
//
|
|
|
|
// a := path.New("foo", "bar", "baz")
|
|
|
|
// b := path.New("foo", path.Wildcard, "baz")
|
|
|
|
// c := path.New(path.Wildcard, "bar", "baz")
|
|
|
|
// d := path.New("foo", "bar", path.Wildcard)
|
|
|
|
// e := path.New(path.Wildcard, path.Wildcard, "baz")
|
|
|
|
// f := path.New(path.Wildcard, "bar", path.Wildcard)
|
|
|
|
// g := path.New("foo", path.Wildcard, path.Wildcard)
|
|
|
|
// h := path.New(path.Wildcard, path.Wildcard, path.Wildcard)
|
|
|
|
//
|
|
|
|
// m.Set(a, 1)
|
|
|
|
// m.Set(b, 2)
|
|
|
|
// m.Set(c, 3)
|
|
|
|
// m.Set(d, 4)
|
|
|
|
// m.Set(e, 5)
|
|
|
|
// m.Set(f, 6)
|
|
|
|
// m.Set(g, 7)
|
|
|
|
// m.Set(h, 8)
|
|
|
|
//
|
|
|
|
// p := path.New("foo", "bar", "baz")
|
|
|
|
//
|
|
|
|
// m.Visit(p, fn)
|
|
|
|
//
|
|
|
|
// Result: fn(1), fn(2), fn(3), fn(4), fn(5), fn(6), fn(7) and fn(8)
|
|
|
|
func (m *Map) Visit(p key.Path, fn VisitorFunc) error {
|
2018-01-29 19:44:18 +00:00
|
|
|
for i, element := range p {
|
2018-09-05 15:36:14 +00:00
|
|
|
if m.wildcard != nil {
|
|
|
|
if err := m.wildcard.Visit(p[i+1:], fn); err != nil {
|
2018-01-29 19:44:18 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
next, ok := m.children[element]
|
2018-01-29 19:44:18 +00:00
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
m = next
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
if !m.ok {
|
2018-01-29 19:44:18 +00:00
|
|
|
return nil
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
return fn(m.val)
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
// VisitPrefixes calls a function fn for every value in the
|
|
|
|
// Map that is registered with a prefix of a path p.
|
|
|
|
//
|
|
|
|
// Example:
|
|
|
|
//
|
|
|
|
// a := path.New()
|
|
|
|
// b := path.New("foo")
|
|
|
|
// c := path.New("foo", "bar")
|
|
|
|
// d := path.New("foo", "baz")
|
|
|
|
// e := path.New(path.Wildcard, "bar")
|
|
|
|
//
|
|
|
|
// m.Set(a, 1)
|
|
|
|
// m.Set(b, 2)
|
|
|
|
// m.Set(c, 3)
|
|
|
|
// m.Set(d, 4)
|
|
|
|
// m.Set(e, 5)
|
|
|
|
//
|
|
|
|
// p := path.New("foo", "bar", "baz")
|
|
|
|
//
|
|
|
|
// m.VisitPrefixes(p, fn)
|
|
|
|
//
|
|
|
|
// Result: fn(1), fn(2), fn(3), fn(5)
|
|
|
|
func (m *Map) VisitPrefixes(p key.Path, fn VisitorFunc) error {
|
2018-01-29 19:44:18 +00:00
|
|
|
for i, element := range p {
|
2018-09-05 15:36:14 +00:00
|
|
|
if m.ok {
|
|
|
|
if err := fn(m.val); err != nil {
|
2018-01-29 19:44:18 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
if m.wildcard != nil {
|
|
|
|
if err := m.wildcard.VisitPrefixes(p[i+1:], fn); err != nil {
|
2018-01-29 19:44:18 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
next, ok := m.children[element]
|
2018-01-29 19:44:18 +00:00
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
m = next
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
if !m.ok {
|
2018-01-29 19:44:18 +00:00
|
|
|
return nil
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
return fn(m.val)
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
// VisitPrefixed calls fn for every value in the map that is
|
|
|
|
// registerd with a path that is prefixed by p. This method
|
|
|
|
// can be used to visit every registered path if p is the
|
|
|
|
// empty path (or root path) which prefixes all paths.
|
|
|
|
//
|
|
|
|
// Example:
|
|
|
|
//
|
|
|
|
// a := path.New("foo")
|
|
|
|
// b := path.New("foo", "bar")
|
|
|
|
// c := path.New("foo", "bar", "baz")
|
|
|
|
// d := path.New("foo", path.Wildcard)
|
|
|
|
//
|
|
|
|
// m.Set(a, 1)
|
|
|
|
// m.Set(b, 2)
|
|
|
|
// m.Set(c, 3)
|
|
|
|
// m.Set(d, 4)
|
|
|
|
//
|
|
|
|
// p := path.New("foo", "bar")
|
|
|
|
//
|
|
|
|
// m.VisitPrefixed(p, fn)
|
|
|
|
//
|
|
|
|
// Result: fn(2), fn(3), fn(4)
|
|
|
|
func (m *Map) VisitPrefixed(p key.Path, fn VisitorFunc) error {
|
|
|
|
for i, element := range p {
|
|
|
|
if m.wildcard != nil {
|
|
|
|
if err := m.wildcard.VisitPrefixed(p[i+1:], fn); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
next, ok := m.children[element]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
m = next
|
|
|
|
}
|
|
|
|
return m.visitSubtree(fn)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Map) visitSubtree(fn VisitorFunc) error {
|
|
|
|
if m.ok {
|
|
|
|
if err := fn(m.val); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if m.wildcard != nil {
|
|
|
|
if err := m.wildcard.visitSubtree(fn); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, next := range m.children {
|
|
|
|
if err := next.visitSubtree(fn); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns the value registered with an exact match of a
|
|
|
|
// path p. If there is no exact match for p, Get returns nil
|
|
|
|
// and false. If p has an exact match and it is set to true,
|
|
|
|
// Get returns nil and true.
|
|
|
|
//
|
|
|
|
// Example:
|
|
|
|
//
|
|
|
|
// m.Set(path.New("foo", "bar"), 1)
|
|
|
|
// m.Set(path.New("baz", "qux"), nil)
|
|
|
|
//
|
|
|
|
// a := m.Get(path.New("foo", "bar"))
|
|
|
|
// b := m.Get(path.New("foo", path.Wildcard))
|
|
|
|
// c, ok := m.Get(path.New("baz", "qux"))
|
|
|
|
//
|
|
|
|
// Result: a == 1, b == nil, c == nil and ok == true
|
|
|
|
func (m *Map) Get(p key.Path) (interface{}, bool) {
|
2018-01-29 19:44:18 +00:00
|
|
|
for _, element := range p {
|
|
|
|
if element.Equal(Wildcard) {
|
2018-09-05 15:36:14 +00:00
|
|
|
if m.wildcard == nil {
|
|
|
|
return nil, false
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
m = m.wildcard
|
2018-01-29 19:44:18 +00:00
|
|
|
continue
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
next, ok := m.children[element]
|
2018-01-29 19:44:18 +00:00
|
|
|
if !ok {
|
2018-09-05 15:36:14 +00:00
|
|
|
return nil, false
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
m = next
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
return m.val, m.ok
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
// Set registers a path p with a value. If the path was already
|
|
|
|
// registered with a value it returns true and false otherwise.
|
|
|
|
//
|
|
|
|
// Example:
|
|
|
|
//
|
|
|
|
// p := path.New("foo", "bar")
|
|
|
|
//
|
|
|
|
// a := m.Set(p, 0)
|
|
|
|
// b := m.Set(p, 1)
|
|
|
|
//
|
|
|
|
// v := m.Get(p)
|
|
|
|
//
|
|
|
|
// Result: a == false, b == true and v == 1
|
|
|
|
func (m *Map) Set(p key.Path, v interface{}) bool {
|
2018-01-29 19:44:18 +00:00
|
|
|
for _, element := range p {
|
|
|
|
if element.Equal(Wildcard) {
|
2018-09-05 15:36:14 +00:00
|
|
|
if m.wildcard == nil {
|
|
|
|
m.wildcard = &Map{}
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
m = m.wildcard
|
2018-01-29 19:44:18 +00:00
|
|
|
continue
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
if m.children == nil {
|
|
|
|
m.children = map[key.Key]*Map{}
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
next, ok := m.children[element]
|
2018-01-29 19:44:18 +00:00
|
|
|
if !ok {
|
2018-09-05 15:36:14 +00:00
|
|
|
next = &Map{}
|
|
|
|
m.children[element] = next
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
m = next
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
set := !m.ok
|
|
|
|
m.val, m.ok = v, true
|
|
|
|
return set
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
// Delete unregisters the value registered with a path. It
|
|
|
|
// returns true if a value was deleted and false otherwise.
|
|
|
|
//
|
|
|
|
// Example:
|
|
|
|
//
|
|
|
|
// p := path.New("foo", "bar")
|
|
|
|
//
|
|
|
|
// m.Set(p, 0)
|
|
|
|
//
|
|
|
|
// a := m.Delete(p)
|
|
|
|
// b := m.Delete(p)
|
|
|
|
//
|
|
|
|
// Result: a == true and b == false
|
|
|
|
func (m *Map) Delete(p key.Path) bool {
|
|
|
|
maps := make([]*Map, len(p)+1)
|
2018-01-29 19:44:18 +00:00
|
|
|
for i, element := range p {
|
2018-09-05 15:36:14 +00:00
|
|
|
maps[i] = m
|
2018-01-29 19:44:18 +00:00
|
|
|
if element.Equal(Wildcard) {
|
2018-09-05 15:36:14 +00:00
|
|
|
if m.wildcard == nil {
|
2018-01-29 19:44:18 +00:00
|
|
|
return false
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
m = m.wildcard
|
2018-01-29 19:44:18 +00:00
|
|
|
continue
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
next, ok := m.children[element]
|
2018-01-29 19:44:18 +00:00
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
m = next
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
deleted := m.ok
|
|
|
|
m.val, m.ok = nil, false
|
|
|
|
maps[len(p)] = m
|
2018-01-29 19:44:18 +00:00
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
// Remove any empty maps.
|
2018-01-29 19:44:18 +00:00
|
|
|
for i := len(p); i > 0; i-- {
|
2018-09-05 15:36:14 +00:00
|
|
|
m = maps[i]
|
|
|
|
if m.ok || m.wildcard != nil || len(m.children) > 0 {
|
2018-01-29 19:44:18 +00:00
|
|
|
break
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
parent := maps[i-1]
|
2018-01-29 19:44:18 +00:00
|
|
|
element := p[i-1]
|
|
|
|
if element.Equal(Wildcard) {
|
|
|
|
parent.wildcard = nil
|
|
|
|
} else {
|
|
|
|
delete(parent.children, element)
|
|
|
|
}
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
return deleted
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
func (m *Map) String() string {
|
2018-01-29 19:44:18 +00:00
|
|
|
var b bytes.Buffer
|
2018-09-05 15:36:14 +00:00
|
|
|
m.write(&b, "")
|
2018-01-29 19:44:18 +00:00
|
|
|
return b.String()
|
|
|
|
}
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
func (m *Map) write(b *bytes.Buffer, indent string) {
|
|
|
|
if m.ok {
|
2018-01-29 19:44:18 +00:00
|
|
|
b.WriteString(indent)
|
2018-09-05 15:36:14 +00:00
|
|
|
fmt.Fprintf(b, "Val: %v", m.val)
|
2018-01-29 19:44:18 +00:00
|
|
|
b.WriteString("\n")
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
if m.wildcard != nil {
|
2018-01-29 19:44:18 +00:00
|
|
|
b.WriteString(indent)
|
|
|
|
fmt.Fprintf(b, "Child %q:\n", Wildcard)
|
2018-09-05 15:36:14 +00:00
|
|
|
m.wildcard.write(b, indent+" ")
|
2018-01-29 19:44:18 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
children := make([]key.Key, 0, len(m.children))
|
|
|
|
for key := range m.children {
|
2018-01-29 19:44:18 +00:00
|
|
|
children = append(children, key)
|
|
|
|
}
|
|
|
|
sort.Slice(children, func(i, j int) bool {
|
|
|
|
return children[i].String() < children[j].String()
|
|
|
|
})
|
|
|
|
|
|
|
|
for _, key := range children {
|
2018-09-05 15:36:14 +00:00
|
|
|
child := m.children[key]
|
2018-01-29 19:44:18 +00:00
|
|
|
b.WriteString(indent)
|
|
|
|
fmt.Fprintf(b, "Child %q:\n", key.String())
|
|
|
|
child.write(b, indent+" ")
|
|
|
|
}
|
|
|
|
}
|