File: System\Data\Common\Utils\TreePrinter.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <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
    }
}