Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Чечулин Антон #189

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions ObjectPrinting/IPrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;

namespace ObjectPrinting;

public interface IPrintingConfig<TOwner>
{
Dictionary<Type, Func<object, string>> TypeSerializers { get; }
Dictionary<PropertyInfo, Func<object, string>> PropertySerializers { get; }
Dictionary<Type, CultureInfo> Cultures { get; }
Dictionary<PropertyInfo, int> PropertiesToTrim { get; }
}
9 changes: 9 additions & 0 deletions ObjectPrinting/IPropertyPrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Reflection;

namespace ObjectPrinting;

public interface IPropertyPrintingConfig<TOwner, TPropType>
{
PrintingConfig<TOwner> ParentConfig { get; }
PropertyInfo Property { get; }
}
6 changes: 6 additions & 0 deletions ObjectPrinting/ITypePrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ObjectPrinting;

public interface ITypePrintingConfig<TOwner, TType>
{
PrintingConfig<TOwner> ParentConfig { get; }
}
9 changes: 9 additions & 0 deletions ObjectPrinting/ObjectExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace ObjectPrinting;

public static class ObjectExtensions
{
public static string PrintToString<T>(this T obj)
{
return ObjectPrinter.For<T>().PrintToString(obj);
}
}
11 changes: 5 additions & 6 deletions ObjectPrinting/ObjectPrinter.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
namespace ObjectPrinting
namespace ObjectPrinting;

public class ObjectPrinter
{
public class ObjectPrinter
public static PrintingConfig<T> For<T>()
{
public static PrintingConfig<T> For<T>()
{
return new PrintingConfig<T>();
}
return new PrintingConfig<T>();
}
}
9 changes: 2 additions & 7 deletions ObjectPrinting/ObjectPrinting.csproj
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<LangVersion>8</LangVersion>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>10</LangVersion>
<TargetFramework>net7.0</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="NUnit" Version="3.12.0" />
</ItemGroup>

</Project>
176 changes: 146 additions & 30 deletions ObjectPrinting/PrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,157 @@
using System;
using System.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace ObjectPrinting
namespace ObjectPrinting;

