-
Notifications
You must be signed in to change notification settings - Fork 245
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
Большаков Николай #235
Open
stupidnessplusplus
wants to merge
13
commits into
kontur-courses:master
Choose a base branch
from
stupidnessplusplus:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Большаков Николай #235
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
bda9d35
Delete existing ObjectPrinting project
stupidnessplusplus e8c52d6
Set up project and classes
stupidnessplusplus 0f03c70
Add classes for tests
stupidnessplusplus 1161a5a
Add tests
stupidnessplusplus 0720f90
Add PropertyPath and PropertyValue
stupidnessplusplus 43dc28a
Add internal PrintingConfig interfaces
stupidnessplusplus 98f8bcf
Add main classes
stupidnessplusplus c3ba198
Remove unnecessary check
stupidnessplusplus 5c19883
Add test
stupidnessplusplus 0ad5995
Fix property enumeration stops after null or excluded property
stupidnessplusplus fcbd4bc
Add tests for nested objects, rename test
stupidnessplusplus 8046986
Change cyclic reference tests
stupidnessplusplus 432232c
Rewrite ObjectPrinter
stupidnessplusplus File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Globalization; | ||
|
||
namespace ObjectPrinting; | ||
|
||
internal interface IPrintingConfig<TOwner> | ||
{ | ||
Dictionary<Type, IPropertyPrintingConfig<TOwner>> PropertyConfigsByType { get; } | ||
|
||
Dictionary<PropertyPath, IPropertyPrintingConfig<TOwner>> PropertyConfigsByPath { get; } | ||
|
||
CultureInfo CultureInfo { get; } | ||
|
||
bool IsToLimitNestingLevel { get; } | ||
|
||
int MaxNestingLevel { get; } | ||
|
||
public bool TryGetConfig( | ||
PropertyPath path, | ||
[MaybeNullWhen(false)] out IPropertyPrintingConfig<TOwner> propertyPrintingConfig) | ||
{ | ||
var obj = path.PropertyValue.Value; | ||
return PropertyConfigsByPath.TryGetValue(path, out propertyPrintingConfig) | ||
|| obj != null | ||
&& PropertyConfigsByType.TryGetValue(obj.GetType(), out propertyPrintingConfig); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System.Globalization; | ||
|
||
namespace ObjectPrinting; | ||
|
||
internal interface IPropertyPrintingConfig<TOwner> | ||
{ | ||
PrintingConfig<TOwner> ParentConfig { get; } | ||
|
||
bool IsExcluded { get; } | ||
|
||
CultureInfo? CultureInfo { get; } | ||
|
||
Func<object, string>? PrintOverride { get; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,199 @@ | ||
namespace ObjectPrinting | ||
using System.Collections; | ||
using System.Reflection; | ||
using System.Text; | ||
|
||
namespace ObjectPrinting; | ||
|
||
public class ObjectPrinter | ||
{ | ||
public class ObjectPrinter | ||
private static readonly HashSet<Type> _finalTypes = | ||
[ | ||
typeof(Guid), | ||
typeof(DateTime), | ||
typeof(TimeSpan), | ||
typeof(string), | ||
]; | ||
|
||
public static PrintingConfig<T> For<T>() | ||
{ | ||
public static PrintingConfig<T> For<T>() | ||
return new PrintingConfig<T>(); | ||
} | ||
|
||
internal static string PrintToString<TOwner>(TOwner? obj, IPrintingConfig<TOwner> printingConfig) | ||
{ | ||
var path = new PropertyPath(new PropertyValue(null, obj)); | ||
|
||
if (!IsExcluded(path, printingConfig, 0)) | ||
{ | ||
var sb = new StringBuilder(); | ||
AppendPrintedObject(sb, path, printingConfig, 0); | ||
return sb.ToString(); | ||
} | ||
|
||
return string.Empty; | ||
} | ||
|
||
private static void AppendPrintedObject<TOwner>( | ||
StringBuilder sb, | ||
PropertyPath path, | ||
IPrintingConfig<TOwner> printingConfig, | ||
int nestingLevel) | ||
{ | ||
var obj = path.PropertyValue.Value; | ||
|
||
if (obj == null) | ||
{ | ||
sb.AppendLine("null"); | ||
} | ||
else if (IsCyclicReference(path)) | ||
{ | ||
AppendCyclicReference(sb, path); | ||
} | ||
else if (IsAlternativelyPrintedObject(path, printingConfig)) | ||
{ | ||
AppendAlternativelyPrintedObject(sb, path, printingConfig); | ||
} | ||
else if (IsFinalTypeObject(obj)) | ||
{ | ||
AppendFinalTypeObject(sb, path, printingConfig); | ||
} | ||
else if (obj is IEnumerable) | ||
{ | ||
AppendEnumerableObjectWithItems(sb, path, printingConfig, nestingLevel); | ||
} | ||
else | ||
{ | ||
AppendObjectWithProperties(sb, path, printingConfig, nestingLevel); | ||
} | ||
} | ||
|
||
private static bool IsCyclicReference(PropertyPath path) | ||
{ | ||
var obj = path.PropertyValue.Value!; | ||
return path.Previous != null | ||
&& path.Previous.Contains(obj); | ||
} | ||
|
||
private static void AppendCyclicReference(StringBuilder sb, PropertyPath path) | ||
{ | ||
var obj = path.PropertyValue.Value!; | ||
var pathToExistingValue = path.Previous!.FindPathTo(obj); | ||
sb.AppendLine($"[root{pathToExistingValue}]"); | ||
} | ||
|
||
private static bool IsAlternativelyPrintedObject<TOwner>(PropertyPath path, IPrintingConfig<TOwner> printingConfig) | ||
{ | ||
return printingConfig.TryGetConfig(path, out var propertyPrintingConfig) | ||
&& propertyPrintingConfig.PrintOverride != null; | ||
} | ||
|
||
private static void AppendAlternativelyPrintedObject<TOwner>( | ||
StringBuilder sb, | ||
PropertyPath path, | ||
IPrintingConfig<TOwner> printingConfig) | ||
{ | ||
if (printingConfig.TryGetConfig(path, out var propertyPrintingConfig)) | ||
{ | ||
return new PrintingConfig<T>(); | ||
var obj = path.PropertyValue.Value!; | ||
var stringValue = propertyPrintingConfig.PrintOverride!(obj); | ||
sb.AppendLine(stringValue); | ||
} | ||
} | ||
} | ||
|
||
private static bool IsFinalTypeObject(object obj) | ||
{ | ||
var type = obj.GetType(); | ||
return type.IsPrimitive | ||
|| _finalTypes.Contains(type); | ||
} | ||
|
||
private static void AppendFinalTypeObject<TOwner>( | ||
StringBuilder sb, | ||
PropertyPath path, | ||
IPrintingConfig<TOwner> printingConfig) | ||
{ | ||
var cultureInfo = printingConfig.TryGetConfig(path, out var propertyPrintingConfig) | ||
&& propertyPrintingConfig.CultureInfo != null | ||
? propertyPrintingConfig.CultureInfo | ||
: printingConfig.CultureInfo; | ||
|
||
var obj = path.PropertyValue.Value!; | ||
var stringValue = obj is IFormattable formattableObj | ||
? formattableObj.ToString(null, cultureInfo) | ||
: obj.ToString(); | ||
|
||
sb.AppendLine(stringValue); | ||
} | ||
|
||
private static void AppendEnumerableObjectWithItems<TOwner>( | ||
StringBuilder sb, | ||
PropertyPath path, | ||
IPrintingConfig<TOwner> printingConfig, | ||
int nestingLevel) | ||
{ | ||
var enumerable = (IEnumerable)path.PropertyValue.Value!; | ||
var type = enumerable.GetType(); | ||
var typeName = GetTypeNameWithoutGenericPart(type); | ||
var values = enumerable | ||
.Cast<object>() | ||
.Select((item, i) => new PropertyValue($"{{{i}}}", type, item)); | ||
|
||
sb.AppendLine(typeName); | ||
AppendPropertyValues(sb, path, printingConfig, values, nestingLevel); | ||
} | ||
|
||
private static void AppendObjectWithProperties<TOwner>( | ||
StringBuilder sb, | ||
PropertyPath path, | ||
IPrintingConfig<TOwner> printingConfig, | ||
int nestingLevel) | ||
{ | ||
var obj = path.PropertyValue.Value!; | ||
var type = obj.GetType(); | ||
var typeName = GetTypeNameWithoutGenericPart(type); | ||
var values = type | ||
.GetProperties(BindingFlags.Public | BindingFlags.Instance) | ||
.Where(property => property.GetIndexParameters().Length == 0) | ||
.Select(property => new PropertyValue(property, property.GetValue(obj))); | ||
|
||
sb.AppendLine(typeName); | ||
AppendPropertyValues(sb, path, printingConfig, values, nestingLevel); | ||
} | ||
|
||
private static void AppendPropertyValues<TOwner>( | ||
StringBuilder sb, | ||
PropertyPath path, | ||
IPrintingConfig<TOwner> printingConfig, | ||
IEnumerable<PropertyValue> propertyValues, | ||
int nestingLevel) | ||
{ | ||
foreach (var propertyValue in propertyValues) | ||
{ | ||
var nextPath = new PropertyPath(propertyValue, path); | ||
|
||
if (!IsExcluded(nextPath, printingConfig, nestingLevel + 1)) | ||
{ | ||
sb.Append('\t', nestingLevel + 1); | ||
sb.Append(propertyValue.Name); | ||
sb.Append(" = "); | ||
AppendPrintedObject(sb, nextPath, printingConfig, nestingLevel + 1); | ||
} | ||
} | ||
} | ||
|
||
private static bool IsExcluded<TOwner>(PropertyPath path, IPrintingConfig<TOwner> printingConfig, int nestingLevel) | ||
{ | ||
return printingConfig.IsToLimitNestingLevel | ||
&& nestingLevel > printingConfig.MaxNestingLevel | ||
|| printingConfig.TryGetConfig(path, out var propertyPrintingConfig) | ||
&& propertyPrintingConfig!.IsExcluded; | ||
} | ||
|
||
private static string GetTypeNameWithoutGenericPart(Type type) | ||
{ | ||
var end = type.Name.IndexOf('`'); | ||
return end == -1 | ||
? type.Name | ||
: type.Name[..end]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,9 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" /> | ||
<PackageReference Include="NUnit" Version="4.2.2" /> | ||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" /> | ||
</ItemGroup> | ||
</Project> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
на мой взгляд класс достаточно тяжело читается, классическая рекурсия тут бы выйграла для чтения