1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
|
//---------------------------------------------------------------------
// <copyright file="TreePrinter.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
//---------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
namespace System.Data.Common.Utils
{
/// <summary>
/// Represents a node in a hierarchical collection of information strings.
/// Intended as a common way mechanism to represent tree structures for debugging (using the TreePrinter class).
/// A node consists of a string (represented as a StringBuilder), its collection of child nodes, and an optional Tag value.
/// </summary>
internal class TreeNode
{
private StringBuilder _text;
private List<TreeNode> _children = new List<TreeNode>();
private int _position;
// Default constructor
internal TreeNode()
{
_text = new StringBuilder();
}
/// <summary>
/// Constructs a new TreeNode with the specified text, tag value and child nodes
/// </summary>
/// <param name="text">The initial value of the new node's text</param>
/// <param name="children">An optional list of initial child nodes</param>
internal TreeNode(string text, params TreeNode[] children)
{
if (string.IsNullOrEmpty(text))
{
_text = new StringBuilder();
}
else
{
_text = new StringBuilder(text);
}
if (children != null)
{
_children.AddRange(children);
}
}
// IEnumerable convenience constructors
internal TreeNode(string text, List<TreeNode> children)
: this(text)
{
if (children != null)
{
_children.AddRange(children);
}
}
// 'public' properties
/// <summary>
/// The current text of this node.
/// </summary>
internal StringBuilder Text { get { return _text; } }
/// <summary>
/// The collection of child nodes for this node, which may be empty.
/// </summary>
internal IList<TreeNode> Children { get { return _children; } }
// Used only by the TreePrinter when generating the output string
internal int Position { get { return _position; } set { _position = value; } }
}
/// <summary>
/// Generates a formatted string from a hierarchy of tree nodes. Derived types may override
/// the PreProcess, Before/AfterAppend, Print, PrintNode and PrintChildren methods to add
/// specific functionality at particular points in process of building the string.
/// </summary>
internal abstract class TreePrinter
{
#region Private Instance Members
private List<TreeNode> _scopes = new List<TreeNode>();
private bool _showLines = true;
private char _horizontals = '_';
private char _verticals = '|';
#endregion
#region 'Public' API
/// <summary>
/// Entry point method for the TreePrinter
/// </summary>
/// <param name="node">The TreeNode instance that is the root of the tree to be printed</param>
/// <returns>A string representation of the specified tree</returns>
internal virtual string Print(TreeNode node)
{
this.PreProcess(node);
StringBuilder text = new StringBuilder();
PrintNode(text, node);
return text.ToString();
}
#endregion
#region 'Protected' API
// 'protected' constructor
internal TreePrinter() { }
// 'protected' API that may be overriden to customize printing
/// <summary>
/// Called once on the root of the tree before printing begins
/// </summary>
/// <param name="node">The TreeNode that is the root of the tree</param>
internal virtual void PreProcess(TreeNode node) { }
/// <summary>
/// Called once for every node after indentation, connecting lines and the node's text value
/// have been added to the output but before the line suffix (if any) has been added.
/// </summary>
/// <param name="node">The current node</param>
/// <param name="text">The StringBuilder into which the tree is being printed</param>
internal virtual void AfterAppend(TreeNode node, StringBuilder text) { }
/// <summary>
/// Called once for every node immediately after the line prefix (if any) and appropriate
/// indentation and connecting lines have been added to the output but before the node's
/// text value has been added.
/// </summary>
/// <param name="node">The current node</param>
/// <param name="text">The StringBuilder into which the tree is being printed</param>
internal virtual void BeforeAppend(TreeNode node, StringBuilder text) { }
/// <summary>
/// The recursive step of the printing process, called once for each TreeNode in the tree
/// </summary>
/// <param name="text">The StringBuilder into which the tree is being printed</param>
/// <param name="node">The current node that should be printed to the StringBuilder</param>
internal virtual void PrintNode(StringBuilder text, TreeNode node)
{
IndentLine(text);
this.BeforeAppend(node, text);
text.Append(node.Text.ToString());
this.AfterAppend(node, text);
PrintChildren(text, node);
}
/// <summary>
/// Called to recursively visit the child nodes of the current TreeNode.
/// </summary>
/// <param name="text">The StringBuilder into which the tree is being printed</param>
/// <param name="node">The current node</param>
internal virtual void PrintChildren(StringBuilder text, TreeNode node)
{
_scopes.Add(node);
node.Position = 0;
foreach (TreeNode childNode in node.Children)
{
text.AppendLine();
node.Position++;
PrintNode(text, childNode);
}
_scopes.RemoveAt(_scopes.Count - 1);
}
#endregion
#region Private Implementation
private void IndentLine(StringBuilder text)
{
int idx = 0;
for (int scopeIdx = 0; scopeIdx < _scopes.Count; scopeIdx++)
{
TreeNode parentScope = _scopes[scopeIdx];
if (!_showLines || (parentScope.Position == parentScope.Children.Count && scopeIdx != _scopes.Count - 1))
{
text.Append(' ');
}
else
{
text.Append(_verticals);
}
idx++;
if (_scopes.Count == idx && _showLines)
{
text.Append(_horizontals);
}
else
{
text.Append(' ');
}
}
}
#endregion
}
}
|