#region Copyright
// Diagnostic Explorer, a .Net diagnostic toolset
// Copyright (C) 2010 Cameron Elliot
//
// This file is part of Diagnostic Explorer.
//
// Diagnostic Explorer is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Diagnostic Explorer is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Diagnostic Explorer. If not, see <http://www.gnu.org/licenses/>.
//
// http://diagexplorer.sourceforge.net/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace DiagnosticExplorer
{
internal class PropertyGetter
{
public const int MaxConcatItems = 10;
protected PropertyGetter()
{
}
public PropertyGetter(PropertyInfo prop)
{
GetFunc = PropertyToFunction(prop);
Name = prop.Name;
CategoryAttribute catAttr = GetAttribute<CategoryAttribute>(prop);
if (catAttr != null)
Category = catAttr.Category;
DescriptionAttribute descAttr = GetAttribute<DescriptionAttribute>(prop);
if (descAttr != null)
Description = descAttr.Description;
PropertyAttribute propAttr = GetAttribute<PropertyAttribute>(prop);
if (propAttr != null)
{
Name = propAttr.Name ?? Name;
Category = propAttr.Category ?? Category;
Description = propAttr.Description ?? Description;
FormatString = propAttr.FormatString ?? GetDefaultFormatString(prop.PropertyType);
}
}
protected Func<object, object> PropertyToFunction(PropertyInfo prop)
{
if (prop == null)
return null;
//return obj => prop.GetValue(obj, null);
//This method takes 2/3 time of prop.GetValue
ParameterExpression objParam = Expression.Parameter(typeof(object), "obj");
UnaryExpression objToType = Expression.Convert(objParam, prop.DeclaringType);
Expression propExp = Expression.Property(objToType, prop);
Expression resultToObj = Expression.Convert(propExp, typeof (object));
return (Func<object, object>)Expression.Lambda(resultToObj, objParam).Compile();
}
protected Func<object, object> GetFunc { get; set; }
protected static string MaxLengthString(string s, int maxLength)
{
if (s == null) return s;
if (s.Length <= maxLength) return s;
return s.Substring(0, maxLength);
}
public virtual IEnumerable<Property> GetProperties(object obj)
{
Property p = new Property();
p.Category = Category;
p.Name = Name;
p.Description = Description;
p.Value = MaxLengthString(GetValue(obj), 8092);
yield return p;
}
private string GetDefaultFormatString(Type type)
{
if (type == typeof(float))
return "{0:N2}";
if (type == typeof(double))
return "{0:N2}";
if (type == typeof(decimal))
return "{0:N2}";
if (type == typeof(DateTime) || type == typeof(DateTime?))
return "{0:d MMM yyyy H:mm:ss}";
return null;
}
private static T GetAttribute<T>(PropertyInfo info) where T : Attribute
{
object[] attr = info.GetCustomAttributes(typeof(T), false);
return attr.Length == 0 ? null : (T)attr[0];
}
public string Name { get; protected set; }
public string Description { get; protected set; }
protected string FormatString { get; private set; }
public string Category { get; private set; }
public string GetValue(object obj)
{
return GetValue(obj, GetFunc);
}
protected string FormatEnumerable(IEnumerable col, string separator, int maxItems)
{
IEnumerable<object> asObject = col.Cast<object>();
int count = asObject.Count();
if (count == 0)
return "0 items";
List<string> values = new List<string>();
if (maxItems <= 0)
maxItems = MaxConcatItems;
int remaining = count - maxItems;
foreach (object o in asObject.Take(maxItems))
values.Add(FormatValue(o));
if (remaining > 0)
values.Add(string.Format("... ({0} more item{1})", remaining, remaining == 1 ? "" : "s"));
string pre = string.Format("{0} item{1}: ", count, count == 1 ? "" : "s");
return pre + string.Join(separator, values.ToArray());
}
public string GetValue(object obj, Func<object, object> propInfo)
{
try
{
object val = propInfo(obj);
if (val == null)
return null;
return FormatValue(val);
}
catch (Exception ex)
{
return string.Format("<{0}>", ex.Message);
}
}
protected string FormatValue(object val)
{
if (val is TimeSpan)
return FormatTimeSpan((TimeSpan)val);
if (val is string)
return (string)val;
if (val is IEnumerable)
return FormatEnumerable((IEnumerable)val, Environment.NewLine, MaxConcatItems);
if (FormatString != null)
return string.Format(FormatString, val);
return val.ToString();
}
protected static string FormatTimeSpan(TimeSpan span)
{
string sign= span < TimeSpan.Zero ? "-" : "";
string format = "{0}{2:D2}:{3:D2}:{4:D2}";
if (span.Days != 0)
format = "{0}{1}.{2:D2}:{3:D2}:{4:D2}";
if (Math.Abs(span.TotalSeconds) < 1)
format += ".{5:D2}";
return string.Format(format, sign,
Math.Abs(span.Days), Math.Abs(span.Hours),
Math.Abs(span.Minutes), Math.Abs(span.Seconds),
Math.Abs(span.Milliseconds));
}
}
}