diff --git a/.gitignore b/.gitignore index a1f466689..def330ce9 100644 --- a/.gitignore +++ b/.gitignore @@ -236,3 +236,5 @@ _Pvt_Extensions .fake/ .idea/ + +fluent-api.sln diff --git a/ObjectPrinterTests/CollectionPrintingTests.cs b/ObjectPrinterTests/CollectionPrintingTests.cs new file mode 100644 index 000000000..0582bfe29 --- /dev/null +++ b/ObjectPrinterTests/CollectionPrintingTests.cs @@ -0,0 +1,148 @@ +using ObjectPrinterTests.Entities; +using ObjectPrinting; +using ObjectPrinting.Extensions; + +namespace ObjectPrinterTests; + +[TestFixture] +public class CollectionPrintingSnapshotTests +{ + private static readonly VerifySettings SnapshotSettings; + + static CollectionPrintingSnapshotTests() + { + SnapshotSettings = new VerifySettings(); + SnapshotSettings.UseDirectory("ExpectedResults"); + } + + [Test] + public Task IntArray_Snapshot() + { + var config = new PrintingConfig(); + var value = new[] { 1, 2, 3 }; + + var actual = config.PrintToString(value); + + return Verify(actual, SnapshotSettings); + } + + [Test] + public Task PrintToString_StringList_ShouldHandleCorrect() + { + var config = new PrintingConfig>(); + var value = new List { "one", "two" }; + + var actual = config.PrintToString(value); + + return Verify(actual, SnapshotSettings); + } + + [Test] + public Task PrintToString_Dictionary_ShouldHandleCorrect() + { + var config = new PrintingConfig>(); + var value = new Dictionary + { + ["a"] = 1, + ["b"] = 2 + }; + + var actual = config.PrintToString(value); + + return Verify(actual, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldHandleClassWithCollections_Correctly() + { + var config = new PrintingConfig(); + var person = new Person + { + Name = "John", + Scores = [10, 20], + Tags = ["dev", "qa"], + Addresses = new Dictionary + { + ["home"] = new() { City = "Chelyabinsk" }, + ["work"] = new() { City = "Sverdlovsk" } + } + }; + + var actual = config.PrintToString(person); + + return Verify(actual, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldHandleListOfLists_Correctly() + { + var config = new PrintingConfig>>(); + var value = new List> + { + new() { 1, 2 }, + new() { 3 } + }; + + var actual = config.PrintToString(value); + + return Verify(actual, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldHandleDictionaryWithListValues_Correctly() + { + var config = new PrintingConfig>>(); + var value = new Dictionary> + { + ["first"] = [1, 2], + ["second"] = [3] + }; + + var actual = config.PrintToString(value); + + return Verify(actual, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldHandleDictionaryWithNull_Correctly() + { + var config = new PrintingConfig>>(); + var value = new Dictionary?> + { + ["first"] = null, + ["second"] = null + }; + + var actual = config.PrintToString(value); + + return Verify(actual, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldHandleListWithNull_Correctly() + { + var config = new PrintingConfig>>(); + var value = new List() + { + null, + null, + null + }; + + var actual = config.PrintToString(value); + + return Verify(actual, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldHandleListCycle_Correctly() + { + var config = new PrintingConfig>(); + var value = new List(); + value.Add(value); + + var actual = config.PrintToString(value); + + return Verify(actual, SnapshotSettings); + } +} diff --git a/ObjectPrinterTests/Entities/A.cs b/ObjectPrinterTests/Entities/A.cs new file mode 100644 index 000000000..d771f6be6 --- /dev/null +++ b/ObjectPrinterTests/Entities/A.cs @@ -0,0 +1,9 @@ +namespace ObjectPrinterTests.Entities; + +public class A +{ + public string Name { get; set; } + public int Number { get; set; } + public double Price { get; set; } + public Guid Id { get; set; } +} \ No newline at end of file diff --git a/ObjectPrinterTests/Entities/Address.cs b/ObjectPrinterTests/Entities/Address.cs new file mode 100644 index 000000000..1b3942ce2 --- /dev/null +++ b/ObjectPrinterTests/Entities/Address.cs @@ -0,0 +1,7 @@ +namespace ObjectPrinterTests.Entities; + +public class Address +{ + public string City { get; set; } = ""; + public string Street { get; set; } = ""; +} \ No newline at end of file diff --git a/ObjectPrinterTests/Entities/B.cs b/ObjectPrinterTests/Entities/B.cs new file mode 100644 index 000000000..c4395047d --- /dev/null +++ b/ObjectPrinterTests/Entities/B.cs @@ -0,0 +1,7 @@ +namespace ObjectPrinterTests.Entities; + +public class B +{ + public A Data { get; set; } + public B Parent { get; set; } +} \ No newline at end of file diff --git a/ObjectPrinterTests/Entities/Person.cs b/ObjectPrinterTests/Entities/Person.cs new file mode 100644 index 000000000..88d12122c --- /dev/null +++ b/ObjectPrinterTests/Entities/Person.cs @@ -0,0 +1,9 @@ +namespace ObjectPrinterTests.Entities; + +public class Person +{ + public string Name { get; set; } = ""; + public int[] Scores { get; set; } = []; + public List Tags { get; set; } = []; + public Dictionary Addresses { get; set; } = new(); +} \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.IntArray_Snapshot.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.IntArray_Snapshot.verified.txt new file mode 100644 index 000000000..7a2e38be0 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.IntArray_Snapshot.verified.txt @@ -0,0 +1,5 @@ +Int32[] [ + 1 + 2 + 3 +] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_Dictionary_ShouldHandleCorrect.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_Dictionary_ShouldHandleCorrect.verified.txt new file mode 100644 index 000000000..74d1a37f3 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_Dictionary_ShouldHandleCorrect.verified.txt @@ -0,0 +1,4 @@ +Dictionary { + [a] = 1 + [b] = 2 +} diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleClassWithCollections_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleClassWithCollections_Correctly.verified.txt new file mode 100644 index 000000000..45bb773da --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleClassWithCollections_Correctly.verified.txt @@ -0,0 +1,20 @@ +Person: + Name = John + Scores = Int32[] [ + 10 + 20 + ] + Tags = List [ + dev + qa + ] + Addresses = Dictionary { + [home] = Address: + City = Chelyabinsk + Street = + + [work] = Address: + City = Sverdlovsk + Street = + + } diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithListValues_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithListValues_Correctly.verified.txt new file mode 100644 index 000000000..010004b06 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithListValues_Correctly.verified.txt @@ -0,0 +1,9 @@ +Dictionary> { + [first] = List [ + 1 + 2 + ] + [second] = List [ + 3 + ] +} \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithNull_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithNull_Correctly.verified.txt new file mode 100644 index 000000000..2679fce74 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleDictionaryWithNull_Correctly.verified.txt @@ -0,0 +1,4 @@ +Dictionary> { + [first] = null + [second] = null +} \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListCycle_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListCycle_Correctly.verified.txt new file mode 100644 index 000000000..e83c15011 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListCycle_Correctly.verified.txt @@ -0,0 +1,3 @@ +List [ + +] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListOfLists_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListOfLists_Correctly.verified.txt new file mode 100644 index 000000000..04c060714 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListOfLists_Correctly.verified.txt @@ -0,0 +1,9 @@ +List> [ + List [ + 1 + 2 + ] + List [ + 3 + ] +] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListWithNull_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListWithNull_Correctly.verified.txt new file mode 100644 index 000000000..2ab2c6367 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_ShouldHandleListWithNull_Correctly.verified.txt @@ -0,0 +1,5 @@ +List [ + null + null + null +] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_StringList_ShouldHandleCorrect.verified.txt b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_StringList_ShouldHandleCorrect.verified.txt new file mode 100644 index 000000000..71c9e25b5 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/CollectionPrintingSnapshotTests.PrintToString_StringList_ShouldHandleCorrect.verified.txt @@ -0,0 +1,4 @@ +List [ + one + two +] \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCultureInfo_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCultureInfo_Correctly.verified.txt new file mode 100644 index 000000000..df311f04c --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCultureInfo_Correctly.verified.txt @@ -0,0 +1,5 @@ +A: + Name = null + Number = 0 + Price = 1234,56 + Id = 00000000-0000-0000-0000-000000000000 diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCustomSerializationForProperty_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCustomSerializationForProperty_Correctly.verified.txt new file mode 100644 index 000000000..0112de9df --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldApplyCustomSerializationForProperty_Correctly.verified.txt @@ -0,0 +1,5 @@ +A: + Name = NAME=Alex + Number = 0 + Price = 0 + Id = 00000000-0000-0000-0000-000000000000 diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeSpecificProperty_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeSpecificProperty_Correctly.verified.txt new file mode 100644 index 000000000..2a69eef1d --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeSpecificProperty_Correctly.verified.txt @@ -0,0 +1,4 @@ +A: + Name = Alex + Price = 0 + Id = 00000000-0000-0000-0000-000000000000 diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeType_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeType_Correctly.verified.txt new file mode 100644 index 000000000..2c706848f --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldExcludeType_Correctly.verified.txt @@ -0,0 +1,4 @@ +A: + Name = Alex + Number = 0 + Price = 0 diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleCyclicReferences_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleCyclicReferences_Correctly.verified.txt new file mode 100644 index 000000000..f29a70f80 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleCyclicReferences_Correctly.verified.txt @@ -0,0 +1,11 @@ +B: + Data = A: + Name = Child + Number = 0 + Price = 0 + Id = 00000000-0000-0000-0000-000000000000 + + Parent = B: + Data = null + Parent = + diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleNullObject_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleNullObject_Correctly.verified.txt new file mode 100644 index 000000000..c296c2eef --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldHandleNullObject_Correctly.verified.txt @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldTrimString_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldTrimString_Correctly.verified.txt new file mode 100644 index 000000000..e9434b485 --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldTrimString_Correctly.verified.txt @@ -0,0 +1,5 @@ +A: + Name = Alex + Number = 0 + Price = 0 + Id = 00000000-0000-0000-0000-000000000000 diff --git a/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldUseCustomSerialization_Correctly.verified.txt b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldUseCustomSerialization_Correctly.verified.txt new file mode 100644 index 000000000..cddf924bc --- /dev/null +++ b/ObjectPrinterTests/ExpectedResults/ObjectPrinterTests.PrintToString_ShouldUseCustomSerialization_Correctly.verified.txt @@ -0,0 +1,5 @@ +A: + Name = null + Number = INT(42) + Price = 0 + Id = 00000000-0000-0000-0000-000000000000 diff --git a/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs new file mode 100644 index 000000000..93acbd7d8 --- /dev/null +++ b/ObjectPrinterTests/ObjectPrinterAcceptanceTests.cs @@ -0,0 +1,129 @@ +using System.Globalization; +using ObjectPrinterTests.Entities; +using ObjectPrinting; +using ObjectPrinting.Extensions; + +namespace ObjectPrinterTests; + +[TestFixture] +public class ObjectPrinterTests +{ + private static readonly VerifySettings SnapshotSettings; + + static ObjectPrinterTests() + { + SnapshotSettings = new VerifySettings(); + SnapshotSettings.UseDirectory("ExpectedResults"); + } + + [Test] + public Task PrintToString_ShouldHandleNullObject_Correctly() + { + A? obj = null; + + var result = ObjectPrinter.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldExcludeType_Correctly() + { + var obj = new A + { + Id = Guid.Parse("11111111-1111-1111-1111-111111111111"), + Name = "Alex" + }; + + var printer = ObjectPrinter.For() + .Excluding(); + + var result = printer.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldUseCustomSerialization_Correctly() + { + var obj = new A { Number = 42 }; + + var printer = ObjectPrinter.For() + .Printing() + .Using(x => $"INT({x})"); + + var result = printer.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldApplyCultureInfo_Correctly() + { + var obj = new A { Price = 1234.56 }; + + var printer = ObjectPrinter.For() + .Printing() + .Using(CultureInfo.GetCultureInfo("fr-FR")); + + var result = printer.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldApplyCustomSerializationForProperty_Correctly() + { + var obj = new A { Name = "Alex" }; + + var printer = ObjectPrinter.For() + .Printing(a => a.Name) + .Using(n => $"NAME={n}"); + + var result = printer.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldTrimString_Correctly() + { + var obj = new A { Name = "Alexander" }; + + var printer = ObjectPrinter.For() + .Printing(a => a.Name) + .TrimmedToLength(4); + + var result = printer.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldExcludeSpecificProperty_Correctly() + { + var obj = new A { Name = "Alex", Number = 10 }; + + var printer = ObjectPrinter.For() + .Excluding(a => a.Number); + + var result = printer.PrintToString(obj); + + return Verify(result, SnapshotSettings); + } + + [Test] + public Task PrintToString_ShouldHandleCyclicReferences_Correctly() + { + var parent = new B(); + var child = new B { Parent = parent }; + parent.Data = new A { Name = "Child" }; + parent.Parent = child; + + var printer = ObjectPrinter.For(); + + var result = printer.PrintToString(parent); + + return Verify(result, SnapshotSettings); + } +} diff --git a/ObjectPrinterTests/ObjectPrinterTests.csproj b/ObjectPrinterTests/ObjectPrinterTests.csproj new file mode 100644 index 000000000..13b2812be --- /dev/null +++ b/ObjectPrinterTests/ObjectPrinterTests.csproj @@ -0,0 +1,32 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ObjectPrinting/Configs/PropertyPrintingConfig.cs b/ObjectPrinting/Configs/PropertyPrintingConfig.cs new file mode 100644 index 000000000..7823283a6 --- /dev/null +++ b/ObjectPrinting/Configs/PropertyPrintingConfig.cs @@ -0,0 +1,30 @@ +using System; +using System.Linq.Expressions; +using ObjectPrinting.Extensions; + +namespace ObjectPrinting.Configs; + +public class PropertyPrintingConfig( + PrintingConfig parent, + Expression> selector) +{ + private readonly string propertyName = PrintingConfigExtensions.GetPropertyName(selector); + + public PrintingConfig Using(Func serializer) + { + parent.PropertySerializers[propertyName] = + x => serializer((TProp)x); + + return parent; + } + + public PrintingConfig TrimmedToLength(int maxLen) + { + if (typeof(TProp) != typeof(string)) + throw new InvalidOperationException( + "TrimmedToLength is only allowed for string properties"); + + parent.StringTrimmingRules[propertyName] = maxLen; + return parent; + } +} \ No newline at end of file diff --git a/ObjectPrinting/Configs/TypePrintingConfig.cs b/ObjectPrinting/Configs/TypePrintingConfig.cs new file mode 100644 index 000000000..924a9bba3 --- /dev/null +++ b/ObjectPrinting/Configs/TypePrintingConfig.cs @@ -0,0 +1,22 @@ +using System; + +namespace ObjectPrinting.Configs; + +public class TypePrintingConfig(PrintingConfig parent) +{ + private PrintingConfig ParentConfig { get; } = parent; + + public PrintingConfig Using(Func serializer) + { + ParentConfig.TypeSerializers[typeof(TProp)] = x => serializer((TProp)x); + return ParentConfig; + } + + public PrintingConfig Using(IFormatProvider provider) + { + ParentConfig.TypeSerializers[typeof(TProp)] = x => + Convert.ToString(x, provider); + + return ParentConfig; + } +} \ No newline at end of file diff --git a/ObjectPrinting/Extensions/CollectionPrintingExtensions.cs b/ObjectPrinting/Extensions/CollectionPrintingExtensions.cs new file mode 100644 index 000000000..9e49e03f8 --- /dev/null +++ b/ObjectPrinting/Extensions/CollectionPrintingExtensions.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using System.Linq; +using System.Text; + +namespace ObjectPrinting.Extensions; + +public static class CollectionPrintingExtensions +{ + public static void PrintDictionary(this IDictionary dict, PrintingConfig config, + StringBuilder sb, int indent) + { + sb.AppendLine(GetTypeName(dict.GetType()) + " {"); + foreach (DictionaryEntry entry in dict) + { + sb.Append(new string('\t', indent + 1)); + sb.Append('['); + entry.Key.Print(config, sb, indent + 1); + sb.Append("] = "); + + if (entry.Value is null) + { + sb.AppendLine("null"); + } + else + { + entry.Value.Print(config, sb, indent + 1); + sb.AppendLine(); + } + } + + sb.Append(new string('\t', indent)); + sb.Append('}'); + } + + public static void PrintEnumerable(this IEnumerable enumerable, PrintingConfig config, + StringBuilder sb, int indent) + { + sb.AppendLine(GetTypeName(enumerable.GetType()) + " ["); + foreach (var item in enumerable) + { + sb.Append(new string('\t', indent + 1)); + if (item is null) + { + sb.AppendLine("null"); + } + else + { + item.Print(config, sb, indent + 1); + sb.AppendLine(); + } + } + + sb.Append(new string('\t', indent)); + sb.Append(']'); + } + + private static string GetTypeName(Type type) + { + if (!type.IsGenericType) + return type.Name; + + var name = type.Name; + var backtickIndex = name.IndexOf('`'); + if (backtickIndex > 0) + name = name[..backtickIndex]; + + var genericArgs = type.GetGenericArguments() + .Select(GetTypeName); + + return $"{name}<{string.Join(", ", genericArgs)}>"; + } +} \ No newline at end of file diff --git a/ObjectPrinting/Extensions/ObjectExtensions.cs b/ObjectPrinting/Extensions/ObjectExtensions.cs new file mode 100644 index 000000000..faa3dfa0f --- /dev/null +++ b/ObjectPrinting/Extensions/ObjectExtensions.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ObjectPrinting.Strategies; + +namespace ObjectPrinting.Extensions; + +public static class ObjectExtensions +{ + private static readonly List Strategies = new() + { + new ExcludedTypeStrategy(), + new TypeSerializerStrategy(), + new DictionaryStrategy(), + new EnumerableStrategy(), + new SimpleTypeStrategy(), + new DefaultObjectStrategy() + }; + + public static void Print(this object obj, PrintingConfig config, + StringBuilder sb, int indent) + { + if (obj is null) + { + sb.Append("null"); + return; + } + + if (!config.Visited.Add(obj)) + { + sb.Append(""); + return; + } + + var type = obj.GetType(); + + foreach (var strategy in Strategies.Where(strategy => strategy.CanPrint(obj, type, config))) + { + strategy.Print(obj, type, config, sb, indent); + return; + } + } +} \ No newline at end of file diff --git a/ObjectPrinting/Extensions/PrintingConfigExtensions.cs b/ObjectPrinting/Extensions/PrintingConfigExtensions.cs new file mode 100644 index 000000000..7158c0f80 --- /dev/null +++ b/ObjectPrinting/Extensions/PrintingConfigExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Linq.Expressions; +using System.Text; + +namespace ObjectPrinting.Extensions; + +public static class PrintingConfigExtensions +{ + internal static string GetPropertyName(Expression> selector) + { + return selector.Body switch + { + MemberExpression m => m.Member.Name, + UnaryExpression { Operand: MemberExpression m2 } => m2.Member.Name, + _ => throw new ArgumentException("Expression must be a property") + }; + } + + public static string PrintToString(this PrintingConfig printingConfig, object obj) + { + var sb = new StringBuilder(); + printingConfig.Visited.Clear(); + obj.Print(printingConfig, sb, 0); + return sb.ToString(); + } +} \ No newline at end of file diff --git a/ObjectPrinting/IPrintStrategy.cs b/ObjectPrinting/IPrintStrategy.cs new file mode 100644 index 000000000..63c25d955 --- /dev/null +++ b/ObjectPrinting/IPrintStrategy.cs @@ -0,0 +1,11 @@ +using System; +using System.Text; + +namespace ObjectPrinting; + +public interface IPrintStrategy +{ + bool CanPrint(object obj, Type type, PrintingConfig config); + void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent); +} \ No newline at end of file diff --git a/ObjectPrinting/ObjectPrinter.cs b/ObjectPrinting/ObjectPrinter.cs index 3c7867c32..4fab11a8b 100644 --- a/ObjectPrinting/ObjectPrinter.cs +++ b/ObjectPrinting/ObjectPrinter.cs @@ -1,10 +1,18 @@ -namespace ObjectPrinting +using ObjectPrinting.Extensions; + +namespace ObjectPrinting; + +public static class ObjectPrinter { - public class ObjectPrinter + public static PrintingConfig For() { - public static PrintingConfig For() - { - return new PrintingConfig(); - } + return new PrintingConfig(); } -} \ No newline at end of file + + /// Синтаксический сахар, вызывает сериализацию обьекта с дефолтным конфигом + public static string PrintToString(object? obj) + { + var printer = new PrintingConfig(); + return printer.PrintToString((TOwner)obj); + } +} diff --git a/ObjectPrinting/ObjectPrinting.csproj b/ObjectPrinting/ObjectPrinting.csproj index c5db392ff..ea98111e3 100644 --- a/ObjectPrinting/ObjectPrinting.csproj +++ b/ObjectPrinting/ObjectPrinting.csproj @@ -5,6 +5,7 @@ + diff --git a/ObjectPrinting/PrintingConfig.cs b/ObjectPrinting/PrintingConfig.cs index a9e082117..702d9d864 100644 --- a/ObjectPrinting/PrintingConfig.cs +++ b/ObjectPrinting/PrintingConfig.cs @@ -1,41 +1,40 @@ using System; -using System.Linq; -using System.Text; +using System.Collections.Generic; +using System.Linq.Expressions; +using ObjectPrinting.Configs; +using ObjectPrinting.Extensions; -namespace ObjectPrinting +namespace ObjectPrinting; + +public class PrintingConfig { - public class PrintingConfig - { - public string PrintToString(TOwner obj) - { - return PrintToString(obj, 0); - } + public Dictionary> TypeSerializers { get; } = new(); + public Dictionary> PropertySerializers { get; } = new(); + public HashSet ExcludedTypes { get; } = []; + public HashSet ExcludedProperties { get; } = []; + + public readonly HashSet Visited = []; + public Dictionary StringTrimmingRules { get; } = new(); - private string PrintToString(object obj, int nestingLevel) - { - //TODO apply configurations - if (obj == null) - return "null" + Environment.NewLine; + public PrintingConfig Excluding() + { + ExcludedTypes.Add(typeof(TProp)); + return this; + } + public PrintingConfig Excluding(Expression> selector) + { + ExcludedProperties.Add(PrintingConfigExtensions.GetPropertyName(selector)); + return this; + } - var finalTypes = new[] - { - typeof(int), typeof(double), typeof(float), typeof(string), - typeof(DateTime), typeof(TimeSpan) - }; - if (finalTypes.Contains(obj.GetType())) - return obj + Environment.NewLine; + public TypePrintingConfig Printing() + { + return new TypePrintingConfig(this); + } - 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(); - } + public PropertyPrintingConfig Printing( + Expression> selector) + { + return new PropertyPrintingConfig(this, selector); } } \ No newline at end of file diff --git a/ObjectPrinting/Solved/ObjectExtensions.cs b/ObjectPrinting/Solved/ObjectExtensions.cs deleted file mode 100644 index b0c94553c..000000000 --- a/ObjectPrinting/Solved/ObjectExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ObjectPrinting.Solved -{ - public static class ObjectExtensions - { - public static string PrintToString(this T obj) - { - return ObjectPrinter.For().PrintToString(obj); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/ObjectPrinter.cs b/ObjectPrinting/Solved/ObjectPrinter.cs deleted file mode 100644 index 540ee769c..000000000 --- a/ObjectPrinting/Solved/ObjectPrinter.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ObjectPrinting.Solved -{ - public class ObjectPrinter - { - public static PrintingConfig For() - { - return new PrintingConfig(); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/PrintingConfig.cs b/ObjectPrinting/Solved/PrintingConfig.cs deleted file mode 100644 index 0ec5aeb2b..000000000 --- a/ObjectPrinting/Solved/PrintingConfig.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Text; - -namespace ObjectPrinting.Solved -{ - public class PrintingConfig - { - public PropertyPrintingConfig Printing() - { - return new PropertyPrintingConfig(this); - } - - public PropertyPrintingConfig Printing(Expression> memberSelector) - { - return new PropertyPrintingConfig(this); - } - - public PrintingConfig Excluding(Expression> memberSelector) - { - return this; - } - - internal PrintingConfig Excluding() - { - return this; - } - - 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(); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/PropertyPrintingConfig.cs b/ObjectPrinting/Solved/PropertyPrintingConfig.cs deleted file mode 100644 index a509697d1..000000000 --- a/ObjectPrinting/Solved/PropertyPrintingConfig.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Globalization; - -namespace ObjectPrinting.Solved -{ - public class PropertyPrintingConfig : IPropertyPrintingConfig - { - private readonly PrintingConfig printingConfig; - - public PropertyPrintingConfig(PrintingConfig printingConfig) - { - this.printingConfig = printingConfig; - } - - public PrintingConfig Using(Func print) - { - return printingConfig; - } - - public PrintingConfig Using(CultureInfo culture) - { - return printingConfig; - } - - PrintingConfig IPropertyPrintingConfig.ParentConfig => printingConfig; - } - - public interface IPropertyPrintingConfig - { - PrintingConfig ParentConfig { get; } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs b/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs deleted file mode 100644 index dd3922394..000000000 --- a/ObjectPrinting/Solved/PropertyPrintingConfigExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace ObjectPrinting.Solved -{ - public static class PropertyPrintingConfigExtensions - { - public static string PrintToString(this T obj, Func, PrintingConfig> config) - { - return config(ObjectPrinter.For()).PrintToString(obj); - } - - public static PrintingConfig TrimmedToLength(this PropertyPrintingConfig propConfig, int maxLen) - { - return ((IPropertyPrintingConfig)propConfig).ParentConfig; - } - - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs deleted file mode 100644 index ac52d5ee5..000000000 --- a/ObjectPrinting/Solved/Tests/ObjectPrinterAcceptanceTests.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Globalization; -using NUnit.Framework; - -namespace ObjectPrinting.Solved.Tests -{ - [TestFixture] - public class ObjectPrinterAcceptanceTests - { - [Test] - public void Demo() - { - var person = new Person { Name = "Alex", Age = 19 }; - - var printer = ObjectPrinter.For() - //1. Исключить из сериализации свойства определенного типа - .Excluding() - //2. Указать альтернативный способ сериализации для определенного типа - .Printing().Using(i => i.ToString("X")) - //3. Для числовых типов указать культуру - .Printing().Using(CultureInfo.InvariantCulture) - //4. Настроить сериализацию конкретного свойства - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - .Printing(p => p.Name).TrimmedToLength(10) - //6. Исключить из сериализации конкретного свойства - .Excluding(p => p.Age); - - string s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - string s2 = person.PrintToString(); - - //8. ...с конфигурированием - string s3 = person.PrintToString(s => s.Excluding(p => p.Age)); - Console.WriteLine(s1); - Console.WriteLine(s2); - Console.WriteLine(s3); - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Solved/Tests/Person.cs b/ObjectPrinting/Solved/Tests/Person.cs deleted file mode 100644 index 858ebbf8d..000000000 --- a/ObjectPrinting/Solved/Tests/Person.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ObjectPrinting.Solved.Tests -{ - public class Person - { - public Guid Id { get; set; } - public string Name { get; set; } - public double Height { get; set; } - public int Age { get; set; } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Strategies/DefaultObjectStrategy.cs b/ObjectPrinting/Strategies/DefaultObjectStrategy.cs new file mode 100644 index 000000000..3588f6908 --- /dev/null +++ b/ObjectPrinting/Strategies/DefaultObjectStrategy.cs @@ -0,0 +1,106 @@ +using System; +using System.Reflection; +using System.Text; +using ObjectPrinting.Extensions; + +namespace ObjectPrinting.Strategies; + +public class DefaultObjectStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => true; + + public void Print(object obj, Type type, + PrintingConfig config, StringBuilder sb, int indent) + { + sb.AppendLine(type.Name + ":"); + + foreach (var prop in type.GetProperties()) + { + if (ShouldSkipProperty(prop, config)) + continue; + + PrintProperty(obj, prop, config, sb, indent); + } + } + + #region Helpers + + private static bool ShouldSkipProperty( + PropertyInfo prop, + PrintingConfig config) + { + var name = prop.Name; + var type = prop.PropertyType; + + return config.ExcludedProperties.Contains(name) + || config.ExcludedTypes.Contains(type); + } + + private static void PrintProperty(object obj, PropertyInfo prop, + PrintingConfig config, StringBuilder sb, int indent) + { + var name = prop.Name; + var value = prop.GetValue(obj); + + PrintPropertyHeader(sb, indent, name); + + if (TryPrintWithCustomSerializer(name, value, config, sb)) return; + if (TryPrintStringTrimmed(name, value, config, sb)) return; + if (TryPrintNull(value, sb)) return; + + PrintNestedObject(value, config, sb, indent + 1); + } + + private static void PrintPropertyHeader(StringBuilder sb, int indent, string name) + { + sb.Append(new string('\t', indent + 1)); + sb.Append(name); + sb.Append(" = "); + } + + private static bool TryPrintWithCustomSerializer( + string name, + object? value, + PrintingConfig config, + StringBuilder sb) + { + if (!config.PropertySerializers.TryGetValue(name, out var serializer)) + return false; + + sb.AppendLine(value != null ? serializer(value) : "null"); + return true; + } + + private static bool TryPrintStringTrimmed(string name, object? value, + PrintingConfig config, StringBuilder sb) + { + if (value is not string s || + !config.StringTrimmingRules.TryGetValue(name, out var max)) + return false; + + sb.AppendLine(s.Length <= max ? s : s[..max]); + return true; + } + + private static bool TryPrintNull(object? value, StringBuilder sb) + { + if (value is not null) + return false; + + sb.AppendLine("null"); + return true; + } + + private static void PrintNestedObject( + object? value, + PrintingConfig config, + StringBuilder sb, + int indent) + { + value.Print(config, sb, indent); + sb.AppendLine(); + } + + #endregion +} diff --git a/ObjectPrinting/Strategies/DictionaryStrategy.cs b/ObjectPrinting/Strategies/DictionaryStrategy.cs new file mode 100644 index 000000000..5b6b4c239 --- /dev/null +++ b/ObjectPrinting/Strategies/DictionaryStrategy.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections; +using System.Text; +using ObjectPrinting.Extensions; + +namespace ObjectPrinting.Strategies; + +public class DictionaryStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => obj is IDictionary; + + public void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent) + { + ((IDictionary)obj).PrintDictionary(config, sb, indent); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Strategies/EnumerableStrategy.cs b/ObjectPrinting/Strategies/EnumerableStrategy.cs new file mode 100644 index 000000000..2b7e4492b --- /dev/null +++ b/ObjectPrinting/Strategies/EnumerableStrategy.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections; +using System.Text; +using ObjectPrinting.Extensions; + +namespace ObjectPrinting.Strategies; + +public class EnumerableStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => obj is IEnumerable && obj is not string; + + public void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent) + { + ((IEnumerable)obj).PrintEnumerable(config, sb, indent); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Strategies/ExcludedTypeStrategy.cs b/ObjectPrinting/Strategies/ExcludedTypeStrategy.cs new file mode 100644 index 000000000..47784b5c9 --- /dev/null +++ b/ObjectPrinting/Strategies/ExcludedTypeStrategy.cs @@ -0,0 +1,15 @@ +using System; +using System.Text; + +namespace ObjectPrinting.Strategies; + +public class ExcludedTypeStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => config.ExcludedTypes.Contains(type); + + public void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent) + { + } +} \ No newline at end of file diff --git a/ObjectPrinting/Strategies/SimpleTypeStrategy.cs b/ObjectPrinting/Strategies/SimpleTypeStrategy.cs new file mode 100644 index 000000000..8f911ce02 --- /dev/null +++ b/ObjectPrinting/Strategies/SimpleTypeStrategy.cs @@ -0,0 +1,16 @@ +using System; +using System.Text; + +namespace ObjectPrinting.Strategies; + +public class SimpleTypeStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => type.IsPrimitive || obj is string || type.GetProperties().Length == 0; + + public void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent) + { + sb.Append(obj); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Strategies/TypeSerializerStrategy.cs b/ObjectPrinting/Strategies/TypeSerializerStrategy.cs new file mode 100644 index 000000000..7703a82b0 --- /dev/null +++ b/ObjectPrinting/Strategies/TypeSerializerStrategy.cs @@ -0,0 +1,17 @@ +using System; +using System.Text; + +namespace ObjectPrinting.Strategies; + +public class TypeSerializerStrategy : IPrintStrategy +{ + public bool CanPrint(object obj, Type type, PrintingConfig config) + => config.TypeSerializers.ContainsKey(type); + + public void Print(object obj, Type type, PrintingConfig config, + StringBuilder sb, int indent) + { + var serializer = config.TypeSerializers[type]; + sb.Append(serializer(obj)); + } +} \ No newline at end of file diff --git a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs b/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs deleted file mode 100644 index 4c8b2445c..000000000 --- a/ObjectPrinting/Tests/ObjectPrinterAcceptanceTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NUnit.Framework; - -namespace ObjectPrinting.Tests -{ - [TestFixture] - public class ObjectPrinterAcceptanceTests - { - [Test] - public void Demo() - { - var person = new Person { Name = "Alex", Age = 19 }; - - var printer = ObjectPrinter.For(); - //1. Исключить из сериализации свойства определенного типа - //2. Указать альтернативный способ сериализации для определенного типа - //3. Для числовых типов указать культуру - //4. Настроить сериализацию конкретного свойства - //5. Настроить обрезание строковых свойств (метод должен быть виден только для строковых свойств) - //6. Исключить из сериализации конкретного свойства - - string s1 = printer.PrintToString(person); - - //7. Синтаксический сахар в виде метода расширения, сериализующего по-умолчанию - //8. ...с конфигурированием - } - } -} \ No newline at end of file diff --git a/ObjectPrinting/Tests/Person.cs b/ObjectPrinting/Tests/Person.cs deleted file mode 100644 index f95559554..000000000 --- a/ObjectPrinting/Tests/Person.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ObjectPrinting.Tests -{ - public class Person - { - public Guid Id { get; set; } - public string Name { get; set; } - public double Height { get; set; } - public int Age { get; set; } - } -} \ No newline at end of file diff --git a/fluent-api.sln b/fluent-api.sln index 69c8db9ed..9ce96dc6a 100644 --- a/fluent-api.sln +++ b/fluent-api.sln @@ -6,6 +6,9 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrinting", "ObjectPrinting\ObjectPrinting.csproj", "{07B8C9B7-8289-46CB-9875-048A57758EEE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{6D308E4A-CEC7-4536-9B87-81CD337A87AD}" + ProjectSection(SolutionItems) = preProject + Samples\.gitignore = Samples\.gitignore + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping", "Samples\FluentMapper\FluentMapping.csproj", "{FEEA5AFE-459A-4D13-81D0-252E1A2E6F4E}" EndProject @@ -13,6 +16,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentMapping.Tests", "Samp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectacle", "Samples\Spectacle\Spectacle.csproj", "{EFA9335C-411B-4597-B0B6-5438D1AE04C3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectPrinterTests", "ObjectPrinterTests\ObjectPrinterTests.csproj", "{4C8905B7-BFC8-4E4A-9DA1-B24435405856}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -35,6 +40,10 @@ Global {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFA9335C-411B-4597-B0B6-5438D1AE04C3}.Release|Any CPU.Build.0 = Release|Any CPU + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C8905B7-BFC8-4E4A-9DA1-B24435405856}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/fluent-api.sln.DotSettings b/fluent-api.sln.DotSettings index 135b83ecb..229f449d2 100644 --- a/fluent-api.sln.DotSettings +++ b/fluent-api.sln.DotSettings @@ -1,6 +1,9 @@  <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> + True True True Imported 10.10.2016