Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added support for property validation in-addition to the prop's type …

…validation.
  • Loading branch information...
commit 4845864aba768831abeef1e10b490b85279f32d1 1 parent 816affd
@adrianaisemberg adrianaisemberg authored
View
74 CLAP/TypeValidator.cs
@@ -1,4 +1,5 @@
-using System.Linq;
+using System.Diagnostics;
+using System.Linq;
using System.Reflection;
using CLAP.Validation;
@@ -8,27 +9,26 @@ internal static class TypeValidator
{
public static void Validate(object obj)
{
- if (obj == null ||
- obj is string)
+ if (Ignore(obj))
{
return;
}
- var type = obj.GetType();
+ Debug.Assert(obj != null);
- if (type.IsArray ||
- type.IsEnum ||
- type.Assembly.GlobalAssemblyCache)
- {
- return;
- }
+ var type = obj.GetType();
+ // type's validators:
+ // first use all the validators defined over this type
+ //
var validators = type.
GetAttributes<ValidateAttribute>().
Cast<IValidation>().
Select(a => a.GetValidator());
- var propsAndValues = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).
+ var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+
+ var propsAndValues = properties.
Select(p => new ValueInfo(p.Name, p.PropertyType, p.GetValue(obj, null))).
ToArray();
@@ -37,12 +37,62 @@ public static void Validate(object obj)
validator.Validate(propsAndValues);
}
- // recursion
+ // property validators:
+ // validate each property value, in case property is a custom class
+ //
+ foreach (var property in properties)
+ {
+ var value = property.GetValue(obj, null);
+
+ if (Ignore(value))
+ {
+ continue;
+ }
+
+ var propertyValidators = property.
+ GetAttributes<ValidateAttribute>().
+ Cast<IValidation>().
+ Select(a => a.GetValidator());
+
+ foreach (var propertyValidator in propertyValidators)
+ {
+ var propertyPropsAndValues = value.GetType().
+ GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).
+ Select(p => new ValueInfo(p.Name, p.PropertyType, p.GetValue(value, null))).
+ ToArray();
+
+ propertyValidator.Validate(propertyPropsAndValues);
+ }
+ }
+
+ // recursion:
+ // validate all values, in case their type has additional validation
//
foreach (var value in propsAndValues.Select(p => p.Value))
{
Validate(value);
}
}
+
+ private static bool Ignore(object obj)
+ {
+ if (obj == null ||
+ obj is string)
+ {
+ return true;
+ }
+
+ var type = obj.GetType();
+
+ if (type.IsArray ||
+ type.IsEnum ||
+ type.Assembly.GlobalAssemblyCache)
+ {
+ return true;
+ }
+
+
+ return false;
+ }
}
}
View
12 CLAP/Utils.cs
@@ -36,6 +36,13 @@ public static string FormatWith(this string format, params object[] args)
return atts;
}
+ public static IEnumerable<T> GetAttributes<T>(this PropertyInfo property) where T : Attribute
+ {
+ var atts = Attribute.GetCustomAttributes(property, typeof(T)).Cast<T>();
+
+ return atts;
+ }
+
public static IEnumerable<T> GetInterfaceAttributes<T>(this MethodInfo method)
{
return method.GetCustomAttributes(true).
@@ -55,11 +62,6 @@ public static IEnumerable<T> GetInterfaceAttributes<T>(this MethodInfo method)
return Attribute.IsDefined(method, typeof(T));
}
- public static bool HasAttribute<T>(this Type type) where T : Attribute
- {
- return Attribute.IsDefined(type, typeof(T));
- }
-
public static bool HasAttribute<T>(this ParameterInfo parameter) where T : Attribute
{
return Attribute.IsDefined(parameter, typeof(T));
View
2  CLAP/Validation/ValidateAttribute.cs
@@ -10,7 +10,7 @@ namespace CLAP.Validation
/// For full documentation, see MSDN:
/// http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression.aspx
/// </remarks>
- [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)]
public sealed class ValidateAttribute : Attribute, IValidation
{
#region Properties
View
14 Tests/Samples.cs
@@ -770,6 +770,11 @@ public void Val(MyValidatedType t)
public void Complex(MyComplexType t)
{
}
+
+ [Verb]
+ public void Props(MyComplexType_2 t)
+ {
+ }
}
public enum Case
@@ -799,4 +804,13 @@ public class MyComplexType
public string Name { get; set; }
public MyValidatedType Validated { get; set; }
}
+
+ public class MyComplexType_2
+ {
+ public int Number { get; set; }
+ public string Name { get; set; }
+
+ [Validate("Number < 50 AND Name IN ('foo','bar')")]
+ public MyValidatedType Validated { get; set; }
+ }
}
View
25 Tests/Tests.cs
@@ -1796,5 +1796,30 @@ public void ComplexGraphType_WithTypeValidation_Pass()
"-t:{Number: 40, Name: 'bar', Validated: { Number: 100, Name: 'barfoo' }}",
}, s);
}
+
+ [Test]
+ [ExpectedException(typeof(ValidationException))]
+ public void ComplexGraphType_WithPropertyValidation_Fail()
+ {
+ var s = new Sample_42();
+
+ Parser.Run(new[]
+ {
+ "props",
+ "-t:{Number: 40, Name: 'foobar', Validated: { Number: 100, Name: 'blah' }}",
+ }, s);
+ }
+
+ [Test]
+ public void ComplexGraphType_WithPropertyValidation_Pass()
+ {
+ var s = new Sample_42();
+
+ Parser.Run(new[]
+ {
+ "props",
+ "-t:{Number: 40000, Name: 'something', Validated: { Number: 40, Name: 'foo' }}",
+ }, s);
+ }
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.