transdep/graph/relationNode.go

175 lines
5.7 KiB
Go
Raw Permalink Normal View History

2018-01-23 20:25:00 +00:00
package graph
import (
"crypto/sha256"
"encoding/json"
)
const (
// OR_REL is a constant used to designate the OR relationship in RelationshipNode instances
OR_REL = iota
// AND_REL is a constant used to designate the AND relationship in RelationshipNode instances
AND_REL
)
/* serializedRelationshipNode is a proxy struct used to serialize an RelationshipNode node into JSON.
The RelationshipNode struct is not directly used because the Go json module requires that attributes must be exported
for it to work, and RelationshipNode struct attributes have no other reason for being exported.
*/
type serializedRelationshipNode struct {
Comment string `json:"comment"`
Relation int `json:"rel"`
Children []interface{} `json:"elmts"`
}
// RelationshipNode instances represents intermediary nodes in the dependency graph. RelationshipNode are N-ary trees,
// not necessarily binary trees.
// Children of such a node are related following either an "and" or an "or" boolean expression.
type RelationshipNode struct {
comment string
relation int
parentNode Node
children []Node
}
/* NewRelationshipNode returns a new RelationshipNode after initializing it.
comment is a free-form string giving some indication as to why this node exists and what it represents w.r.t. the
dependency tree.
relation is either equal to AND_REL or OR_REL
*/
func NewRelationshipNode(comment string, relation int) *RelationshipNode {
if relation != AND_REL && relation != OR_REL {
panic("Contract violation: relation is not equal to AND_REL or OR_REL.")
}
g := new(RelationshipNode)
g.comment = comment
g.relation = relation
return g
}
// Implements json.Marshaler
func (rn *RelationshipNode) MarshalJSON() ([]byte, error) {
srn := new(serializedRelationshipNode)
srn.Comment = rn.comment
srn.Relation = rn.relation
for _, v := range rn.children {
srn.Children = append(srn.Children, v)
}
return json.Marshal(srn)
}
// Implements json.Unmarshaler
func (rn *RelationshipNode) UnmarshalJSON(b []byte) error {
// This function unserializes first a serializedRelationShip node then tries to use this object to initialize the
// receiver.
srn := new(serializedRelationshipNode)
err := json.Unmarshal(b, srn)
if err != nil {
return err
}
rn.comment = srn.Comment
rn.relation = srn.Relation
for _, chld := range srn.Children {
m := chld.(map[string]interface{})
rn.addChildrenFromMap(m)
}
return nil
}
/* addChildrenFromMap discovers from a map of interface{} the type of the object that was serialized as this map.
This is due to the fact that struct instances implementing an interface are uniformed as interface{} instances during
the serialization process and it is up to the unserializer to detect what's what.
Using the map key names, the object type is discovered. Ultimately, the object is initialized and added as a child of
the receiver.
*/
func (rn *RelationshipNode) addChildrenFromMap(m map[string]interface{}) {
if _, ok := m["target"]; ok {
rn.children = append(rn.children, NewAliasNode(m["target"].(string), m["source"].(string)))
} else if _, ok := m["domain"]; ok {
rn.children = append(rn.children, NewDomainNameNode(m["domain"].(string), m["dnssec"].(bool)))
} else if _, ok := m["ip"]; ok {
if _, ok := m["name"]; ok {
rn.children = append(rn.children, NewIPNodeWithName(m["ip"].(string), m["name"].(string), int(m["asn"].(float64))))
} else {
rn.children = append(rn.children, NewIPNode(m["ip"].(string), int(m["asn"].(float64))))
}
} else if _, ok := m["comment"]; ok {
// When there is a comment, this indicates a RelationshipNode => recursive call
chldGraph := new(RelationshipNode)
// Initialization of the child RelationshipNode cannot be done with initializeFromSerializedRelNode because the
// child node is also represented as a map!
chldGraph.initializeFromMap(m)
rn.children = append(rn.children, chldGraph)
} else if c, ok := m["type"] ; ok && c.(string) == "cycle" {
// Cycles are represented in JSON as an object containing a "type" key, and a "cycle" string value.
rn.children = append(rn.children, new(Cycle))
} else {
panic("BUG: invalid or unknown child type")
}
}
// initializeFromMap initializes the receiver using a map representing a RelationshipNode unserialized from JSON
func (rn *RelationshipNode) initializeFromMap(m map[string]interface{}) {
rn.comment = m["comment"].(string)
// float64 is used for type casting because JSON numbers are floats. We recast it as int because we know that values
// are only equal to AND_REL or OR_REL
rn.relation = int(m["rel"].(float64))
for _, chld := range m["elmts"].([]interface{}) {
m := chld.(map[string]interface{})
rn.addChildrenFromMap(m)
}
}
func (rn *RelationshipNode) deepcopy() Node {
cg := new(RelationshipNode)
cg.comment = rn.comment
cg.relation = rn.relation
cg.children = make([]Node, 0, len(rn.children))
cg.parentNode = rn.parentNode
for _, chld := range rn.children {
cg.children = append(cg.children, chld.deepcopy())
}
return cg
}
// AddChild adds a Node to the children of the receiver. This is the main function used for tree building
func (rn *RelationshipNode) AddChild(c Node) {
rn.children = append(rn.children, c)
}
func (rn *RelationshipNode) String() string {
jsonbtr, err := json.Marshal(rn)
if err != nil {
return ""
}
return string(jsonbtr)
}
func (rn *RelationshipNode) hash() [8]byte {
var ret [8]byte
h := sha256.Sum256([]byte(rn.String()))
copy(ret[:], h[:8])
return ret
}
func (rn *RelationshipNode) setParent(p Node) {
rn.parentNode = p
}
func (rn *RelationshipNode) parent() Node {
return rn.parentNode
}
func (rn *RelationshipNode) setParentNodes() {
for _, chld := range rn.children {
chld.setParent(rn)
if cg, ok := chld.(*RelationshipNode); ok {
cg.setParentNodes()
}
}
}