private static void AppendGroupNode(DotStringBuilder result, string rank, string cluster)
 {
     result.StartGroup()
         .AppendConfig("rank", rank)
         .AppendNode(cluster, "", "style", "invis", "shape", "none", "fixedsize", "true", "width", "0", "height", "0")
         .EndGroup();
 }
        private void AddDependenciesBetweenGroups(DotStringBuilder result)
        {
            var usedGroups = new HashSet<string>(graph.Vertices.Where(v => v.GroupElement != null)
                .Select(g => g.GroupElement.Name));

            if (usedGroups.Count < 2)
                return;

            var usedDeps = architecture.Edges.Where(e => usedGroups.Contains(e.Source) && usedGroups.Contains(e.Target))
                .ToList();

            if (!usedDeps.Any())
                return;

            IDictionary<string, int> components;
            int numConnectedComponents = architecture.StronglyConnectedComponents(out components);
            if (numConnectedComponents < 2)
                return;

            //var circularDeps = components.Select(c => new { Group = c.Key, Index = c.Value })
            //	.Where(e => usedGroups.Contains(e.Group))
            //	.GroupBy(c => c.Index)
            //	.Where(g => g.Count() > 1);

            //foreach (var circ in circularDeps)
            //{
            //	var nodes = circ.ToList();
            //	nodes.Add(nodes.First());

            //	result.Append(prefix)
            //		.Append("{ ")
            //		.Append(string.Join(" -> ", nodes.Select(e => groupInfos[e.Group].TopNode)))
            //		.Append(" [weight=1000];")
            //		.Append("}\n");

            //	result.Append(prefix)
            //		.Append("{ ")
            //		.Append(string.Join(" -> ", nodes.Select(e => groupInfos[e.Group].BottomNode)))
            //		.Append(" [weight=1000];")
            //		.Append("}\n");
            //}

            foreach (var dep in usedDeps)
            {
                if (components[dep.Source] == components[dep.Target])
                    continue;

                result.AppendEdge(groupInfos[dep.Source].BottomNode, groupInfos[dep.Target].TopNode, "style", "invis", "weight", "1000");
            }
        }
        public void Output(ArchitectureGraph architecture, List<OutputEntry> warnings)
        {
            architecture.Vertices.ForEach((proj, i) => ids.Add(proj, i + 1));

            var result = new DotStringBuilder("Architecture");

            architecture.Vertices.ForEach(v => result.AppendNode(v, v, "shape", "box"));

            result.AppendSpace();

            var deps = new HashSet<GroupDependency>(architecture.Edges);
            var inversible = new HashSet<GroupDependency>(deps.Where(d => deps.Contains(new GroupDependency(d.Target, d.Source))));

            architecture.Edges.ForEach(d => result.AppendEdge(d.Source, d.Target, "style", GetStyle(d), "color", GetColor(d, inversible)));

            File.WriteAllText(file, result.ToString());
        }
        //private DependencyGraph Sample(DependencyGraph aGraph, List<OutputEntry> aWarnings, int percent)
        //{
        //    var rand = new Random();
        //    var selected = new HashSet<Library>(aWarnings.SelectMany(w => w.Projects)
        //        .Concat(aGraph.Vertices.Where(v => rand.Next(100) < percent)));
        //    var result = new DependencyGraph();
        //    result.AddVertexRange(selected);
        //    result.AddEdgeRange(aGraph.Edges.Where(e => selected.Contains(e.Source) && selected.Contains(e.Target)));
        //    return result;
        //}
        private string GenerateDot()
        {
            var result = new DotStringBuilder("Dependencies");

            result.AppendConfig("concentrate", "true");

            int clusterIndex = 1;

            foreach (var group in graph.Vertices.Where(a => a.GroupElement != null)
                .GroupBy(a => a.GroupElement.Name))
            {
                var groupName = @group.Key;
                var clusterName = "cluster" + clusterIndex++;

                var groupInfo = new GroupInfo(clusterName + "_top", clusterName + "_bottom");
                groupInfos.Add(groupName, groupInfo);

                result.StartSubgraph(clusterName);

                result.AppendConfig("label", groupName);
                result.AppendConfig("color", "lightgray");
                result.AppendConfig("style", "filled");
                result.AppendConfig("fontsize", "20");

                result.AppendSpace();

                AppendGroupNode(result, "min", groupInfo.TopNode);

                result.AppendSpace();

                var projs = new HashSet<Library>(group);

                projs.ForEach(a => AppendProject(result, a));

                result.AppendSpace();

                graph.Edges.Where(e => projs.Contains(e.Source) && projs.Contains(e.Target))
                    .ForEach(d => AppendDependency(result, d));

                result.AppendSpace();

                AppendGroupNode(result, "max", groupInfo.BottomNode);

                result.EndSubgraph();

                result.AppendSpace();
            }

            graph.Vertices.Where(a => a.GroupElement == null)
                .ForEach(a => AppendProject(result, a));

            result.AppendSpace();

            graph.Edges.Where(e => AreFromDifferentGroups(e.Source, e.Target))
                .ForEach(d => AppendDependency(result, d));

            result.AppendSpace();

            AddDependenciesBetweenGroups(result);

            return result.ToString();
        }
 private void AppendProject(DotStringBuilder result, Library library)
 {
     result.AppendNode(library, library.Name, "shape", library is Project ? "box" : "ellipse", "color",
         GetColor(warnings.Where(w => w.Projects.Contains(library))));
 }
        private void AppendDependency(DotStringBuilder result, Dependency dep)
        {
            var invert = wrongDependencies.Contains(dep);

            result.AppendEdge(invert ? dep.Target : dep.Source, invert ? dep.Source : dep.Target, //
                "style", dep.Type == Dependency.Types.LibraryReference ? "dashed" : null, //
                "color", GetColor(warnings.Where(w => w.Dependencies.Contains(dep))), //
                "dir", invert ? "back" : null);
        }