123 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			123 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
package templating
 | 
						|
 | 
						|
import (
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
// node is an item in a sorted k-ary tree of filter parts. Each child is sorted by its part value.
 | 
						|
// The special value of "*", is always sorted last.
 | 
						|
type node struct {
 | 
						|
	separator string
 | 
						|
	value     string
 | 
						|
	children  nodes
 | 
						|
	template  *Template
 | 
						|
}
 | 
						|
 | 
						|
// insert inserts the given string template into the tree.  The filter string is separated
 | 
						|
// on the template separator and each part is used as the path in the tree.
 | 
						|
func (n *node) insert(filter string, template *Template) {
 | 
						|
	n.separator = template.separator
 | 
						|
	n.recursiveInsert(strings.Split(filter, n.separator), template)
 | 
						|
}
 | 
						|
 | 
						|
// recursiveInsert does the actual recursive insertion
 | 
						|
func (n *node) recursiveInsert(values []string, template *Template) {
 | 
						|
	// Add the end, set the template
 | 
						|
	if len(values) == 0 {
 | 
						|
		n.template = template
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// See if the the current element already exists in the tree. If so, insert the
 | 
						|
	// into that sub-tree
 | 
						|
	for _, v := range n.children {
 | 
						|
		if v.value == values[0] {
 | 
						|
			v.recursiveInsert(values[1:], template)
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// New element, add it to the tree and sort the children
 | 
						|
	newNode := &node{value: values[0]}
 | 
						|
	n.children = append(n.children, newNode)
 | 
						|
	sort.Sort(&n.children)
 | 
						|
 | 
						|
	// Now insert the rest of the tree into the new element
 | 
						|
	newNode.recursiveInsert(values[1:], template)
 | 
						|
}
 | 
						|
 | 
						|
// search searches for a template matching the input string
 | 
						|
func (n *node) search(line string) *Template {
 | 
						|
	separator := n.separator
 | 
						|
	return n.recursiveSearch(strings.Split(line, separator))
 | 
						|
}
 | 
						|
 | 
						|
// recursiveSearch performs the actual recursive search
 | 
						|
func (n *node) recursiveSearch(lineParts []string) *Template {
 | 
						|
	// Nothing to search
 | 
						|
	if len(lineParts) == 0 || len(n.children) == 0 {
 | 
						|
		return n.template
 | 
						|
	}
 | 
						|
 | 
						|
	// If last element is a wildcard, don't include it in this search since it's sorted
 | 
						|
	// to the end but lexicographically it would not always be and sort.Search assumes
 | 
						|
	// the slice is sorted.
 | 
						|
	length := len(n.children)
 | 
						|
	if n.children[length-1].value == "*" {
 | 
						|
		length--
 | 
						|
	}
 | 
						|
 | 
						|
	// Find the index of child with an exact match
 | 
						|
	i := sort.Search(length, func(i int) bool {
 | 
						|
		return n.children[i].value >= lineParts[0]
 | 
						|
	})
 | 
						|
 | 
						|
	// Found an exact match, so search that child sub-tree
 | 
						|
	if i < len(n.children) && n.children[i].value == lineParts[0] {
 | 
						|
		return n.children[i].recursiveSearch(lineParts[1:])
 | 
						|
	}
 | 
						|
	// Not an exact match, see if we have a wildcard child to search
 | 
						|
	if n.children[len(n.children)-1].value == "*" {
 | 
						|
		return n.children[len(n.children)-1].recursiveSearch(lineParts[1:])
 | 
						|
	}
 | 
						|
	return n.template
 | 
						|
}
 | 
						|
 | 
						|
// nodes is simply an array of nodes implementing the sorting interface.
 | 
						|
type nodes []*node
 | 
						|
 | 
						|
// Less returns a boolean indicating whether the filter at position j
 | 
						|
// is less than the filter at position k. Filters are order by string
 | 
						|
// comparison of each component parts.  A wildcard value "*" is never
 | 
						|
// less than a non-wildcard value.
 | 
						|
//
 | 
						|
// For example, the filters:
 | 
						|
//             "*.*"
 | 
						|
//             "servers.*"
 | 
						|
//             "servers.localhost"
 | 
						|
//             "*.localhost"
 | 
						|
//
 | 
						|
// Would be sorted as:
 | 
						|
//             "servers.localhost"
 | 
						|
//             "servers.*"
 | 
						|
//             "*.localhost"
 | 
						|
//             "*.*"
 | 
						|
func (n *nodes) Less(j, k int) bool {
 | 
						|
	if (*n)[j].value == "*" && (*n)[k].value != "*" {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	if (*n)[j].value != "*" && (*n)[k].value == "*" {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	return (*n)[j].value < (*n)[k].value
 | 
						|
}
 | 
						|
 | 
						|
// Swap swaps two elements of the array
 | 
						|
func (n *nodes) Swap(i, j int) { (*n)[i], (*n)[j] = (*n)[j], (*n)[i] }
 | 
						|
 | 
						|
// Len returns the length of the array
 | 
						|
func (n *nodes) Len() int { return len(*n) }
 |