tool/TeamCity.Docker/Generic/Graph.cs (223 lines of code) (raw):

namespace TeamCity.Docker.Generic { using System; using System.Collections.Generic; using System.Linq; using System.Text; using IoC; internal class Graph<TNode, TLink> : IGraph<TNode, TLink> { [NotNull] private readonly IEqualityComparer<TNode> _nodeComparer; [NotNull] private readonly IEqualityComparer<TLink> _linkComparer; [NotNull] private readonly HashSet<INode<TNode>> _nodes; [NotNull] private readonly HashSet<ILink<TNode, TLink>> _links; public Graph() :this(EqualityComparer<TNode>.Default, EqualityComparer<TLink>.Default) { } // ReSharper disable once MemberCanBePrivate.Global public Graph( [NotNull] IEqualityComparer<TNode> nodeComparer, [NotNull] IEqualityComparer<TLink> linkComparer) { _nodeComparer = nodeComparer ?? throw new ArgumentNullException(nameof(nodeComparer)); _linkComparer = linkComparer ?? throw new ArgumentNullException(nameof(linkComparer)); _nodes = new HashSet<INode<TNode>>(); _links = new HashSet<ILink<TNode, TLink>>(); } public IEnumerable<INode<TNode>> Nodes => _nodes; public int NodesCount => _nodes.Count; public IEnumerable<ILink<TNode, TLink>> Links => _links; public int LinksCount => _links.Count; public bool TryAddNode(TNode value, out INode<TNode> node) { if (value == null) throw new ArgumentNullException(nameof(value)); var newNode = new Node(value, _nodeComparer); return TryAddNode(newNode, out node); } public bool TryRemoveNode(INode<TNode> node) { if (node == null) throw new ArgumentNullException(nameof(node)); return _nodes.Remove(node); } public bool TryAddLink(INode<TNode> from, TLink value, INode<TNode> to, out ILink<TNode, TLink> link) { if (from == null) throw new ArgumentNullException(nameof(from)); if (value == null) throw new ArgumentNullException(nameof(value)); if (to == null) throw new ArgumentNullException(nameof(to)); TryAddNode(from, out from); TryAddNode(to, out to); var newLink = link = new Link(from, value, to, _linkComparer); var success = _links.Add(newLink); if (!success) { link = _links.Single(i => i.Equals(newLink)); } return success; } public bool TryRemoveLink(ILink<TNode, TLink> link) { if (link == null) throw new ArgumentNullException(nameof(link)); return _links.Remove(link); } public IGraph<TNode, TLink> Copy(Predicate<INode<TNode>> filter) { var clone = new Graph<TNode, TLink>(_nodeComparer, _linkComparer); var newNodes = Nodes .Where(node => filter(node)) .Select(node => { clone.TryAddNode(node.Value, out var newNode); return new { node, newNode}; }) .ToDictionary(i => i.node, i => i.newNode); foreach (var link in Links) { if (newNodes.TryGetValue(link.From, out _) && newNodes.TryGetValue(link.To, out _)) { clone.TryAddLink(newNodes[link.From], link.Value, newNodes[link.To], out var _); } } return clone; } public override string ToString() { var dict = new Dictionary<INode<TNode>, int>(); var sb = new StringBuilder(); sb.AppendLine("digraph g {"); foreach (var node in _nodes) { sb.AppendLine($"{GetId(dict, node)} [label=\"{node.Value}\"];"); } foreach (var link in _links) { sb.AppendLine($"{GetId(dict, link.From)} -> {GetId(dict, link.To)};"); } sb.AppendLine("}"); return sb.ToString(); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } if (obj.GetType() != GetType()) { return false; } var other = (Graph<TNode, TLink>) obj; return _nodes.SetEquals(other._nodes) && _links.SetEquals(other._links); } public override int GetHashCode() { unchecked { return _nodes.Sum(node => node.GetHashCode()) + _links.Sum(link => link.GetHashCode()); } } private bool TryAddNode(INode<TNode> newNode, out INode<TNode> node) { var success = _nodes.Add(newNode); node = success ? newNode : _nodes.Single(i => i.Equals(newNode)); return success; } private static int GetId(IDictionary<INode<TNode>, int> dictionary, INode<TNode> node) { if(dictionary.TryGetValue(node, out var id)) { return id; } id = dictionary.Any() ? dictionary.Values.Max() + 1 : 0; dictionary.Add(node, id); return id; } private class Node: INode<TNode> { [NotNull] private readonly IEqualityComparer<TNode> _nodeComparer; public Node( TNode value, [NotNull] IEqualityComparer<TNode> nodeComparer) { if (value == null) { throw new ArgumentNullException(nameof(value)); } _nodeComparer = nodeComparer ?? throw new ArgumentNullException(nameof(nodeComparer)); Value = value; } public TNode Value { get; } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } if (obj.GetType() != GetType()) { return false; } var other = (Node) obj; return _nodeComparer.Equals(Value, other.Value); } public override int GetHashCode() => _nodeComparer.GetHashCode(Value); public override string ToString() => Value.ToString(); } private class Link: ILink<TNode, TLink> { private readonly IEqualityComparer<TLink> _linkComparer; public Link( [NotNull] INode<TNode> from, [NotNull] TLink value, [NotNull] INode<TNode> to, [NotNull] IEqualityComparer<TLink> linkComparer) { if (value == null) { throw new ArgumentNullException(nameof(value)); } _linkComparer = linkComparer ?? throw new ArgumentNullException(nameof(linkComparer)); From = from ?? throw new ArgumentNullException(nameof(from)); To = to ?? throw new ArgumentNullException(nameof(to)); Value = value; } public INode<TNode> From { get; } public TLink Value { get; } public INode<TNode> To { get; } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } if (obj.GetType() != GetType()) { return false; } var other = (Link) obj; return From.Equals(other.From) && _linkComparer.Equals(Value, other.Value) && To.Equals(other.To); } public override int GetHashCode() { unchecked { var hashCode = From.GetHashCode(); hashCode = (hashCode * 397) ^ _linkComparer.GetHashCode(Value); hashCode = (hashCode * 397) ^ To.GetHashCode(); return hashCode; } } public override string ToString() => $"{From} -- {Value} --> {To}"; } } }