Wednesday, May 4, 2011

ObjectDumper extended

This should be functionally equivalent to Microsoft's version just my own refactorings to support extensibility: No behavior should be different.


using System;
using System.IO;
using System.Collections;
using System.Reflection;

namespace Utilities
{
/// 
/// From Microsoft's http://archive.msdn.microsoft.com/cs2008samples/
/// modified for extensibility
/// 
public class ObjectDumper
{

/// 
/// StaticWrite
/// 
/// public static void Write(object element)
{
Write(element, 0);
}
protected static TextWriter DefaultWriter=Console.Out;
/// 
/// Static Write
/// 
/// /// public static void Write(object element, int depth)
{
Write(element, depth, DefaultWriter);
}
/// 
/// Static Write
/// 
/// /// /// public static void Write(object element, int depth, TextWriter log)
{
var dumper = new ObjectDumper(depth) {_writer = log};
dumper.WriteObject(null, element);
}


private TextWriter _writer;
int _pos;
int _level;
readonly int _depth;

protected ObjectDumper(int depth)
{
_depth = depth;
}
protected ObjectDumper(int depth, TextWriter log):this(depth)
{
_writer = log;
}
/// 
/// MethodWrite
/// 
/// protected void WriteString(string s)
{
if (s != null)
{
_writer.Write(s);
_pos += s.Length;
}
}

protected virtual void WriteIndent()
{
for (int i = 0; i < _level; i++) _writer.Write("  ");
        }

        protected void WriteLine()
        {
            _writer.WriteLine();
            _pos = 0;
        }
        
        protected virtual void WriteTab()
        {
            WriteString("  ");
            while (_pos % 8 != 0) WriteString(" ");
        }
        protected  void DescendIfDepthAllows(Action doIfDescend)
        {
            if (_level < _depth)
            {
                _level++;
                doIfDescend();
                _level--;
               
            }
        }

        protected virtual  void WriteObject(string prefix, object element)
        {
            if (element == null || element is ValueType || element is string)
            {
                WriteIndent();
                WriteString(prefix);
                WriteValue(element);
                WriteLine();
            }
            else
            {
                var enumerableElement = element as IEnumerable;
                if (enumerableElement != null)
                {
                    WriteEnumerable(prefix, enumerableElement);
                }
                else
                {
                    WriteWithReflection(element, prefix);
                }
            }
        }

        protected virtual void WriteWithReflection(object element, string prefix)
        {
            MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
            WriteIndent();
            WriteString(prefix);
            bool propWritten = false;
            foreach (MemberInfo m in members)
            {
                var f = m as FieldInfo;
                var p = m as PropertyInfo;
                if (f != null || p != null)
                {
                    if (propWritten)
                    {
                        WriteTab();
                    }
                    else
                    {
                        propWritten = true;
                    }
                    WriteString(m.Name);
                    WriteString("=");
                    Type t = f != null ? f.FieldType : p.PropertyType;
                    
                    if (t.IsValueType || t == typeof(string))
                    {
                         WriteValue(GetValue(element, m, f, p)); 
                    }
                    else
                    {
                        WriteString(typeof (IEnumerable).IsAssignableFrom(t) ? "..." : "{ }");
                    }
                }
            }
            if (propWritten) WriteLine();
            WriteReflectionChildren(element, members);
        }
        protected virtual object GetValue(object element, MemberInfo m, FieldInfo f, PropertyInfo p)
        {
           return f != null ? f.GetValue(element) : p.GetValue(element, null);
        }
        

        protected void WriteReflectionChildren(object element, MemberInfo[] members)
        {
            if (_level < _depth)
            {
                foreach (MemberInfo m in members)
                {
                    var f = m as FieldInfo;
                    var p = m as PropertyInfo;
                    if (f != null || p != null)
                    {
                        Type t = f != null ? f.FieldType : p.PropertyType;
                        if (!(t.IsValueType || t == typeof(string)))
                        {
                            
                            object value =GetValue(element,m,f,p);
                            if (value != null)
                            {
                                _level++;
                                WriteObject(m.Name + ": ", value);
                                _level--;
                            }
                        }
                    }
                }
            }
        }

        protected virtual void WriteEnumerable(string prefix, IEnumerable enumerableElement)
        {
            foreach (object item in enumerableElement)
            {
                if (item is IEnumerable && !(item is string))
                {
                    WriteIndent();
                    WriteString(prefix);
                    WriteString("...");
                    WriteLine();
                    if (_level < _depth)
                    {
                        _level++;
                        WriteObject(prefix, item);
                        _level--;
                    }
                }
                else
                {
                    WriteObject(prefix, item);
                }
            }
        }
        
