| 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 dataimport (	"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 nilvalue 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 anempty 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 whichcan 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 filterfunction 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()}
 |