using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ComposableCore;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
namespace ComposableServices
{
/// <summary>
/// Validation extensions which
/// </summary>
public static class ValidationExt
{
/// <summary>
/// Combines a stateful source (combination of a source of T and a state object of a list of validationerrors) with a validationfunction and a validationerrormessage into a Ftype
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Source"></param>
/// <param name="F"></param>
/// <param name="sError"></param>
/// <returns></returns>
public static fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>,
Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>> fRule<T>(this Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> Source, Func<T, bool> F, String sError)
{
return fType.toFtype<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>, Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>>((x => Source.Rule(F, sError)));
}
/// <summary>
/// Wraps a function that excutes a validation and that puts the validation error on the state object of a stateful object.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Source"></param>
/// <param name="F"></param>
/// <param name="sError"></param>
/// <returns></returns>
public static Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> Rule<T>(this Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> Source, Func<T, bool> F, String sError)
{
return Source.Then((x =>
{
if (!F(x))
{
Source.State.setState(new ValidationError(sError));
}
return x;
}));
}
/// <summary>
/// Reads the validation attributes on the properties of a Type and store the returned fvalidation functions into a Ftype.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Source"></param>
/// <param name="obj"></param>
/// <param name="setCurrentValue">Call back function needed to replace the referenced object value at the time the Ftype was created with the current value of object that is validated.</param>
/// <returns></returns>
public static fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>, Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>> fValidationAttrToRule<T>(
this Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> Source, T obj, Func<T, PropertyInfo, Object, Object> setCurrentValue)
{
return fValidationAttrToRule<T>(Source, obj, new List<string>(), setCurrentValue);
}
/// <summary>
/// Reads the validation attributes on the properties of a Type and store the returned fvalidation functions into a Ftype. Exclude the validation attributes on properties listed in Excluded.
/// </summary>
/// <typeparam name="T">The type which is decorated with the validation attributes</typeparam>
/// <param name="Source"></param>
/// <param name="obj"></param>
/// <param name="Excludes"></param>
/// <param name="setCurrentValue">Call back function needed to replace the referenced object value at the time the Ftype was created with the current value of object that is validated.</param>
/// <returns></returns>
public static fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>, Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>> fValidationAttrToRule<T>(
this Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> Source, T obj, List<String> Excludes, Func<T, PropertyInfo, Object, Object> setCurrentValue)
{
if (Excludes == null) { Excludes = new List<string>(); }
Func<List<string>, string, bool> fExclude = ((e, i) => { return !e.Contains(i); });
fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>, Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>> retvalue = null;
System.Reflection.PropertyInfo[] props = obj.GetType().GetProperties();
Func<Object, T, string,
PropertyInfo,
Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>,
fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>,
Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>>> eval =
((v, tp, p, prpinfo, ctx) =>
{
fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>, Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>> fret;
fret = fType.toFtype<
Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>,
Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>>
(x =>
{
if (setCurrentValue != null)
{
//v is de value of the property at time of creation of this eval function
//get te real value for the current instance
v = setCurrentValue(tp, prpinfo, v);
}
//var s= Stateful.ToStateful(setInstance(x.Value), x.State);
ValidationContext context = new ValidationContext(tp, null, null) { MemberName = p };
List<ValidationResult> valrets = new List<ValidationResult>();
Validator.TryValidateProperty(v, context, valrets);
if (valrets.Count > 0)
{
valrets.ForEach((vr) => ctx.State.setState(new ValidationError(vr.ErrorMessage)));
}
return x;
});
if (retvalue == null)
{
retvalue = fret;
}
else
{
retvalue = retvalue.Then(fret);
}
return retvalue;
});
var qry = (from prop in props
where (prop.GetCustomAttributes(typeof(ValidationAttribute), false).Length > 0) && (fExclude(Excludes, prop.Name))
select eval((object)prop.GetValue(obj, null), obj, prop.Name, prop, Source)).ToList();
return retvalue;
}
/// <summary>
/// Wraps a source of T in a Stateful object of T combined with a fState object.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Source"></param>
/// <returns></returns>
public static Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> toValidationStateFul<T>(this T Source)
{
//The action delegates manage the state. These delegates are private to this method.
//The fState does'nt have any member variable to hold the state. This construct gives maximum protection.
fState<ValidationError, CloneableList<ValidationError>> mystate;
Action<CloneableList<ValidationError>, ValidationError> statesetter = ((L, I) => L.Add(I));
Action<CloneableList<ValidationError>> resetsetter = ((L) => L.Clear());
mystate = new fState<ValidationError, CloneableList<ValidationError>>(statesetter, new CloneableList<ValidationError>(), resetsetter);
return Source.ToStateful(mystate);
}
}
}