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

Волков Кирилл #199

Open
wants to merge 6 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
47 changes: 47 additions & 0 deletions ObjectPrinting/Extensions/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text;

namespace ObjectPrinting.Extensions;

public static class EnumerableExtensions
{
public static string ObjectPrint<T>(
this IEnumerable<object> enumerable,
Serializer<T> serializer,
ImmutableList<object> previous)
{

previous = previous.Add(enumerable);

return enumerable.ObjectPrintingSerialize(
"[", "]",
obj => $"{serializer.PrintToString(obj, previous)},",
previous.Count);
}

public static string ObjectPrintingSerialize<T>(
this IEnumerable<T> enumerable,
string startSymbol,
string endSymbol,
Func<T, string> elementSerializer,
int indentationLevel)
{
var sb = new StringBuilder();
sb.AppendLine(startSymbol);

var indentation = new string('\t', indentationLevel);

foreach (var obj in enumerable)
{
sb.Append(indentation);
sb.AppendLine(elementSerializer(obj));
}

sb.Append(new string('\t', indentationLevel - 1));
sb.Append(endSymbol);

return sb.ToString();
}
}
29 changes: 29 additions & 0 deletions ObjectPrinting/Extensions/IDictionaryExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;

namespace ObjectPrinting.Extensions;

public static class DictionaryExtensions
{
public static string ObjectPrint<T>(
this IDictionary dictionary,
Serializer<T> serializer,
ImmutableList<object> previous)
{
previous = previous.Add(dictionary);

return dictionary
.CastToDictionaryEntries()
.ObjectPrintingSerialize(
"{", "}",
pair => $"{pair.Key}: {serializer.PrintToString(pair.Value, previous)};",
previous.Count);
}

private static IEnumerable<DictionaryEntry> CastToDictionaryEntries(this IDictionary dictionary)
{
foreach (DictionaryEntry entry in dictionary)
yield return entry;
}
}
14 changes: 14 additions & 0 deletions ObjectPrinting/Extensions/ObjectExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace ObjectPrinting.Extensions;

public static class ObjectExtensions
{
public static string PrintToString<T>(this T obj, PrintingConfig<T> printingConfig)
{
return new Serializer<T>(printingConfig).PrintToString(obj);
}

public static string PrintToString<T>(this T obj)
{
return new Serializer<T>(ObjectPrinter.For<T>()).PrintToString(obj);
}
}
9 changes: 9 additions & 0 deletions ObjectPrinting/Interfaces/IPropertySpecifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace ObjectPrinting.Interfaces
{
public interface IPropertySpecifier<out T, TOwner>
{
public PrintingConfig<TOwner> With(Func<T, string> serializer);
}
}
9 changes: 9 additions & 0 deletions ObjectPrinting/Interfaces/ISerializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Collections.Immutable;

namespace ObjectPrinting.Interfaces;

public interface ISerializer<in T>
{
public string PrintToString(object obj, ImmutableList<object> previous);
public string PrintToString(T obj);
}
10 changes: 10 additions & 0 deletions ObjectPrinting/Interfaces/ISerializerSetter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
using System.Reflection;

namespace ObjectPrinting.Interfaces
{
public interface ISerializerSetter
{
public void SetSerializer<T>(Func<T, string> serializer, PropertyInfo propertyInfo);
}
}
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">

This comment was marked as resolved.


<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>
139 changes: 103 additions & 36 deletions ObjectPrinting/PrintingConfig.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,108 @@
using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
using ObjectPrinting.Interfaces;

namespace ObjectPrinting
namespace ObjectPrinting;