        protected virtual void WriteValue(object o)
        {
            if (o == null)
            {
                WriteString("null");
            }
            else if (o is DateTime)
            {
                WriteString(((DateTime)o).ToShortDateString());
            }
            else if (o is ValueType || o is string)
            {
                WriteString(o.ToString());
            }
            else if (o is IEnumerable)
            {
                WriteString("...");
            }
            else
            {
                WriteString("{ }");
            }
        }

       
    }
}

Note that this code currently throws an exception if the object passed into it is a Type, for example:

var type=typeof(string);
ObjectDumper.Write(type);


Throws: TargetInvocationException: Exception has been thrown by the target of an invocation. So... the refactoring commenced and the derived class that handles types, and also KeyValuePairs





using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace TechnicalDebtTaskLib.Utilities
{
    public class ObjectDumperExtended : ObjectDumper
    {

        public new static void Write(object element)
        {
            Write(element, 0);
        }

        public new static void Write(object element, int depth)
        {
            Write(element, depth, DefaultWriter);
        }

        public new static void Write(object element, int depth, TextWriter log)
        {
            var dumper = new ObjectDumperExtended(depth,log) ;
            dumper.WriteObject(null, element);
        }

        public static string ToDumpString(object element, int depth = 0)
        {
            using (var textWriter = new StringWriter())
            {
                Write(element, depth, textWriter);
                return textWriter.ToString();
            }
        }
        protected override void WriteObject(string prefix, object element)
        {
            if (element != null && element is Type )
            {
                WriteType(prefix, element as Type);
            }
            else
                base.WriteObject(prefix, element);
        }

        private void WriteType(string prefix, Type type)
        {
            switch (_typeStrategy)
            {
                case TypeStrategy.Custom:
                    WriteString(_writeTypeStrategy(prefix,type));
                    break;
                case TypeStrategy.Extended:
                    base.WriteObject(prefix,type);
                    break;
                default:
                    WriteString(prefix + type.ToString());
                    break;
            }
          
        }

        protected override object GetValue(object element, MemberInfo m, FieldInfo f, PropertyInfo p)
        {
            var blackList = new[] { "DeclaringMethod", "GenericParameterAttributes", "GenericParameterPosition" };
            if (element is Type && blackList.Contains(m.Name))
            {
                return "[UnsafeToReflect]";
            }
            return base.GetValue(element, m, f, p);
        }

        private static TypeStrategy _typeStrategy = TypeStrategy.Basic;
        
        public enum TypeStrategy
        {
            Basic=1,
            Custom=2,
            Extended=3
        }

        private static Func<string, Type, string> _writeTypeStrategy;
        public static void SetTypeStrategy(Func<string,Type ,string> formatter)
        {
            if (formatter == null)
                _typeStrategy = TypeStrategy.Basic;
            else
            {
                _writeTypeStrategy = formatter;
                _typeStrategy = TypeStrategy.Custom;
            }
        }

      public static void SetTypeStrategyBasic()
      {
          _typeStrategy = TypeStrategy.Basic;
          _writeTypeStrategy = null;

      }
        public static void SetTypeStrategyExtended()
        {
            _typeStrategy = TypeStrategy.Extended;
            _writeTypeStrategy = null;
        }

        public static Func<string> WriteIndentStrategy { get; set; }
        protected override void WriteIndent()
        {
            if (WriteIndentStrategy != null)
                WriteString(WriteIndentStrategy());
            else
                base.WriteIndent();
        }

        public static Func<string> WriteTabStrategy { get; set; }
        protected override void WriteTab()
        {
            if (WriteTabStrategy != null)
                WriteString(WriteTabStrategy());
            else
                base.WriteTab();
        }

        private ObjectDumperExtended(int depth,TextWriter log) : base(depth,log){}


        protected override void WriteValue(object o)
        {
            if (o == null)
            {
                WriteString("null");
            }
            else if (o is DateTime)
            {
                WriteString(((DateTime)o).ToShortDateString());
            }
            else if (o.GetType().IsGenericType)
            {
                var baseType = o.GetType().GetGenericTypeDefinition();
                if (baseType == typeof(KeyValuePair<,>))
                {
                    var argTypes = baseType.GetGenericArguments();
                    WriteKeyValuePair(o, argTypes[0], argTypes[1]);
                }

            }
            else if (o is ValueType || o is string)
            {
                WriteString(o.ToString());
            }
            else if (o is IEnumerable)
            {
                WriteString("...");
            }
            else
            {
                WriteString("{ }");
            }
        }

        private void WriteKeyValuePair(object o, Type keyType, Type valueType)
        {
            var key = o.GetType().GetProperty("Key").GetValue(o, null);
            var value = o.GetType().GetProperty("Value").GetValue(o, null);
            WriteObject(keyType.Name + ":", key);
            WriteIndent();
            WriteObject(valueType.Name + ":", value);
        }

    }
}

No comments:

Post a Comment