Commit 86cf5ecf authored by Michael Herndon's avatar Michael Herndon

PROTO: flex yaml extension

 - add yaml flex extension project.
 - prototype a writer/reader for yaml.
 - fix various reflection API issues, including a stack overflow.
 - start with Writer texts. Adding VisitValue tests.
 - test various data types.
 - test properties with attributse such as [EncryptAttribute]
parent 4ddef404
......@@ -28,6 +28,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NerdyMishka.Akashic.Tests",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NerdyMishka.Akashic.Migrations", "src\Data\Akashic.Migrations\NerdyMishka.Akashic.Migrations.csproj", "{AE8D86C2-451F-4505-AC56-1703F0BFB123}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{8CEB02B9-A9A9-4660-A023-41E357B52DFA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NerdyMishka.Extensions.Proto.Flex.Tests", "test\Extensions\Proto.Flex\NerdyMishka.Extensions.Proto.Flex.Tests.csproj", "{6DC5653A-F974-4A10-AAC4-8AA5AC3C6293}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -58,6 +62,10 @@ Global
{AE8D86C2-451F-4505-AC56-1703F0BFB123}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE8D86C2-451F-4505-AC56-1703F0BFB123}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE8D86C2-451F-4505-AC56-1703F0BFB123}.Release|Any CPU.Build.0 = Release|Any CPU
{6DC5653A-F974-4A10-AAC4-8AA5AC3C6293}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6DC5653A-F974-4A10-AAC4-8AA5AC3C6293}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6DC5653A-F974-4A10-AAC4-8AA5AC3C6293}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6DC5653A-F974-4A10-AAC4-8AA5AC3C6293}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -74,6 +82,8 @@ Global
{A55DFF34-F557-4204-A4AD-F99F546C0A79} = {DA32C16D-7292-4EEC-90C5-59EC66CC018D}
{A3622B10-92F5-4853-A36F-942673D0BBE1} = {A55DFF34-F557-4204-A4AD-F99F546C0A79}
{AE8D86C2-451F-4505-AC56-1703F0BFB123} = {7705CFA1-C4EB-4D6B-A9EE-4F782C589FD7}
{8CEB02B9-A9A9-4660-A023-41E357B52DFA} = {DA32C16D-7292-4EEC-90C5-59EC66CC018D}
{6DC5653A-F974-4A10-AAC4-8AA5AC3C6293} = {8CEB02B9-A9A9-4660-A023-41E357B52DFA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {16666A01-3CC0-4111-8A58-9A6CA34CD76C}
......
......@@ -60,18 +60,9 @@ namespace NerdyMishka.ComponentModel.DataAnnotations
public override object ConvertFrom(object value)
{
if(value == null)
if(value == null)
return null;
bool x = (bool)value;
return x == true ? this.Yes[0] : this.No[0];
}
public override object ConvertTo(object value)
{
if(value == null)
return null;
string v = (string)value;
......@@ -82,6 +73,16 @@ namespace NerdyMishka.ComponentModel.DataAnnotations
return this.Yes.Any(o => o.ToLowerInvariant() == v);
}
public override object ConvertTo(object value)
{
if(value == null)
return null;
bool x = (bool)value;
return x == true ? this.Yes[0] : this.No[0];
}
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using NerdyMishka.ComponentModel.ValueConversion;
namespace NerdyMishka.ComponentModel.DataAnnotations
......@@ -10,6 +12,15 @@ namespace NerdyMishka.ComponentModel.DataAnnotations
{
public string Encoding { get; set; }
public static List<ValueConverter> Converters { get; } =
new List<ValueConversion.ValueConverter>() {
new StringEncryptionConverter(),
new ByteEncryptionConverter(),
new SecureStringEncryptionConverter(),
new StringToBytesEncryptionConverter(),
new BytesToStringEncryptionConverter(),
};
public EncryptAttribute(Type valueConverter = null, string encoding = null) :base(valueConverter)
{
this.Encoding = encoding ?? "UTF-8";
......
......@@ -38,14 +38,14 @@ namespace NerdyMishka.ComponentModel.DataAnnotations
.LoadProperties(true).Properties;
var names = converterProps.Select(o => o.Name).ToList();
foreach(var prop in attrProps)
foreach(var attrProp in attrProps)
{
if(names.Contains(prop.Name))
if(names.Contains(attrProp.Name))
{
var setter = converterProps.FirstOrDefault(o => o.Name == prop.Name);
if(setter.PropertyInfo.PropertyType == prop.PropertyInfo.PropertyType)
var setter = converterProps.FirstOrDefault(o => o.Name == attrProp.Name);
if(setter.PropertyInfo.PropertyType == attrProp.PropertyInfo.PropertyType)
{
var value = prop.GetValue(obj);
var value = attrProp.GetValue(this);
setter.SetValue(obj, value);
}
continue;
......
using System;
using System.Collections.Generic;
using System.Linq;
using NerdyMishka.ComponentModel.ValueConversion;
using NerdyMishka.Reflection;
using NerdyMishka.Reflection.Extensions;
namespace NerdyMishka.ComponentModel.DataAnnotations
{
public static class Extensions
{
public static IReadOnlyCollection<ValueConverter> GetValueConverters(this IProperty property, Type convertTo = null)
{
if(convertTo == null)
convertTo = property.ClrType;
var converters = property.GetMetadata<List<ValueConverter>>(
"ValueConverters:" + convertTo.FullName);
if(converters != null)
return converters;
var attributes = property.Attributes
.Where(o => o is IValueConverterAttribute)
.ToList();
foreach(var attr in attributes)
((IValueConverterAttribute)attr).Initialize();
var encryption = attributes.FirstOrDefault(o => o is EncryptAttribute);
if(encryption != null && ((EncryptAttribute)encryption).Instance == null)
{
var clrType = property.ClrType;
var propertyType = property.ClrType.AsTypeInfo();
if(propertyType.IsNullableOfT())
clrType = propertyType?.UnderlyingType?.ClrType ?? clrType;
var attr = (EncryptAttribute)encryption;
var converter = EncryptAttribute.Converters.FirstOrDefault(
o => o.CanConvertFrom(clrType) && o.CanConvertTo(convertTo));
if(converter != null)
attr.Instance = converter;
}
converters = attributes
.Cast<IValueConverterAttribute>()
.Where(o => o.Instance != null)
.Select(o => o.Instance)
.ToList();
property.SetMetadata("ValueConverters:" + convertTo.FullName, converters);
return converters;
}
}
}
\ No newline at end of file
......@@ -10,6 +10,8 @@ using NerdyMishka.Text;
namespace NerdyMishka.ComponentModel.ValueConversion
{
public class SecureStringEncryptionConverter : ValueEncryptionConverter<SecureString, string>
{
private Encoding encoding;
......@@ -21,19 +23,10 @@ namespace NerdyMishka.ComponentModel.ValueConversion
this.encoding = encoding ?? Encodings.Utf8NoBom;
}
// string
public override object ConvertFrom(object value)
{
if(value == null)
return null;
var ss = (SecureString)value;
var bytes = ss.ToBytes(this.encoding);
return this.EncryptString((byte[])bytes);
}
// byte
public override object ConvertTo(object value)
public override object ConvertFrom(object value)
{
if(value == null)
return null;
......@@ -50,6 +43,17 @@ namespace NerdyMishka.ComponentModel.ValueConversion
return ss;
}
// string
public override object ConvertTo(object value)
{
if(value == null)
return null;
var ss = (SecureString)value;
var bytes = ss.ToBytes(this.encoding);
return this.EncryptString((byte[])bytes);
}
}
public class StringEncryptionConverter : ValueEncryptionConverter<string, string>
......@@ -67,7 +71,12 @@ namespace NerdyMishka.ComponentModel.ValueConversion
if(value == null)
return null;
return this.EncryptString((string)value);
var str = value.ToString();
if(str == string.Empty)
return str;
return this.DecryptString(str);
}
// byte
......@@ -76,7 +85,12 @@ namespace NerdyMishka.ComponentModel.ValueConversion
if(value == null)
return null;
return this.DecryptString((string)value);
var str = value.ToString();
if(str == string.Empty)
return str;
return this.EncryptString((string)value);
}
}
......@@ -95,7 +109,11 @@ namespace NerdyMishka.ComponentModel.ValueConversion
if(value == null)
return null;
return this.Encrypt((string) value);
var bytes = (byte[])value;
if(bytes.Length == 0)
return string.Empty;
return this.DecryptString(bytes);
}
// byte
......@@ -104,7 +122,11 @@ namespace NerdyMishka.ComponentModel.ValueConversion
if(value == null)
return null;
return this.DecryptString((byte[])value);
var str = value.ToString();
if(str == string.Empty)
return Array.Empty<byte>();
return this.Encrypt(str);
}
}
......@@ -122,16 +144,24 @@ namespace NerdyMishka.ComponentModel.ValueConversion
if(value == null)
return null;
return this.EncryptString((byte[])value);
var str = value.ToString();
if(str == string.Empty)
return Array.Empty<byte>();
return this.Decrypt((string)value);
}
// byte
public override object ConvertTo(object value)
{
if(value == null)
if(value == null)
return null;
return this.Decrypt((string)value);
byte[] bytes = (byte[])value;
if(bytes.Length == 0)
return string.Empty;
return this.EncryptString(bytes);
}
}
......@@ -150,7 +180,11 @@ namespace NerdyMishka.ComponentModel.ValueConversion
if(value == null)
return null;
return this.Encrypt((byte[])value);
byte[] bytes = (byte[])value;
if(bytes.Length == 0)
return bytes;
return this.Decrypt((byte[])value);
}
// byte
......@@ -159,7 +193,11 @@ namespace NerdyMishka.ComponentModel.ValueConversion
if(value == null)
return null;
return this.Decrypt((byte[])value);
byte[] bytes = (byte[])value;
if(bytes.Length == 0)
return bytes;
return this.Encrypt((byte[])value);
}
}
......
......@@ -100,15 +100,15 @@ namespace NerdyMishka.ComponentModel.ValueConversion
public override object ConvertFrom(object value)
{
if(value == null)
return null;
return this.ComputeHashAsString((string)value);
return value;
}
public override object ConvertTo(object value)
{
return value;
if(value == null)
return null;
return this.ComputeHashAsString((string)value);
}
}
......
......@@ -6,6 +6,7 @@ namespace NerdyMishka
public static class EnumerableExtensions
{
public static bool EqualTo<T>(this IEnumerable<T> left, IEnumerable<T> right, IComparer<T> comparer)
{
......@@ -17,7 +18,7 @@ namespace NerdyMishka
var leftEnumerator = left.GetEnumerator();
var rightEnumerator = right.GetEnumerator();
while(true)
{
var lNext = leftEnumerator.MoveNext();
......
......@@ -10,6 +10,11 @@ namespace NerdyMishka.Reflection.Extensions
public static class TypeExtensions
{
public static IType AsTypeInfo(this Type clrType)
{
return ReflectionCache.GetOrAdd(clrType);
}
public static IItemType AsItemType(this IType type)
{
return type as IItemType;
......@@ -55,6 +60,7 @@ namespace NerdyMishka.Reflection.Extensions
type.SetFlag("Searched:IsNullable<>", true);
type.SetFlag("IsNullable<>", result);
return result;
}
......
......@@ -8,6 +8,7 @@ namespace NerdyMishka.Reflection
{
public interface IProperty : IReflectionMember
{
IType DeclaringType { get; }
PropertyInfo PropertyInfo { get; }
......
......@@ -11,9 +11,9 @@ namespace NerdyMishka.Reflection
IMethod CreateMethod(MethodInfo info, ParameterInfo[] parameters);
IProperty CreateProperty(PropertyInfo info);
IProperty CreateProperty(PropertyInfo info, IType declaringType = null);
IProperty CreateProperty(FieldInfo info);
IProperty CreateProperty(FieldInfo info, IType declaringType = null);
IType CreateType(Type info);
......
......@@ -33,9 +33,10 @@ namespace NerdyMishka.Reflection
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance);
IEnumerable<IProperty> GetProperties(
string name,
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance);
IMethod GetMethod(
string name,
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance,
......
......@@ -12,6 +12,11 @@ namespace NerdyMishka.Reflection
return new ReflectionPropertyMember(info);
}
public virtual IProperty BuildProperty(PropertyInfo info, IType declaringType)
{
return new ReflectionPropertyMember(info, declaringType);
}
public static Type[] DataTypes { get; set; } = new []{
typeof(string),
typeof(DateTime),
......@@ -39,19 +44,19 @@ namespace NerdyMishka.Reflection
return new ReflectionMethod(info, parameters, this);
}
public IProperty CreateProperty(PropertyInfo info)
public IProperty CreateProperty(PropertyInfo info, IType declaringType = null)
{
return new ReflectionPropertyMember(info);
return new ReflectionPropertyMember(info, declaringType);
}
public IProperty CreateProperty(FieldInfo info)
public IProperty CreateProperty(FieldInfo info, IType declaringType = null)
{
return new ReflectionPropertyMember(info);
return new ReflectionPropertyMember(info, declaringType);
}
public IType CreateType(Type info)
{
return ReflectionCache.GetOrAdd(info);
return new ReflectionType(info, this);
}
public IType CreateType(TypeInfo info)
......
......@@ -14,7 +14,7 @@ namespace NerdyMishka.Reflection
private Delegate getter;
private Delegate setter;
public ReflectionPropertyMember(PropertyInfo info)
public ReflectionPropertyMember(PropertyInfo info, IType delcaringType = null)
{
this.Name = info.Name;
this.PropertyInfo = info;
......@@ -24,9 +24,11 @@ namespace NerdyMishka.Reflection
this.IsPublic = info.GetMethod.IsPublic || info.SetMethod.IsPublic;
this.IsPrivate = !this.IsPublic;
this.IsInstance = !this.IsStatic;
this.ClrType = info.PropertyType;
this.DeclaringType = delcaringType ?? ReflectionCache.GetOrAdd(info.DeclaringType);
}
public ReflectionPropertyMember(FieldInfo info)
public ReflectionPropertyMember(FieldInfo info, IType declaringType = null)
{
this.FieldInfo = info;
this.IsField = true;
......@@ -34,8 +36,9 @@ namespace NerdyMishka.Reflection
this.CanWrite = true;
this.IsPublic = info.IsPublic;
this.IsPrivate = !info.IsPublic;
this.ClrType = info.FieldType;
this.IsStatic = info.IsStatic;
this.DeclaringType = declaringType ?? ReflectionCache.GetOrAdd(info.DeclaringType);
}
public bool CanRead { get; protected set; }
......@@ -58,6 +61,8 @@ namespace NerdyMishka.Reflection
public FieldInfo FieldInfo { get; protected set; }
public IType DeclaringType { get; protected set; }
public virtual object GetValue(object instance)
{
if(!this.CanRead)
......
......@@ -154,7 +154,7 @@ namespace NerdyMishka.Reflection
this.properties = new List<IProperty>();
PropertyInfo [] propertyInfos = this.ClrType.GetProperties(flags);
foreach(var pi in propertyInfos)
this.properties.Add(this.Factory.CreateProperty(pi));
this.properties.Add(this.Factory.CreateProperty(pi, this));
return this;
}
......@@ -167,15 +167,15 @@ namespace NerdyMishka.Reflection
.FirstOrDefault();
}
public IEnumerable<IProperty> GetProperties(
string name,
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance)
{
bool isStatic = flags.HasFlag(BindingFlags.Static);
bool isInstance = flags.HasFlag(BindingFlags.Instance);
bool isPublic = flags.HasFlag(BindingFlags.Public);
bool isPrivate = flags.HasFlag(BindingFlags.NonPublic);
bool isCaseInsenstive = flags.HasFlag(BindingFlags.IgnoreCase);
var query = this.Properties.AsQueryable();
......@@ -194,6 +194,18 @@ namespace NerdyMishka.Reflection
else
query = query.Where(o => o.IsPrivate);
}
return query;
}
public IEnumerable<IProperty> GetProperties(
string name,
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance)
{
bool isCaseInsenstive = flags.HasFlag(BindingFlags.IgnoreCase);
var query = this.GetProperties();
if(isCaseInsenstive)
{
......
using System.Text.RegularExpressions;
namespace NerdyMishka.Text
{
public static class StringExtensions
{
public static bool Match(this string left, string pattern, bool ignoreCase =true, bool isRegex = false)
{
if(!isRegex)
{
if(ignoreCase)
return string.Equals(left, pattern, System.StringComparison.OrdinalIgnoreCase);
return left == pattern;
}
var options = RegexOptions.None;
if(ignoreCase)
options = RegexOptions.IgnoreCase;
return Regex.IsMatch(left, pattern, options);
}
}
}
\ No newline at end of file
using NerdyMishka.Reflection;
using NerdyMishka.Reflection.Extensions;
internal static class Extensions
{
public static bool IsListLke(this IType typeInfo)
{
return typeInfo.IsArray() || typeInfo.IsICollectionOfT() || typeInfo.IsIListOfT() || typeInfo.IsICollection() || typeInfo.IsIList();
}
public static bool IsDictionaryLike(this IType typeInfo)
{
return typeInfo.IsIDictionaryOfKv() || typeInfo.IsIDictionary();
}
}
\ No newline at end of file
using System.Collections.Generic;
using NerdyMishka.ComponentModel.ValueConversion;
namespace NerdyMishka.Extensions.Flex
{
public interface IFlexSerializationSettings
{
bool OmitNulls { get; }
List<ValueConverter> ValueConverters { get; }
}
public interface IMutableFlexSerializationSettings
{
bool OmitNulls { get; set; }
List<ValueConverter> ValueConverters { get; set; }
}
public class FlexSerializationSettings : IFlexSerializationSettings, IMutableFlexSerializationSettings
{
public bool OmitNulls { get; set; }
public List<ValueConverter> ValueConverters { get; set; }
}
}
\ No newline at end of file
namespace NerdyMishka.Extensions.Flex
{
[System.Serializable]
public class MappingException : System.Exception
{
public MappingException() { }
public MappingException(string message) : base(message) { }
public MappingException(string message, System.Exception inner) : base(message, inner) { }
protected MappingException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
}
\ No newline at end of file
using System;
using System.Collections;
using YamlDotNet.RepresentationModel;
using NerdyMishka.Reflection;
using NerdyMishka.Reflection.Extensions;
using System.Collections.Generic;
using System.Linq;
using NerdyMishka.Text;
using NerdyMishka.ComponentModel.DataAnnotations;
using NerdyMishka.ComponentModel.ValueConversion;
namespace NerdyMishka.Extensions.Flex
{
public class YamlObjectWriter
{
private ITextTransform propertyTransform = new CamelCaseTransform();
private IFlexSerializationSettings settings;
public YamlObjectWriter(IFlexSerializationSettings settings = null)
{
this.settings = settings ?? new FlexSerializationSettings();
}
public YamlNode Visit<T>(T value)
{
return this.Visit(value, null, typeof(T).AsTypeInfo());
}
public YamlNode Visit(object value)
{
if(value == null)
{
if(this.settings.OmitNulls)
return null;
return new YamlScalarNode("null");
}
return this.Visit(value, null, value.GetType().AsTypeInfo());
}
public YamlNode Visit(object value, IProperty property, IType type)
{
if(property != null && type == null)
type = property.ClrType.AsTypeInfo();
if(type.IsDataType)
return this.VisitValue(value, property, type);