public class PrintingConfig<TOwner> : ISerializerSetter
{
public class PrintingConfig<TOwner>
{
public string PrintToString(TOwner obj)
{
return PrintToString(obj, 0);
}

private string PrintToString(object obj, int nestingLevel)
{
//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();
}
private readonly Dictionary<Type, Func<object, string>> typeSerializers = new();
private readonly Dictionary<PropertyInfo, Func<object, string>> propertySerializers = new();

private readonly HashSet<PropertyInfo> excludedProps = new();
private readonly HashSet<Type> excludedTypes = new();

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

public PrintingConfig<TOwner> Exclude<T>(Expression<Func<TOwner, T>> expression)
{
excludedProps.Add(GetPropertyInfoFromExpression(expression));
return this;
}

public IPropertySpecifier<T, TOwner> Serialize<T>()
{
return new PropertySpecifier<T, TOwner>(this);
}

public IPropertySpecifier<T, TOwner> Serialize<T>(Expression<Func<TOwner, T>> expression)
{
return new PropertySpecifier<T, TOwner>(this, GetPropertyInfoFromExpression(expression));
}

public PrintingConfig<TOwner> SetCulture<T>(CultureInfo cultureInfo)
where T: IFormattable
{
return Serialize<T>()
.With(obj => string.Format(cultureInfo, "{0}", obj));
}

public PrintingConfig<TOwner> SetCulture<T>(Expression<Func<TOwner, T>> expression, CultureInfo cultureInfo)
where T: IFormattable
{
return Serialize(expression)
.With(obj => string.Format(cultureInfo, "{0}", obj));
}

public PrintingConfig<TOwner> SliceStrings(int maxLength)
{
if (maxLength <= 0)
throw new ArgumentException("Parameter maxLength must be positive");

return Serialize<string>()
.With(s => maxLength >= s.Length ? s : s[..maxLength]);
}

void ISerializerSetter.SetSerializer<T>(Func<T, string> serializer, PropertyInfo propertyInfo)

This comment was marked as resolved.

{
if (propertyInfo is not null)
propertySerializers[propertyInfo] = obj => serializer((T) obj);
else
typeSerializers[typeof(T)] = obj => serializer((T) obj);
}

internal bool TrySerializeValueType(Type type, object obj, out string serialized)
{
serialized = "";

if (obj is not string && !type.IsValueType)
return false;

serialized = typeSerializers.TryGetValue(type, out var serializer)
? serializer(obj)
: obj.ToString();

return true;
}

internal bool IsExcluded(PropertyInfo propertyInfo)
{
return excludedProps.Contains(propertyInfo) || excludedTypes.Contains(propertyInfo.PropertyType);
}

internal bool TrySerializeProperty(object obj, PropertyInfo propertyInfo, out string serialized)
{
serialized = "";

if (!propertySerializers.TryGetValue(propertyInfo, out var serializer))
return false;

serialized = serializer(propertyInfo.GetValue(obj));
return true;
}

private static PropertyInfo GetPropertyInfoFromExpression<T>(Expression<Func<TOwner, T>> expression)
{
if (expression.Body is not MemberExpression memberExpression)
throw new ArgumentException($"Expression {expression} is not MemberExpression");

return memberExpression.Member as PropertyInfo;
}
}
30 changes: 30 additions & 0 deletions ObjectPrinting/PropertySpecifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Reflection;
using ObjectPrinting.Interfaces;

namespace ObjectPrinting
{
public class PropertySpecifier<T, TOwner> : IPropertySpecifier<T, TOwner>
{
private PrintingConfig<TOwner> PrintingConfig { get; }
private readonly PropertyInfo propertyInfo;

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

public PropertySpecifier(PrintingConfig<TOwner> printingConfig)
{
PrintingConfig = printingConfig;
}

public PrintingConfig<TOwner> With(Func<T, string> serializer)
{
((ISerializerSetter)PrintingConfig).SetSerializer(serializer, propertyInfo);

return PrintingConfig;
}
}
}
74 changes: 74 additions & 0 deletions ObjectPrinting/Serializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Text;
using ObjectPrinting.Extensions;
using ObjectPrinting.Interfaces;

namespace ObjectPrinting;

public class Serializer<T> : ISerializer<T>
{
private readonly PrintingConfig<T> printingConfig;

public Serializer(PrintingConfig<T> printingConfig)
{
this.printingConfig = printingConfig;
}

public string PrintToString(T obj)
{
return PrintToString(obj, new List<object>().ToImmutableList());
}

public string PrintToString(object obj, ImmutableList<object> previous)
{
if (obj == null)
return "null";

var type = obj.GetType();

if (printingConfig.TrySerializeValueType(type, obj, out var serializedValue))
return serializedValue;

if (IsCyclic(obj, previous))
return "cyclic link";

if (obj is IDictionary dictionary)
return dictionary.ObjectPrint(this, previous);

if (obj is IEnumerable enumerable)
return enumerable.Cast<object>().ObjectPrint(this, previous);

var indentation = new string('\t', previous.Count + 1);
previous = previous.Add(obj);
var sb = new StringBuilder();
sb.AppendLine(type.Name + " (");

foreach (var property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (printingConfig.IsExcluded(property))
continue;

sb.Append($"{indentation}{property.Name}: ");

if (printingConfig.TrySerializeProperty(obj, property, out var serialized))
sb.Append(serialized);
else
sb.Append(PrintToString(property.GetValue(obj), previous));

sb.AppendLine(";");
}

sb.Append(new string('\t', previous.Count - 1) + ")");

return sb.ToString();
}

private static bool IsCyclic(object current, IEnumerable<object> previous)
{
return previous.Any(e => ReferenceEquals(e, current));
}
}
Loading