public class PrintingConfig<TOwner> : IPrintingConfig<TOwner>
{
public class PrintingConfig<TOwner>
private readonly Dictionary<Type, CultureInfo> cultures = new();
private readonly Dictionary<Type, Func<object, string>> typeSerializers = new();
private readonly Dictionary<PropertyInfo, Func<object, string>> propertySerializers = new();
private readonly Dictionary<PropertyInfo, int> proprtiesToTrim = new();
private readonly HashSet<PropertyInfo> excludedProperties = new();
private readonly HashSet<Type> excludedTypes = new();
private readonly HashSet<Type> finalTypes = new()
{
typeof(int),
typeof(double),
typeof(float),
typeof(string),
typeof(DateTime),
typeof(TimeSpan)
};

Dictionary<Type, Func<object, string>> IPrintingConfig<TOwner>.TypeSerializers => typeSerializers;
Dictionary<PropertyInfo, int> IPrintingConfig<TOwner>.PropertiesToTrim => proprtiesToTrim;
Dictionary<PropertyInfo, Func<object, string>> IPrintingConfig<TOwner>.PropertySerializers => propertySerializers;
Dictionary<Type, CultureInfo> IPrintingConfig<TOwner>.Cultures => cultures;

public TypePrintingConfig<TOwner, TType> For<TType>()
{
return new TypePrintingConfig<TOwner, TType>(this);
}

public PropertyPrintingConfig<TOwner, TPropType> For<TPropType>(Expression<Func<TOwner, TPropType>> memberSelector)
{
return new PropertyPrintingConfig<TOwner, TPropType>(this, GetPropertyInfo(memberSelector));
}

public PrintingConfig<TOwner> Excluding<TPropType>(Expression<Func<TOwner, TPropType>> memberSelector)
{
var property = GetPropertyInfo(memberSelector);
excludedProperties.Add(property);
return this;
}

public PrintingConfig<TOwner> Excluding<TPropType>()
{
excludedTypes.Add(typeof(TPropType));
return this;
}

private PropertyInfo GetPropertyInfo<TPropType>(Expression<Func<TOwner, TPropType>> expression)
{
public string PrintToString(TOwner obj)
if (expression.Body is MemberExpression member)
{
return PrintToString(obj, 0);
return (PropertyInfo)member.Member;
}
throw new ArgumentException("please, choose property");
}

public string PrintToString(TOwner obj)
{
return PrintToString(obj, 0, new HashSet<object>());
}

private string PrintToString(object obj, int nestingLevel, HashSet<object> printed)
{
if (obj == null)
return "null" + Environment.NewLine;
var type = obj.GetType();

private string PrintToString(object obj, int nestingLevel)
if (finalTypes.Contains(type))
return obj + Environment.NewLine;

if (printed.Contains(obj))
return "this " + type.Name + Environment.NewLine;
printed.Add(obj);

if (obj is IEnumerable<object> collection)
return PrintCollection(collection, nestingLevel, printed);
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
return PrintDictionary(obj, nestingLevel, printed);
var sb = new StringBuilder();
sb.AppendLine(type.Name);
sb = PrintNestingElements(sb, obj, nestingLevel, printed);
return sb.ToString();
}

private string PrintCollection(IEnumerable<object> collection, int nestingLevel, HashSet<object> printed)
{
var sb = new StringBuilder();
var identation = new string('\t', nestingLevel + 1);
if (!collection.GetEnumerator().MoveNext())
return ("Empty" + Environment.NewLine);
foreach (var item in collection)
{
//TODO apply configurations
if (obj == null)
return "null" + Environment.NewLine;

var finalTypes = new[]
{
typeof(int), typeof(double), typeof(float), typeof(string),
typeof(DateTime), typeof(TimeSpan)
};
if (finalTypes.Contains(obj.GetType()))
return obj + Environment.NewLine;

var identation = new string('\t', nestingLevel + 1);
var sb = new StringBuilder();
var type = obj.GetType();
sb.AppendLine(type.Name);
foreach (var propertyInfo in type.GetProperties())
{
sb.Append(identation + propertyInfo.Name + " = " +
PrintToString(propertyInfo.GetValue(obj),
nestingLevel + 1));
}
return sb.ToString();
sb.Append(identation + PrintToString(item, nestingLevel + 1, printed));
}
return sb.ToString();
}

private string PrintDictionary(object obj, int nestingLevel, HashSet<object> printed)
{
var dict = (IDictionary)obj;
if (!dict.GetEnumerator().MoveNext())
return ("Empty" + Environment.NewLine);
var sb = new StringBuilder();
var identation = new string('\t', nestingLevel + 1);
foreach (var pair in dict)
{
var key = ((DictionaryEntry)pair).Key;
var value = ((DictionaryEntry)pair).Value;
sb.Append(identation + PrintToString(key, nestingLevel, printed).Trim()
+ " : "
+ PrintToString(value, nestingLevel, printed).Trim()
+ Environment.NewLine);
}
return sb.ToString();
}

private StringBuilder PrintNestingElements(StringBuilder sb, object obj, int nestingLevel, HashSet<object> printed)
{
var type = obj.GetType();
var identation = new string('\t', nestingLevel + 1);
foreach (var property in type.GetProperties())
{
if (!excludedTypes.Contains(property.PropertyType) && !excludedProperties.Contains(property))
sb.Append(identation + property.Name
+ " = "
+ PrintProperty(property, property.GetValue(obj), nestingLevel + 1, printed));
}
return sb;
}

private string PrintProperty(PropertyInfo property, object obj, int nestingLevel, HashSet<object> printed)
{
string result = null;
if (cultures.TryGetValue(property.PropertyType, out var culture))
result = string.Format(culture, "{0}", obj);
if (typeSerializers.TryGetValue(property.PropertyType, out var typeSerialize))
result = typeSerialize.Invoke(obj);
if (propertySerializers.TryGetValue(property, out var propertySerialize))
result = propertySerialize.Invoke(obj);
if (proprtiesToTrim.TryGetValue(property, out var trim))
{
result ??= obj as string;
result = result[..Math.Min(result.Length, trim)];
}

return result is null ? PrintToString(obj, nestingLevel, printed) : result + Environment.NewLine;
}
}
28 changes: 28 additions & 0 deletions ObjectPrinting/PropertyPrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Reflection;

namespace ObjectPrinting;

public class PropertyPrintingConfig<TOwner, TPropType> : IPropertyPrintingConfig<TOwner, TPropType>
{
private readonly PrintingConfig<TOwner> printingConfig;
private readonly PropertyInfo propertyInfo;

public PropertyPrintingConfig(PrintingConfig<TOwner> printingConfig, PropertyInfo property)
{
this.printingConfig = printingConfig;
propertyInfo = property;
}

public PrintingConfig<TOwner> Using(Func<TPropType, string> print)
{
var config = (IPrintingConfig<TOwner>)printingConfig;
config.PropertySerializers[propertyInfo] = p => print((TPropType)p);

return printingConfig;
}

PrintingConfig<TOwner> IPropertyPrintingConfig<TOwner, TPropType>.ParentConfig => printingConfig;

PropertyInfo IPropertyPrintingConfig<TOwner, TPropType>.Property => propertyInfo;
}
20 changes: 20 additions & 0 deletions ObjectPrinting/PropertyPrintingConfigExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace ObjectPrinting;

public static class PropertyPrintingConfigExtensions
{
public static string PrintToString<T>(this T obj, Func<PrintingConfig<T>, PrintingConfig<T>> config)
{
return config(ObjectPrinter.For<T>()).PrintToString(obj);
}

public static PrintingConfig<TOwner> TrimmedToLength<TOwner>(this PropertyPrintingConfig<TOwner, string> propConfig, int maxLen)
{
var propertyConfig = (IPropertyPrintingConfig<TOwner, string>)propConfig;
var printingConfig = (IPrintingConfig<TOwner>)propertyConfig.ParentConfig;
printingConfig.PropertiesToTrim[propertyConfig.Property] = maxLen;
return propertyConfig.ParentConfig;
}

}
10 changes: 0 additions & 10 deletions ObjectPrinting/Solved/ObjectExtensions.cs

This file was deleted.

10 changes: 0 additions & 10 deletions ObjectPrinting/Solved/ObjectPrinter.cs

This file was deleted.

Loading