From 188b0da82842f9a57d161ae8eb41db167bebf827 Mon Sep 17 00:00:00 2001 From: ayende Date: Wed, 4 Apr 2007 07:14:40 +0000 Subject: [PATCH] Adding the following validators: - ValidateNotSameValue - ValidateCollectionNotEmpty - ValidateGroupNotEmpty git-svn-id: https://svn.castleproject.org/svn/castle/trunk@3673 73e77b4c-caa6-f847-a29a-24ab75ae54b6 --- .../CachedValidationRegistryTestCase.cs | 8 +- .../Castle.Components.Validator.Tests.csproj | 3 + .../Models/Client.cs | 2 + .../ValidatorRunnerTestCase.cs | 12 ++ .../CollectionNotEmptyValidatorTestCase.cs | 64 +++++++ .../GroupNotEmptyValidatorTestCase.cs | 103 ++++++++++++ .../NotSameValueValidatorTestCase.cs | 105 ++++++++++++ .../Attributes/AbstractValidationAttribute.cs | 10 ++ .../ValidateCollectionNotEmptyAttribute.cs | 36 ++++ .../Attributes/ValidateGroupNotEmptyAttribute.cs | 65 +++++++ .../Attributes/ValidateNonEmptyAttribute.cs | 5 +- .../Attributes/ValidateNotSameValueAttribute.cs | 89 ++++++++++ .../CachedValidationRegistry.cs | 48 +++--- .../Castle.Components.Validator.csproj | 6 + .../IValidatorBuilder.cs | 16 +- .../IValidatorRegistry.cs | 44 ++--- .../MessageConstants.cs | 4 + .../Messages.Designer.cs | 36 ++++ .../Castle.Components.Validator/Messages.resx | 14 +- .../Castle.Components.Validator/ValidatorRunner.cs | 13 +- .../Validators/AbstractValidator.cs | 4 +- .../Validators/CollectionNotEmptyValidator.cs | 63 +++++++ .../Validators/GroupNotEmptyValidator.cs | 187 +++++++++++++++++++++ .../Validators/NotSameValueValidator.cs | 70 ++++++++ 24 files changed, 945 insertions(+), 62 deletions(-) create mode 100644 Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/CollectionNotEmptyValidatorTestCase.cs create mode 100644 Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/GroupNotEmptyValidatorTestCase.cs create mode 100644 Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/NotSameValueValidatorTestCase.cs create mode 100644 Components/General/Validator/Castle.Components.Validator/Attributes/ValidateCollectionNotEmptyAttribute.cs create mode 100644 Components/General/Validator/Castle.Components.Validator/Attributes/ValidateGroupNotEmptyAttribute.cs create mode 100644 Components/General/Validator/Castle.Components.Validator/Attributes/ValidateNotSameValueAttribute.cs create mode 100644 Components/General/Validator/Castle.Components.Validator/Validators/CollectionNotEmptyValidator.cs create mode 100644 Components/General/Validator/Castle.Components.Validator/Validators/GroupNotEmptyValidator.cs create mode 100644 Components/General/Validator/Castle.Components.Validator/Validators/NotSameValueValidator.cs diff --git a/Components/General/Validator/Castle.Components.Validator.Tests/CachedValidationRegistryTestCase.cs b/Components/General/Validator/Castle.Components.Validator.Tests/CachedValidationRegistryTestCase.cs index aa3282e21..536eb4d98 100644 --- a/Components/General/Validator/Castle.Components.Validator.Tests/CachedValidationRegistryTestCase.cs +++ b/Components/General/Validator/Castle.Components.Validator.Tests/CachedValidationRegistryTestCase.cs @@ -31,10 +31,10 @@ namespace Castle.Components.Validator.Tests [Test] public void RunWhenEverytimeTest() { - IValidator[] validators = registry.GetValidators(typeof(Client), RunWhen.Everytime); + IValidator[] validators = registry.GetValidators(new ValidatorRunner(registry), typeof(Client), RunWhen.Everytime); Assert.IsNotNull(validators); - Assert.AreEqual(6, validators.Length); + Assert.AreEqual(8, validators.Length); foreach(IValidator val in validators) { @@ -45,10 +45,10 @@ namespace Castle.Components.Validator.Tests [Test] public void RunWhenCustomTest() { - IValidator[] validators = registry.GetValidators(typeof(Client), RunWhen.Custom); + IValidator[] validators = registry.GetValidators(new ValidatorRunner(registry), typeof(Client), RunWhen.Custom); Assert.IsNotNull(validators); - Assert.AreEqual(7, validators.Length); // RunWhen.Everytime is returned too + Assert.AreEqual(9, validators.Length); // RunWhen.Everytime is returned too foreach(IValidator val in validators) { diff --git a/Components/General/Validator/Castle.Components.Validator.Tests/Castle.Components.Validator.Tests.csproj b/Components/General/Validator/Castle.Components.Validator.Tests/Castle.Components.Validator.Tests.csproj index 578a5d0ac..8810c21d9 100644 --- a/Components/General/Validator/Castle.Components.Validator.Tests/Castle.Components.Validator.Tests.csproj +++ b/Components/General/Validator/Castle.Components.Validator.Tests/Castle.Components.Validator.Tests.csproj @@ -42,6 +42,9 @@ + + + diff --git a/Components/General/Validator/Castle.Components.Validator.Tests/Models/Client.cs b/Components/General/Validator/Castle.Components.Validator.Tests/Models/Client.cs index 421106541..d58b3ab70 100644 --- a/Components/General/Validator/Castle.Components.Validator.Tests/Models/Client.cs +++ b/Components/General/Validator/Castle.Components.Validator.Tests/Models/Client.cs @@ -30,6 +30,7 @@ namespace Castle.Components.Validator.Tests.Models this.confirmation = confirmation; } + [ValidateGroupNotEmpty("EmailOrPass")] [ValidateEmail] public string Email { @@ -37,6 +38,7 @@ namespace Castle.Components.Validator.Tests.Models set { email = value; } } + [ValidateGroupNotEmpty("EmailOrPass")] [ValidateLength(3, 12, "Invalid password length")] public string Password { diff --git a/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorRunnerTestCase.cs b/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorRunnerTestCase.cs index 3e9ff3035..647e3117b 100644 --- a/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorRunnerTestCase.cs +++ b/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorRunnerTestCase.cs @@ -66,5 +66,17 @@ namespace Castle.Components.Validator.Tests client = new Client(1, 27, "hammett", "100, street", "hammett@gmail.com", "123", "123"); Assert.IsTrue(runner.IsValid(client)); } + + [Test] + public void GroupValidation() + { + Client client = new Client(); + client.Email = "foo@bar.com"; + Assert.IsFalse(runner.IsValid(client)); + + Assert.AreEqual(0, runner.GetErrorSummary(client).GetErrorsForProperty("Email").Length); + Assert.AreEqual(0, runner.GetErrorSummary(client).GetErrorsForProperty("Password").Length); + + } } } diff --git a/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/CollectionNotEmptyValidatorTestCase.cs b/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/CollectionNotEmptyValidatorTestCase.cs new file mode 100644 index 000000000..09371b7a2 --- /dev/null +++ b/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/CollectionNotEmptyValidatorTestCase.cs @@ -0,0 +1,64 @@ +// Copyright 2004-2007 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; + +namespace Castle.Components.Validator.Tests.ValidatorTests +{ + using System.Globalization; + using System.Threading; + using NUnit.Framework; + + [TestFixture] + public class CollectionNotEmptyValidatorTestCase + { + private CollectionNotEmptyValidator validator; + private TestTarget target; + + [SetUp] + public void Init() + { + Thread.CurrentThread.CurrentCulture = + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); + + validator = new CollectionNotEmptyValidator(); + validator.Initialize(typeof(TestTarget).GetProperty("TargetField")); + target = new TestTarget(); + } + + [Test] + public void NullAndEmptyCollections() + { + Assert.IsFalse(validator.IsValid(target, null)); + Assert.IsFalse(validator.IsValid(target, new List())); + } + + [Test] + public void NonEmptyCollection() + { + Assert.IsTrue(validator.IsValid(target, "1,2,3".Split(',') ) ); + } + + public class TestTarget + { + private IList targetField = new List(); + + public IList TargetField + { + get { return targetField; } + set { targetField = value; } + } + } + } +} \ No newline at end of file diff --git a/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/GroupNotEmptyValidatorTestCase.cs b/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/GroupNotEmptyValidatorTestCase.cs new file mode 100644 index 000000000..b41f6885c --- /dev/null +++ b/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/GroupNotEmptyValidatorTestCase.cs @@ -0,0 +1,103 @@ +// Copyright 2004-2007 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.Components.Validator.Tests.ValidatorTests +{ + using System.Globalization; + using System.Threading; + using NUnit.Framework; + + [TestFixture] + public class GroupNotEmptyValidatorTestCase + { + private GroupNotEmptyValidator validator; + private TestTarget target; + + [SetUp] + public void Init() + { + Thread.CurrentThread.CurrentCulture = + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); + + validator = new GroupNotEmptyValidator("Dummy"); + validator.Initialize(typeof(TestTarget).GetProperty("Foo")); + validator.FriendlyName = "BAR"; + validator.Initialize(typeof(TestTarget).GetProperty("Bar")); + target = new TestTarget(); + } + + [Test] + public void BothEmptyStrings() + { + target.Bar = null; + target.Foo = ""; + Assert.IsFalse(validator.IsValid(target)); + } + + [Test] + public void BothNonEmptyStrings() + { + target.Bar = "Abva"; + target.Foo = "tdhf"; + Assert.IsTrue(validator.IsValid(target)); + } + + [Test] + public void FirstEmptySecondFull() + { + target.Bar = ""; + target.Foo = " "; + Assert.IsTrue(validator.IsValid(target)); + } + + + [Test] + public void FirstFullSecondEmpty() + { + target.Bar = "aa "; + target.Foo = ""; + Assert.IsTrue(validator.IsValid(target)); + } + + [Test] + public void ErrorMessage() + { + Assert.IsFalse(validator.IsValid(target)); + + Assert.IsTrue( + "At least one of the values in (Foo, BAR) should not be empty"== validator.ErrorMessage + || "At least one of the values in (BAR, Foo) should not be empty" == validator.ErrorMessage); + + } + + public class TestTarget + { + private string foo; + private string bar; + + + public string Bar + { + get { return bar; } + set { bar = value; } + } + + public string Foo + { + get { return foo; } + set { foo = value; } + } + } + } +} \ No newline at end of file diff --git a/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/NotSameValueValidatorTestCase.cs b/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/NotSameValueValidatorTestCase.cs new file mode 100644 index 000000000..baa30d3c1 --- /dev/null +++ b/Components/General/Validator/Castle.Components.Validator.Tests/ValidatorTests/NotSameValueValidatorTestCase.cs @@ -0,0 +1,105 @@ +// Copyright 2004-2007 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Castle.Components.Validator.Tests.ValidatorTests +{ + using System.Globalization; + using System.Threading; + using NUnit.Framework; + + [TestFixture] + public class NotSameValueValidatorTestCase + { + private NotSameValueValidator validator1, validator2, validator3; + private TestTarget target; + + [SetUp] + public void Init() + { + Thread.CurrentThread.CurrentCulture = + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); + + validator1 = new NotSameValueValidator("15"); + validator1.Initialize(typeof(TestTarget).GetProperty("TargetField1")); + + validator2 = new NotSameValueValidator(15); + validator2.Initialize(typeof(TestTarget).GetProperty("TargetField2")); + + ValidateNotSameValueAttribute attribute = new ValidateNotSameValueAttribute(typeof(Guid), Guid.Empty.ToString()); + validator3 = (NotSameValueValidator)attribute.Build(); + validator3.Initialize(typeof(TestTarget).GetProperty("TargetField3")); + + target = new TestTarget(); + } + + [Test] + public void InvalidForString() + { + Assert.IsFalse(validator1.IsValid(target, "15")); + Assert.IsTrue(validator1.IsValid(target, "abc")); + } + + [Test] + public void InvalidForInt() + { + Assert.IsFalse(validator2.IsValid(target, 15)); + } + + [Test] + public void ValidForString() + { + Assert.IsTrue(validator1.IsValid(target, "not 15")); + } + + [Test] + public void ValidForInt() + { + Assert.IsTrue(validator2.IsValid(target, 100)); + } + + [Test] + public void InvalidGuid() + { + Assert.IsFalse(validator3.IsValid(target, Guid.Empty)); + } + + public class TestTarget + { + private string targetField1; + private int targetField2; + private Guid targetField3; + + public string TargetField1 + { + get { return targetField1; } + set { targetField1 = value; } + } + + public int TargetField2 + { + get { return targetField2; } + set { targetField2 = value; } + } + + + public Guid TargetField3 + { + get { return targetField3; } + set { targetField3 = value; } + } + } + } +} \ No newline at end of file diff --git a/Components/General/Validator/Castle.Components.Validator/Attributes/AbstractValidationAttribute.cs b/Components/General/Validator/Castle.Components.Validator/Attributes/AbstractValidationAttribute.cs index 464e96af3..1e88d81ea 100644 --- a/Components/General/Validator/Castle.Components.Validator/Attributes/AbstractValidationAttribute.cs +++ b/Components/General/Validator/Castle.Components.Validator/Attributes/AbstractValidationAttribute.cs @@ -116,6 +116,16 @@ namespace Castle.Components.Validator /// public abstract IValidator Build(); + + /// + /// Constructs and configures an + /// instance based on the properties set on the attribute instance. + /// + public virtual IValidator Build(ValidatorRunner validatorRunner, Type type) + { + return Build(); + } + /// /// Applies the common configuration defined on the attribute. /// diff --git a/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateCollectionNotEmptyAttribute.cs b/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateCollectionNotEmptyAttribute.cs new file mode 100644 index 000000000..fe38d5f40 --- /dev/null +++ b/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateCollectionNotEmptyAttribute.cs @@ -0,0 +1,36 @@ +// Copyright 2004-2007 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.Components.Validator +{ + /// + /// Validates that the collection is not empty + /// + public class ValidateCollectionNotEmptyAttribute : AbstractValidationAttribute + { + /// + /// Constructs and configures an + /// instance based on the properties set on the attribute instance. + /// + /// + public override IValidator Build() + { + CollectionNotEmptyValidator validator = new CollectionNotEmptyValidator(); + + ConfigureValidatorMessage(validator); + + return validator; + } + } +} \ No newline at end of file diff --git a/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateGroupNotEmptyAttribute.cs b/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateGroupNotEmptyAttribute.cs new file mode 100644 index 000000000..c6428d25f --- /dev/null +++ b/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateGroupNotEmptyAttribute.cs @@ -0,0 +1,65 @@ +// Copyright 2004-2007 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections; + +namespace Castle.Components.Validator +{ + + using System; + + /// + /// Validate that at least one of the properties in the group is not null or empty (for strings) + /// + [Serializable] + public class ValidateGroupNotEmptyAttribute : AbstractValidationAttribute + { + private string group; + private static IDictionary groupsPerType = Hashtable.Synchronized(new Hashtable()); + + /// + /// Initializes a new instance of the class. + /// + /// The group. + public ValidateGroupNotEmptyAttribute(string group) + { + this.group = group; + } + + /// + /// Constructs and configures an + /// instance based on the properties set on the attribute instance. + /// + /// + public override IValidator Build() + { + throw new NotSupportedException("You must call Build with a type parameter"); + } + + public override IValidator Build(ValidatorRunner validatorRunner, Type type) + { + + GroupNotEmptyValidator validator = (GroupNotEmptyValidator) + validatorRunner.ExtendedProperties[group]; + if(validator==null) + { + validatorRunner.ExtendedProperties[group] = validator + = new GroupNotEmptyValidator(group); + } + ConfigureValidatorMessage(validator); + + return validator; + } + } +} \ No newline at end of file diff --git a/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateNonEmptyAttribute.cs b/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateNonEmptyAttribute.cs index b0cb0977f..90dd5bf16 100644 --- a/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateNonEmptyAttribute.cs +++ b/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateNonEmptyAttribute.cs @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. + namespace Castle.Components.Validator { using System; - + using System.Collections; using Castle.Components.Validator; /// @@ -24,6 +25,8 @@ namespace Castle.Components.Validator [Serializable] public class ValidateNonEmptyAttribute : AbstractValidationAttribute { + private readonly IDictionary validatorPerGroup = Hashtable.Synchronized(new Hashtable()); + /// /// Initializes a new instance of the class. /// diff --git a/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateNotSameValueAttribute.cs b/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateNotSameValueAttribute.cs new file mode 100644 index 000000000..9c76a0a74 --- /dev/null +++ b/Components/General/Validator/Castle.Components.Validator/Attributes/ValidateNotSameValueAttribute.cs @@ -0,0 +1,89 @@ +// Copyright 2004-2007 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Reflection; + +namespace Castle.Components.Validator +{ + /// + /// Validates that the content has not been set to the specified value + /// + public class ValidateNotSameValueAttribute : AbstractValidationAttribute + { + private readonly object value; + + /// + /// Initializes a new instance of the class. + /// + public ValidateNotSameValueAttribute(object mustNotBeThisValue) + { + this.value = mustNotBeThisValue; + } + + /// + /// Initializes a new instance of the class. + /// + /// Type of the value. + /// The must not be this value. + public ValidateNotSameValueAttribute(Type valueType, object mustNotBeThisValue) + :this(valueType,mustNotBeThisValue, null) + { + + } + + /// + /// Initializes a new instance of the class. + /// + public ValidateNotSameValueAttribute(Type valueType, object mustNotBeThisValue, string errorMessage) + : base(errorMessage) + { + if (mustNotBeThisValue==null) + { + throw new ArgumentException( + "You cannot use ValidateNotSameValue validator for null values, use ValidateNotEmpty for this"); + } + if (typeof(IConvertible).IsAssignableFrom(valueType)) + this.value = Convert.ChangeType(mustNotBeThisValue, valueType); + else + { + MethodBase createInstance = valueType.GetMethod("Parse",BindingFlags.Public|BindingFlags.Static); + if(createInstance!=null) + { + this.value = createInstance.Invoke(null, new object[] { mustNotBeThisValue }); + } + ConstructorInfo ctor = valueType.GetConstructor(new Type[] { mustNotBeThisValue.GetType() }); + if(ctor==null) + { + throw new ArgumentException( + "valueType must be a type that implements IConvertible, or have a Parse method, or have a constructor that accept a " + mustNotBeThisValue.GetType() + ); + } + this.value = Activator.CreateInstance(valueType, mustNotBeThisValue); + } + } + + /// + /// Constructs and configures an + /// instance based on the properties set on the attribute instance. + /// + /// + public override IValidator Build() + { + IValidator validator = new NotSameValueValidator(value); + ConfigureValidatorMessage(validator); + return validator; + } + } +} \ No newline at end of file diff --git a/Components/General/Validator/Castle.Components.Validator/CachedValidationRegistry.cs b/Components/General/Validator/Castle.Components.Validator/CachedValidationRegistry.cs index 0c825f293..0854f7267 100644 --- a/Components/General/Validator/Castle.Components.Validator/CachedValidationRegistry.cs +++ b/Components/General/Validator/Castle.Components.Validator/CachedValidationRegistry.cs @@ -29,16 +29,17 @@ namespace Castle.Components.Validator #region IValidatorRegistry Members - /// - /// Gets all validators associated with a . - /// - /// The validators returned are initialized. - /// - /// - /// Target type. - /// Restrict the set returned to the phase specified - /// A Validator array - public IValidator[] GetValidators(Type targetType, RunWhen runWhen) + /// + /// Gets all validators associated with a . + /// + /// The validators returned are initialized. + /// + /// + /// The validator runner. + /// Target type. + /// Restrict the set returned to the phase specified + /// A Validator array + public IValidator[] GetValidators(ValidatorRunner validatorRunner, Type targetType, RunWhen runWhen) { PropertyInfo[] properties = (PropertyInfo[]) propertiesPerType[targetType]; @@ -52,23 +53,24 @@ namespace Castle.Components.Validator foreach(PropertyInfo prop in properties) { - list.AddRange(GetValidators(targetType, prop, runWhen)); + list.AddRange(GetValidators(validatorRunner, targetType, prop, runWhen)); } return (IValidator[]) list.ToArray(typeof(IValidator)); } - /// - /// Gets all validators associated with a property. - /// - /// The validators returned are initialized. - /// - /// - /// Target type. - /// The property. - /// Restrict the set returned to the phase specified - /// A Validator array - public IValidator[] GetValidators(Type targetType, PropertyInfo property, RunWhen runWhen) + /// + /// Gets all validators associated with a property. + /// + /// The validators returned are initialized. + /// + /// + /// The validator runner. + /// Target type. + /// The property. + /// Restrict the set returned to the phase specified + /// A Validator array + public IValidator[] GetValidators(ValidatorRunner validatorRunner, Type targetType, PropertyInfo property, RunWhen runWhen) { object[] builders = (object[]) attrsPerProperty[property]; @@ -82,7 +84,7 @@ namespace Castle.Components.Validator foreach(IValidatorBuilder builder in builders) { - IValidator validator = builder.Build(); + IValidator validator = builder.Build(validatorRunner, targetType); if (!IsValidatorOnPhase(validator, runWhen)) continue; diff --git a/Components/General/Validator/Castle.Components.Validator/Castle.Components.Validator.csproj b/Components/General/Validator/Castle.Components.Validator/Castle.Components.Validator.csproj index 2a05ac1a0..e4044c581 100644 --- a/Components/General/Validator/Castle.Components.Validator/Castle.Components.Validator.csproj +++ b/Components/General/Validator/Castle.Components.Validator/Castle.Components.Validator.csproj @@ -37,9 +37,12 @@ + + + @@ -84,6 +87,8 @@ + + @@ -95,6 +100,7 @@ + diff --git a/Components/General/Validator/Castle.Components.Validator/IValidatorBuilder.cs b/Components/General/Validator/Castle.Components.Validator/IValidatorBuilder.cs index 613fe4cd9..4d6d46717 100644 --- a/Components/General/Validator/Castle.Components.Validator/IValidatorBuilder.cs +++ b/Components/General/Validator/Castle.Components.Validator/IValidatorBuilder.cs @@ -12,19 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. + namespace Castle.Components.Validator { - using Castle.Components.Validator; + using System; + using Castle.Components.Validator; /// /// Constructs an implementation. /// public interface IValidatorBuilder { - /// - /// Builds this instance. - /// - /// - IValidator Build(); + /// + /// Builds this instance. + /// + /// The validator runner. + /// The type that this validator is built for + /// + IValidator Build(ValidatorRunner validatorRunner, Type type); } } diff --git a/Components/General/Validator/Castle.Components.Validator/IValidatorRegistry.cs b/Components/General/Validator/Castle.Components.Validator/IValidatorRegistry.cs index cae1af168..6e355cce6 100644 --- a/Components/General/Validator/Castle.Components.Validator/IValidatorRegistry.cs +++ b/Components/General/Validator/Castle.Components.Validator/IValidatorRegistry.cs @@ -22,27 +22,29 @@ namespace Castle.Components.Validator /// public interface IValidatorRegistry { - /// - /// Gets all validators associated with a . - /// - /// The validators returned are initialized. - /// - /// - /// Restrict the set returned to the phase specified - /// Target type. - /// A Validator array - IValidator[] GetValidators(Type targetType, RunWhen runWhen); + /// + /// Gets all validators associated with a . + /// + /// The validators returned are initialized. + /// + /// + /// The validator runner. + /// Target type. + /// Restrict the set returned to the phase specified + /// A Validator array + IValidator[] GetValidators(ValidatorRunner validatorRunner, Type targetType, RunWhen runWhen); - /// - /// Gets all validators associated with a property. - /// - /// The validators returned are initialized. - /// - /// - /// Target type. - /// The property. - /// Restrict the set returned to the phase specified - /// A Validator array - IValidator[] GetValidators(Type targetType, PropertyInfo property, RunWhen runWhen); + /// + /// Gets all validators associated with a property. + /// + /// The validators returned are initialized. + /// + /// + /// The validator runner. + /// Target type. + /// The property. + /// Restrict the set returned to the phase specified + /// A Validator array + IValidator[] GetValidators(ValidatorRunner validatorRunner, Type targetType, PropertyInfo property, RunWhen runWhen); } } diff --git a/Components/General/Validator/Castle.Components.Validator/MessageConstants.cs b/Components/General/Validator/Castle.Components.Validator/MessageConstants.cs index 9aa9677cc..150e17bb1 100644 --- a/Components/General/Validator/Castle.Components.Validator/MessageConstants.cs +++ b/Components/General/Validator/Castle.Components.Validator/MessageConstants.cs @@ -34,5 +34,9 @@ namespace Castle.Components.Validator public const string RangeTooHighMessage = "range_toohigh"; public const string RangeTooLowMessage = "range_toolow"; public const string RangeTooHighOrLowMessage = "range_toohighorlow"; + public const string NotSameAsMessage = "not_same_as_invalid"; + public const string GroupNotEmpty = "group_not_empty_invalid"; + public const string GroupNotEmptySeperator = "group_not_empty_seperator"; + public const string CollectionNotEmpty = "collection_not_empty"; } } diff --git a/Components/General/Validator/Castle.Components.Validator/Messages.Designer.cs b/Components/General/Validator/Castle.Components.Validator/Messages.Designer.cs index 01bdd07db..5bfa85ec8 100644 --- a/Components/General/Validator/Castle.Components.Validator/Messages.Designer.cs +++ b/Components/General/Validator/Castle.Components.Validator/Messages.Designer.cs @@ -61,6 +61,15 @@ namespace Castle.Components.Validator { } /// + /// Looks up a localized string similar to Collection must not be empty. + /// + internal static string collection_not_empty { + get { + return ResourceManager.GetString("collection_not_empty", resourceCulture); + } + } + + /// /// Looks up a localized string similar to Please enter a valid date. /// internal static string date_invalid { @@ -115,6 +124,24 @@ namespace Castle.Components.Validator { } /// + /// Looks up a localized string similar to At least one of the values in ({0}) should not be empty. + /// + internal static string group_not_empty_invalid { + get { + return ResourceManager.GetString("group_not_empty_invalid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to , . + /// + internal static string group_not_empty_seperator { + get { + return ResourceManager.GetString("group_not_empty_seperator", resourceCulture); + } + } + + /// /// Looks up a localized string similar to Please enter a valid integer in this field. /// internal static string integer_invalid { @@ -160,6 +187,15 @@ namespace Castle.Components.Validator { } /// + /// Looks up a localized string similar to Field must not equals '{0}'. + /// + internal static string not_same_as_invalid { + get { + return ResourceManager.GetString("not_same_as_invalid", resourceCulture); + } + } + + /// /// Looks up a localized string similar to Field must be between {0} and {1}. /// internal static string range_invalid { diff --git a/Components/General/Validator/Castle.Components.Validator/Messages.resx b/Components/General/Validator/Castle.Components.Validator/Messages.resx index 1288e4a98..bc495dd54 100644 --- a/Components/General/Validator/Castle.Components.Validator/Messages.resx +++ b/Components/General/Validator/Castle.Components.Validator/Messages.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Collection must not be empty + Please enter a valid date @@ -135,6 +138,12 @@ Field has an invalid content + + At least one of the values in ({0}) should not be empty + + + , + Please enter a valid integer in this field @@ -153,6 +162,9 @@ Field must be between {0} and {1} + + Field must not equals '{0}' + Field must be less than or equal to {0} @@ -171,4 +183,4 @@ Field value is invalid (not a valid time) - + \ No newline at end of file diff --git a/Components/General/Validator/Castle.Components.Validator/ValidatorRunner.cs b/Components/General/Validator/Castle.Components.Validator/ValidatorRunner.cs index f476bd8b2..cf02a9003 100644 --- a/Components/General/Validator/Castle.Components.Validator/ValidatorRunner.cs +++ b/Components/General/Validator/Castle.Components.Validator/ValidatorRunner.cs @@ -33,7 +33,7 @@ namespace Castle.Components.Validator public class ValidatorRunner { private readonly static IDictionary type2Validator; - + private readonly IDictionary extendedProperties = new Hashtable(); private readonly bool inferValidators; private readonly IDictionary errorPerInstance; private readonly IValidatorRegistry registry; @@ -50,7 +50,12 @@ namespace Castle.Components.Validator type2Validator[typeof(DateTime)] = typeof(DateTimeValidator); } - /// + public IDictionary ExtendedProperties + { + get { return extendedProperties; } + } + + /// /// Initializes a new instance of the class. /// /// The instance registry. @@ -146,7 +151,7 @@ namespace Castle.Components.Validator if (parentType == null) throw new ArgumentNullException("parentType"); if (property == null) throw new ArgumentNullException("property"); - IValidator[] validators = registry.GetValidators(parentType, property, runWhenPhase); + IValidator[] validators = registry.GetValidators(this, parentType, property, runWhenPhase); if (inferValidators && validators.Length == 0) { @@ -193,7 +198,7 @@ namespace Castle.Components.Validator { if (objectInstance == null) throw new ArgumentNullException("objectInstance"); - IValidator[] validators = registry.GetValidators(objectInstance.GetType(), runWhen); + IValidator[] validators = registry.GetValidators(this, objectInstance.GetType(), runWhen); Array.Sort(validators, ValidatorComparer.Instance); diff --git a/Components/General/Validator/Castle.Components.Validator/Validators/AbstractValidator.cs b/Components/General/Validator/Castle.Components.Validator/Validators/AbstractValidator.cs index f27958725..3c73ee3c8 100644 --- a/Components/General/Validator/Castle.Components.Validator/Validators/AbstractValidator.cs +++ b/Components/General/Validator/Castle.Components.Validator/Validators/AbstractValidator.cs @@ -48,7 +48,7 @@ namespace Castle.Components.Validator /// Implementors should perform any initialization logic /// /// The target property - public void Initialize(PropertyInfo property) + public virtual void Initialize(PropertyInfo property) { this.property = property; @@ -219,7 +219,7 @@ namespace Castle.Components.Validator /// /// /// A resource set instance - protected static ResourceSet GetResourceForCurrentCulture() + protected internal static ResourceSet GetResourceForCurrentCulture() { return resourceManager.GetResourceSet(Thread.CurrentThread.CurrentCulture, true, true); } diff --git a/Components/General/Validator/Castle.Components.Validator/Validators/CollectionNotEmptyValidator.cs b/Components/General/Validator/Castle.Components.Validator/Validators/CollectionNotEmptyValidator.cs new file mode 100644 index 000000000..3e1c0d87f --- /dev/null +++ b/Components/General/Validator/Castle.Components.Validator/Validators/CollectionNotEmptyValidator.cs @@ -0,0 +1,63 @@ +// Copyright 2004-2007 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.Components.Validator +{ + using System; + using System.Collections; + + /// + /// Validates that the content is a collection that is not empty + /// + public class CollectionNotEmptyValidator : AbstractValidator + { + /// + /// Implementors should perform the actual validation upon + /// the property value + /// + /// The target type instance + /// The property/field value. It can be null. + /// + /// true if the value is accepted (has passed the validation test) + /// + public override bool IsValid(object instance, object fieldValue) + { + ICollection collection = fieldValue as ICollection; + if (collection == null) + { + return false; + } + return collection.Count != 0; + } + + /// + /// Gets a value indicating whether this validator supports web validation. + /// + /// + /// if web validation is supported; otherwise, . + /// + public override bool SupportsWebValidation + { + get { return false; } + } + + protected override string MessageKey + { + get + { + return MessageConstants.CollectionNotEmpty; + } + } + } +} \ No newline at end of file diff --git a/Components/General/Validator/Castle.Components.Validator/Validators/GroupNotEmptyValidator.cs b/Components/General/Validator/Castle.Components.Validator/Validators/GroupNotEmptyValidator.cs new file mode 100644 index 000000000..a66630059 --- /dev/null +++ b/Components/General/Validator/Castle.Components.Validator/Validators/GroupNotEmptyValidator.cs @@ -0,0 +1,187 @@ +// Copyright 2004-2007 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +namespace Castle.Components.Validator +{ + using System; + using System.Collections; + using System.Reflection; + using System.Text; + + /// + /// Ensures that at least one property in the group was filled with some value + /// + [Serializable] + public class GroupNotEmptyValidator : IValidator + { + private IDictionary properties = new Hashtable(); + private RunWhen runWhen = RunWhen.Everytime; + private int executionOrder; + private string errorMessage; + private string friendlyName; + private string groupName; + + public GroupNotEmptyValidator(string groupName) + { + this.groupName = groupName; + friendlyName = friendlyName; + } + + /// + /// Implementors should perform any initialization logic + /// + /// The target property + public void Initialize(PropertyInfo property) + { + properties[property] = FriendlyName ?? property.Name; + } + + /// + /// The target property + /// + public PropertyInfo Property + { + get { throw new NotSupportedException("Group validator has more than a single property"); } + } + + /// + /// Defines when to run the validation. + /// Defaults to RunWhen.Everytime + /// + public RunWhen RunWhen + { + get { return runWhen; } + set { runWhen = value; } + } + + /// + /// Gets or sets the validation execution order. + /// + /// The execution order. + public int ExecutionOrder + { + get { return executionOrder; } + set { executionOrder = value; } + } + + /// + /// The error message to be displayed if the validation fails + /// + /// The error message. + public string ErrorMessage + { + get + { + if (errorMessage == null) + { + errorMessage = BuildErrorMessage(); + } + return errorMessage; + } + set { errorMessage = value; } + } + + private string BuildErrorMessage() + { + StringBuilder sb = new StringBuilder(); + string seperator = + AbstractValidator.GetResourceForCurrentCulture().GetString(MessageConstants.GroupNotEmptySeperator); + foreach(string name in properties.Values) + { + sb.Append(name).Append(seperator); + } + if (sb.Length > 0) + { + sb.Remove(sb.Length - seperator.Length, seperator.Length); + } + string messageFormat = AbstractValidator.GetResourceForCurrentCulture().GetString(MessageConstants.GroupNotEmpty); + return string.Format(messageFormat, sb); + } + + /// + /// Gets or sets the a friendly name for the target property + /// + /// The name. + public string FriendlyName + { + get { return friendlyName; } + set { friendlyName = value; } + } + + /// + /// Implementors should perform the actual validation upon + /// the property value + /// + /// + /// true if the field is OK + public bool IsValid(object instance) + { + bool result = false; + foreach(PropertyInfo info in properties.Keys) + { + object o = info.GetValue(instance, null); + result |= o != null && o.ToString().Length != 0; + } + return result; + } + + /// + /// Implementors should perform the actual validation upon + /// the property value + /// + /// + /// + /// true if the field is OK + public bool IsValid(object instance, object fieldValue) + { + throw new NotSupportedException("Must validate on the entire instance, not a single property"); + } + + /// + /// Gets a value indicating whether this validator supports web validation. + /// + /// + /// if web validation is supported; otherwise, . + /// + public bool SupportsWebValidation + { + get { return false; } + } + + /// + /// Applies the web validation by setting up one or + /// more input rules on . + /// + /// The config. + /// Type of the input. + /// The generator. + /// The attributes. + /// The name. + public void ApplyWebValidation(WebValidationConfiguration config, InputElementType inputType, + IWebValidationGenerator generator, IDictionary attributes, + string name) + { + } + + /// + /// Gets the property name. The + /// is returned if non-null, otherwise it will return the property name. + /// + public string Name + { + get { return groupName; } + } + } +} \ No newline at end of file diff --git a/Components/General/Validator/Castle.Components.Validator/Validators/NotSameValueValidator.cs b/Components/General/Validator/Castle.Components.Validator/Validators/NotSameValueValidator.cs new file mode 100644 index 000000000..8f7746c51 --- /dev/null +++ b/Components/General/Validator/Castle.Components.Validator/Validators/NotSameValueValidator.cs @@ -0,0 +1,70 @@ +// Copyright 2004-2007 Castle Project - http://www.castleproject.org/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Castle.Components.Validator +{ + using System.Collections; + using System.Reflection; + + /// + /// Validates that the content is not set to the specified value + /// + public class NotSameValueValidator : AbstractValidator + { + private readonly object value; + + /// + /// Initializes a new instance of the class. + /// + public NotSameValueValidator(object value) + { + this.value = value; + } + + /// + /// Validates that the fieldValue + /// is not set to the specified value + /// + /// The target type instance + /// The property/field value. It can be null. + /// + /// true if the value is accepted (has passed the validation test) + /// + public override bool IsValid(object instance, object fieldValue) + { + return Equals(value, fieldValue) == false; + } + + /// + /// Gets a value indicating whether this validator supports web validation. + /// + /// + /// if web validation is supported; otherwise, . + /// + public override bool SupportsWebValidation + { + get { return false; } + } + + protected override string MessageKey + { + get { return MessageConstants.NotSameAsMessage; } + } + + protected override string BuildErrorMessage() + { + return string.Format(GetResourceForCurrentCulture().GetString(MessageConstants.NotSameAsMessage), value); + } + } +} \ No newline at end of file -- 2.11.4.GIT