123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- /*
- * EliasDB
- *
- * Copyright 2016 Matthias Ladkau. All rights reserved.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
- package data
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "sort"
- "strconv"
- )
- /*
- Node models nodes in the graph
- */
- type Node interface {
- /*
- Key returns a potentially non human-readable unique key for this node.
- */
- Key() string
- /*
- Name returns a human-readable name for this node.
- */
- Name() string
- /*
- Kind returns a human-readable kind for this node.
- */
- Kind() string
- /*
- Data returns the node data of this node.
- */
- Data() map[string]interface{}
- /*
- Attr returns an attribute of this node.
- */
- Attr(attr string) interface{}
- /*
- SetAttr sets an attribute of this node. Setting a nil
- value removes the attribute.
- */
- SetAttr(attr string, val interface{})
- /*
- IndexMap returns a representation of this node as a string map which
- can be used to provide a full-text search.
- */
- IndexMap() map[string]string
- /*
- String returns a string representation of this node.
- */
- String() string
- }
- /*
- NodeKey is the key attribute for a node
- */
- const NodeKey = "key"
- /*
- NodeName is the name attribute for a node
- */
- const NodeName = "name"
- /*
- NodeKind is the kind attribute for a node
- */
- const NodeKind = "kind"
- /*
- CopyNode returns a shallow copy of a given node.
- */
- func CopyNode(node Node) Node {
- ret := NewGraphNode()
- for k, v := range node.Data() {
- ret.SetAttr(k, v)
- }
- return ret
- }
- /*
- graphNode data structure.
- */
- type graphNode struct {
- data map[string]interface{} // Data which is held by this node
- }
- /*
- NewGraphNode creates a new Node instance.
- */
- func NewGraphNode() Node {
- return &graphNode{make(map[string]interface{})}
- }
- /*
- NewGraphNodeFromMap creates a new Node instance.
- */
- func NewGraphNodeFromMap(data map[string]interface{}) Node {
- return &graphNode{data}
- }
- /*
- Key returns a potentially non human-readable unique key for this node.
- */
- func (gn *graphNode) Key() string {
- return gn.stringAttr(NodeKey)
- }
- /*
- Kind returns a human-readable kind for this node.
- */
- func (gn *graphNode) Kind() string {
- return gn.stringAttr(NodeKind)
- }
- /*
- Data returns the node data of this node.
- */
- func (gn *graphNode) Data() map[string]interface{} {
- return gn.data
- }
- /*
- Name returns a human-readable name for this node.
- */
- func (gn *graphNode) Name() string {
- return gn.stringAttr(NodeName)
- }
- /*
- Attr returns an attribute of this node.
- */
- func (gn *graphNode) Attr(attr string) interface{} {
- val, _ := gn.data[attr]
- return val
- }
- /*
- SetAttr sets an attribute of this node. Setting a nil
- value removes the attribute.
- */
- func (gn *graphNode) SetAttr(attr string, val interface{}) {
- if val != nil {
- gn.data[attr] = val
- } else {
- delete(gn.data, attr)
- }
- }
- /*
- Return the value of an attribute as a string. Or an
- empty string if it can't be represented as a string.
- */
- func (gn *graphNode) stringAttr(attr string) string {
- val, found := gn.data[attr]
- if st, ok := val.(string); found && ok {
- return st
- } else if st, ok := val.(fmt.Stringer); found && ok {
- return st.String()
- } else if found {
- return fmt.Sprintf("%v", val)
- }
- return ""
- }
- /*
- IndexMap returns a representation of this node as a string map which
- can be used to provide a full-text search.
- */
- func (gn *graphNode) IndexMap() map[string]string {
- return createIndexMap(gn, func(attr string) bool {
- return attr == NodeKey || attr == NodeKind
- })
- }
- /*
- createIndexMap creates a representation of a node as a string map. A filter
- function can be specified to filters out specific attributes.
- */
- func createIndexMap(gn *graphNode, attFilter func(attr string) bool) map[string]string {
- var addMap func(prefix string, data map[string]interface{})
- ret := make(map[string]string)
- addMap = func(prefix string, data map[string]interface{}) {
- for key, val := range data {
- attr := prefix + key
- // Ignore attributes which are uninteresting for a full-text search
- if attFilter(attr) {
- continue
- }
- // Detect nested structures and recurse into them
- if valmap, ok := val.(map[string]interface{}); ok {
- addMap(prefix+key+".", valmap)
- }
- // See the type of val and print it accordingly - ignore byte slices
- if st, ok := val.(string); ok {
- // Value is actually a string - no change needed
- ret[attr] = st
- } else if st, ok := val.(fmt.Stringer); ok {
- // Value has a proper string representation - use that
- ret[attr] = st.String()
- } else if _, ok := val.([]byte); !ok {
- // For all other cases (except ignored byte slices) try first a
- // JSON representation
- jsonBytes, err := json.Marshal(val)
- jsonString := string(jsonBytes)
- if err == nil && jsonString != "{}" {
- ret[attr] = string(jsonString)
- } else {
- // Otherwise do best effort printing
- ret[attr] = fmt.Sprintf("%v", val)
- }
- }
- }
- }
- addMap("", gn.data)
- return ret
- }
- /*
- String returns a string representation of this node.
- */
- func (gn *graphNode) String() string {
- return dataToString("GraphNode", gn)
- }
- /*
- dataToString returns a string representation of a data item.
- */
- func dataToString(dataType string, gn *graphNode) string {
- var buf bytes.Buffer
- attrlist := make([]string, 0, len(gn.data))
- maxlen := 0
- for attr := range gn.data {
- attrlist = append(attrlist, attr)
- if alen := len(attr); alen > maxlen {
- maxlen = alen
- }
- }
- sort.StringSlice(attrlist).Sort()
- buf.WriteString(dataType + ":\n")
- buf.WriteString(fmt.Sprintf(" %"+
- strconv.Itoa(maxlen)+"v : %v\n", "key", gn.Key()))
- buf.WriteString(fmt.Sprintf(" %"+
- strconv.Itoa(maxlen)+"v : %v\n", "kind", gn.Kind()))
- for _, attr := range attrlist {
- if attr == NodeKey || attr == NodeKind {
- continue
- }
- buf.WriteString(fmt.Sprintf(" %"+
- strconv.Itoa(maxlen)+"v : %v\n", attr, gn.data[attr]))
- }
- return buf.String()
- }
|