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

Холстинин Егор #188

Open
wants to merge 14 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
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>();
}
}
7 changes: 1 addition & 6 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>
<LangVersion>latest</LangVersion>
<TargetFramework>netcoreapp3.1</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>
168 changes: 141 additions & 27 deletions ObjectPrinting/PrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,155 @@
using System;
using System.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace ObjectPrinting
namespace ObjectPrinting;

public class PrintingConfig<TOwner>
{
public class PrintingConfig<TOwner>
private readonly HashSet<Type> excludedTypes = new HashSet<Type>();
private readonly HashSet<PropertyInfo> excludedProperties = new HashSet<PropertyInfo>();

internal readonly Dictionary<Type, Func<object, string>> typeSerializers =
new Dictionary<Type, Func<object, string>>();

internal readonly Dictionary<PropertyInfo, Func<object, string>> propertySerializers =
new Dictionary<PropertyInfo, Func<object, string>>();

public TypePrintingConfig<TOwner, TPropType> Printing<TPropType>()
{
return new TypePrintingConfig<TOwner, TPropType>(this);
}

public PropertyPrintingConfig<TOwner, TPropType> Printing<TPropType>(
Expression<Func<TOwner, TPropType>> memberSelector)
{
var propInfo = ((MemberExpression)memberSelector.Body).Member as PropertyInfo;
return new PropertyPrintingConfig<TOwner, TPropType>(this, propInfo);
}

public PrintingConfig<TOwner> Excluding<TPropType>(Expression<Func<TOwner, TPropType>> memberSelector)
{
var propInfo = ((MemberExpression)memberSelector.Body).Member as PropertyInfo;
excludedProperties.Add(propInfo);
return this;
}

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

public string PrintToString(TOwner obj)
{
return PrintToString(obj, ImmutableHashSet<object>.Empty);
}

private string SerializeIEnumerable(IEnumerable enumerable, ImmutableHashSet<object> nestedObjects)
{
if (enumerable is IDictionary dictionary)
return SerializeIDictionary(dictionary, nestedObjects);
var enumerator = enumerable.GetEnumerator();
var hasFirstElement = enumerator.MoveNext();
if (!hasFirstElement)
return "[]";
var firstItemType = enumerator.Current;
if (firstItemType.GetType().IsValueType || firstItemType is string)
{
return SerializeValueTypeIEnumerable(enumerable, nestedObjects);
}

return SerializeReferenceTypeIEnumerable(enumerable, nestedObjects);
}

private string SerializeValueTypeIEnumerable(IEnumerable enumerable, ImmutableHashSet<object> nestedObjects)
{
public string PrintToString(TOwner obj)
var sb = new List<string>();
sb.Add("[");
foreach (var element in enumerable)
{
return PrintToString(obj, 0);
sb.Add(PrintToString(element, nestedObjects).Trim());
}
sb.Add("]");
return string.Join(' ', sb);
}

private string PrintToString(object obj, int nestingLevel)
private string SerializeReferenceTypeIEnumerable(IEnumerable enumerable, ImmutableHashSet<object> nestedObjects)
{
var sb = new List<string>();
var identation = new string('\t', nestedObjects.Count);
sb.Add($"{identation}[");
foreach (var element in enumerable)
{
//TODO apply configurations
if (obj == null)
return "null" + Environment.NewLine;
sb.Add(PrintToString(element, nestedObjects.Add(sb)).TrimEnd());
}
sb.Add($"{identation}]");
return string.Join(Environment.NewLine, sb);
}

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())
private string SerializeIDictionary(IDictionary dictionary, ImmutableHashSet<object> nestedObjects)
{
var sb = new List<string>();
var identation = new string('\t', nestedObjects.Count);
sb.Add($"{identation}{{");
nestedObjects = nestedObjects.Add(dictionary);
foreach (DictionaryEntry dictionaryEntry in dictionary)
{
sb.Add($"{identation}\t[");
sb.Add($"{identation}\t{PrintToString(dictionaryEntry.Key, nestedObjects).TrimEnd()}");
sb.Add($"{identation}\t:");
sb.Add($"{identation}\t{PrintToString(dictionaryEntry.Value, nestedObjects).TrimEnd()}");
sb.Add($"{identation}\t]");
}
sb.Add($"{identation}}}");
return string.Join(Environment.NewLine, sb);
}

private string PrintToString(object obj, ImmutableHashSet<object> nestedObjects)
{
if (obj == null)
return "null" + Environment.NewLine;

var objType = obj.GetType();
if (obj.GetType().IsValueType || obj is string)
{
if (typeSerializers.TryGetValue(objType, out var serializer))
return serializer(obj) + Environment.NewLine;
return obj + Environment.NewLine;
}

if (obj is IEnumerable enumerable)
return SerializeIEnumerable(enumerable, nestedObjects) + Environment.NewLine;

var identation = new string('\t', nestedObjects.Count + 1);
var sb = new StringBuilder();
sb.AppendLine(objType.Name);
nestedObjects = nestedObjects.Add(obj);
foreach (var propertyInfo in objType.GetProperties())
{
var propertyValue = propertyInfo.GetValue(obj);
if (ExcludedFromSerialization(propertyInfo) || nestedObjects.Contains(propertyValue))
continue;

if (propertySerializers.TryGetValue(propertyInfo, out var serializer))
{
sb.Append(identation + propertyInfo.Name + " = " +
PrintToString(propertyInfo.GetValue(obj),
nestingLevel + 1));
sb.Append(identation + propertyInfo.Name + " = " + serializer(propertyValue) + Environment.NewLine);
continue;
}
return sb.ToString();

sb.Append(identation + propertyInfo.Name + " = " +
PrintToString(propertyInfo.GetValue(obj), nestedObjects));
}

return sb.ToString();
}

private bool ExcludedFromSerialization(PropertyInfo propertyInfo)
{
return excludedTypes.Contains(propertyInfo.PropertyType) || excludedProperties.Contains(propertyInfo);
}
}
30 changes: 30 additions & 0 deletions ObjectPrinting/PropertyPrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Reflection;

namespace ObjectPrinting;

public class PropertyPrintingConfig<TOwner, TPropType> : IPropertyPrintingConfig<TOwner, TPropType>
{
public PrintingConfig<TOwner> PrintingConfig { get; }
public PropertyInfo PropertyInfo { get; }

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

public PrintingConfig<TOwner> Using(Func<TPropType, string> print)
{
var castedFunc = new Func<object, string>((obj) => print((TPropType)obj));
PrintingConfig.propertySerializers.Add(PropertyInfo, castedFunc);
return PrintingConfig;
}

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

public interface IPropertyPrintingConfig<TOwner, TPropType>
{
PrintingConfig<TOwner> ParentConfig { get; }
}
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 culturedFunc = new Func<string, string>(s => s[..Math.Min(maxLen, s.Length)]);
var castedFunc = new Func<object, string>(obj => culturedFunc((string)obj));
var parent = propConfig.PrintingConfig;
parent.propertySerializers.Add(propConfig.PropertyInfo, castedFunc);
return parent;
}

}
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.

62 changes: 0 additions & 62 deletions ObjectPrinting/Solved/PrintingConfig.cs

This file was deleted.

Loading