diff --git a/src/NHibernate.Validator.Tests/Base/Address.cs b/src/NHibernate.Validator.Tests/Base/Address.cs index 176386f..e02ae1f 100644 --- a/src/NHibernate.Validator.Tests/Base/Address.cs +++ b/src/NHibernate.Validator.Tests/Base/Address.cs @@ -60,6 +60,8 @@ public string Zip set { zip = value; } } + [Length(Max = 1000, Tags = "T1")] + [Length(Max = 2000, Tags = "T2")] public string Line2 { get { return line2; } @@ -130,4 +132,4 @@ public AddressDef() Define(x => x.AddressFlags).Enum(); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator.Tests/Integration/HibernateAnnotationIntegrationFixture.cs b/src/NHibernate.Validator.Tests/Integration/HibernateAnnotationIntegrationFixture.cs index 981078e..0b4de53 100644 --- a/src/NHibernate.Validator.Tests/Integration/HibernateAnnotationIntegrationFixture.cs +++ b/src/NHibernate.Validator.Tests/Integration/HibernateAnnotationIntegrationFixture.cs @@ -152,6 +152,19 @@ public void ApplyOnEnumColumn() Assert.That(serialColumn.CheckConstraint, Is.Null.Or.Empty, "Validator annotation should not generate check for [Flag]ed Enums"); } + [Test] + public void SameAttributeWithDifferentTagsOnPropertyDoNotModifyNhibMappings() + { + PersistentClass classMapping = cfg.GetClassMapping(typeof(Address)); + IEnumerator ie = classMapping.GetProperty("Line2").ColumnIterator.GetEnumerator(); + ie.MoveNext(); + Column serialColumn = (Column) ie.Current; + + + Assert.AreEqual(serialColumn.Length, 255, "Same attribute with different tags must not change Nhib mappings"); + } + + /// /// Test pre-update/save events and custom interpolator /// diff --git a/src/NHibernate.Validator.Tests/Mappings/MixAddress.cs b/src/NHibernate.Validator.Tests/Mappings/MixAddress.cs index 766a4cc..efe817b 100644 --- a/src/NHibernate.Validator.Tests/Mappings/MixAddress.cs +++ b/src/NHibernate.Validator.Tests/Mappings/MixAddress.cs @@ -67,5 +67,12 @@ public bool InternalValid get { return internalValid; } set { internalValid = value; } } + + + [Min(1, Tags = "T1")] //This attribute will be redefined in xml mapping + [Min(1000, Tags = "T2")] + [Min(333, Tags = "T3")] //And this also will be redefined by xml + public int Num { get; set; } + } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator.Tests/Mappings/MixAddress.nhv.xml b/src/NHibernate.Validator.Tests/Mappings/MixAddress.nhv.xml index ab0f1bf..0bae04e 100644 --- a/src/NHibernate.Validator.Tests/Mappings/MixAddress.nhv.xml +++ b/src/NHibernate.Validator.Tests/Mappings/MixAddress.nhv.xml @@ -33,5 +33,9 @@ + + + + - \ No newline at end of file + diff --git a/src/NHibernate.Validator.Tests/Mappings/MixedClassMappingFixture.cs b/src/NHibernate.Validator.Tests/Mappings/MixedClassMappingFixture.cs index bd420dd..b94dd50 100644 --- a/src/NHibernate.Validator.Tests/Mappings/MixedClassMappingFixture.cs +++ b/src/NHibernate.Validator.Tests/Mappings/MixedClassMappingFixture.cs @@ -3,8 +3,10 @@ using System.Reflection; using NHibernate.Validator.Cfg; using NHibernate.Validator.Cfg.MappingSchema; +using NHibernate.Validator.Constraints; using NHibernate.Validator.Mappings; using NUnit.Framework; +using SharpTestsEx; using RangeAttribute=NHibernate.Validator.Constraints.RangeAttribute; namespace NHibernate.Validator.Tests.Mappings @@ -95,6 +97,18 @@ public void MemberAttributes() Assert.AreEqual(9999, ra.Max); } } + + mi = typeof(MixAddress).GetProperty("Num"); + mas = new List(rm.GetMemberAttributes(mi)); + Assert.AreEqual(2, mas.Count); + foreach (var ma in mas) + { + if (ma is MinAttribute mia) + { + Assert.Contains(mia.Value, new[] { 100, 1000 }); + } + } + } [Test] @@ -102,11 +116,11 @@ public void Members() { IClassMapping rm = new AttributeOverXmlClassMapping(GetXmlClassMapping(typeof (MixAddress))); var mi = new List(rm.GetMembers()); - Assert.AreEqual(16, mi.Count); // the members of the class by reflection + Assert.AreEqual(18, mi.Count); // the members of the class by reflection rm = new XmlOverAttributeClassMapping(GetXmlClassMapping(typeof (MixAddress))); mi = new List(rm.GetMembers()); - Assert.AreEqual(16, mi.Count); + Assert.AreEqual(18, mi.Count); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator.Tests/Specifics/NHV119/Fixture.cs b/src/NHibernate.Validator.Tests/Specifics/NHV119/Fixture.cs new file mode 100644 index 0000000..cc1c1a2 --- /dev/null +++ b/src/NHibernate.Validator.Tests/Specifics/NHV119/Fixture.cs @@ -0,0 +1,105 @@ +using System.Reflection; +using NHibernate.Validator.Cfg; +using NHibernate.Validator.Cfg.Loquacious; +using NHibernate.Validator.Engine; +using NUnit.Framework; +using SharpTestsEx; + +namespace NHibernate.Validator.Tests.Specifics.NHV119 +{ + + [TestFixture] + public class Fixture : BaseValidatorFixture + { + [Test] + public void TestSameAttributeByDifferentTagsAttributeConfig() + { + //var vl = GetClassValidator(typeof(Model1)); + + var vtor = new ValidatorEngine(); + + var m = new Model1(); + + m.Qnt = 1000; + vtor.Validate(m).Should().Be.Empty(); + + m.Qnt = 50; + vtor.Validate(m).Should().Not.Be.Empty(); + + m.Qnt = 100; + vtor.Validate(m, "T2").Should().Be.Empty(); + + vtor.Validate(m, "T2", null).Should().Not.Be.Empty(); + + m.Qnt = 10; + vtor.Validate(m, "T1").Should().Be.Empty(); + } + + [Test] + public void TestSameAttributeByDifferentTagsXmlOverAttributeConfig() + { + //var vl = GetClassValidator(typeof(Model1)); + + var vtor = new ValidatorEngine(); + var cfg = new XmlConfiguration(); + cfg.Properties[Environment.ValidatorMode] = "OverrideAttributeWithExternal"; + string an = Assembly.GetExecutingAssembly().FullName; + cfg.Mappings.Add(new MappingConfiguration(an, "NHibernate.Validator.Tests.Specifics.NHV119.Mappings.nhv.xml")); + vtor.Configure(cfg); + + var m = new Model1(); + + m.Qnt = 1000; + vtor.Validate(m).Should().Be.Empty(); + + m.Qnt = 100; + vtor.Validate(m).Should().Not.Be.Empty(); + + m.Qnt = 20; + vtor.Validate(m, "T1").Should().Be.Empty(); + + m.Qnt = 19; + vtor.Validate(m, "T1").Should().Not.Be.Empty(); + + + m.Qnt = 199; + vtor.Validate(m, "T2").Should().Not.Be.Empty(); + + m.Qnt = 200; + vtor.Validate(m, "T2").Should().Be.Empty(); + } + + [Test] + public void TestSameAttributeByDifferentTagsFluentConfig() + { + //var vl = GetClassValidator(typeof(Model1)); + + + var configure = new FluentConfiguration(); + configure.Register(new[] { typeof(Model1Validation) }) + .SetDefaultValidatorMode(ValidatorMode.UseExternal); + + var vtor = new ValidatorEngine(); + vtor.Configure(configure); + + var m = new Model1(); + + m.Qnt = 3000; + vtor.Validate(m).Should().Be.Empty(); + + m.Qnt = 300; + vtor.Validate(m).Should().Not.Be.Empty(); + + m.Qnt = 30; + vtor.Validate(m, "T1").Should().Be.Empty(); + + + m.Qnt = 299; + vtor.Validate(m, "T2").Should().Not.Be.Empty(); + + m.Qnt = 3001; + vtor.Validate(m, "T2").Should().Be.Empty(); + } + } + +} diff --git a/src/NHibernate.Validator.Tests/Specifics/NHV119/Mappings.nhv.xml b/src/NHibernate.Validator.Tests/Specifics/NHV119/Mappings.nhv.xml new file mode 100644 index 0000000..0b9d745 --- /dev/null +++ b/src/NHibernate.Validator.Tests/Specifics/NHV119/Mappings.nhv.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/src/NHibernate.Validator.Tests/Specifics/NHV119/Model1.cs b/src/NHibernate.Validator.Tests/Specifics/NHV119/Model1.cs new file mode 100644 index 0000000..fcd2507 --- /dev/null +++ b/src/NHibernate.Validator.Tests/Specifics/NHV119/Model1.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NHibernate.Validator.Constraints; + +namespace NHibernate.Validator.Tests.Specifics.NHV119 +{ + public class Model1 + { + [Min(10, Tags = "T1")] + [Min(777, Tags = "T2")] //This attribute will be ignored (redefined), by next one + [Min(100, Tags = "T2")] //And this will be redefined by xml config in TestSameAttributeByDifferentTagsXmlOverAttributeConfig test + [Min(1000)] + public int Qnt { get; set; } + } +} diff --git a/src/NHibernate.Validator.Tests/Specifics/NHV119/Model1Validation.cs b/src/NHibernate.Validator.Tests/Specifics/NHV119/Model1Validation.cs new file mode 100644 index 0000000..5c99781 --- /dev/null +++ b/src/NHibernate.Validator.Tests/Specifics/NHV119/Model1Validation.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NHibernate.Validator.Cfg.Loquacious; + +namespace NHibernate.Validator.Tests.Specifics.NHV119 +{ + public class Model1Validation : ValidationDef + { + public Model1Validation() + { + Define(x => x.Qnt).GreaterThanOrEqualTo(30).WithTags("T1"); + Define(x => x.Qnt).GreaterThanOrEqualTo(300).WithTags("T2"); + Define(x => x.Qnt).GreaterThanOrEqualTo(3000).WithTags("T2"); //This definition must override previous one + } + } +} diff --git a/src/NHibernate.Validator.Tests/Utils/AttributeUtilsFixture.cs b/src/NHibernate.Validator.Tests/Utils/AttributeUtilsFixture.cs index 5d4c70d..55aba42 100644 --- a/src/NHibernate.Validator.Tests/Utils/AttributeUtilsFixture.cs +++ b/src/NHibernate.Validator.Tests/Utils/AttributeUtilsFixture.cs @@ -10,14 +10,14 @@ public class AttributeUtilsFixture [Test] public void AttributeCanBeMultiplied() { - PatternAttribute patternAttribute = new PatternAttribute(); + var patternAttribute = new PatternAttribute(); Assert.AreEqual(true, (AttributeUtils.AttributeAllowsMultiple(patternAttribute))); } [Test] public void AttributeCannotBeMultiplied() { - LengthAttribute lenghtAttribute = new LengthAttribute(); + var lenghtAttribute = new IBANAttribute(); Assert.AreEqual(false, (AttributeUtils.AttributeAllowsMultiple(lenghtAttribute))); } } diff --git a/src/NHibernate.Validator/Constraints/DecimalMaxAttribute.cs b/src/NHibernate.Validator/Constraints/DecimalMaxAttribute.cs index 7598e6b..33221ae 100644 --- a/src/NHibernate.Validator/Constraints/DecimalMaxAttribute.cs +++ b/src/NHibernate.Validator/Constraints/DecimalMaxAttribute.cs @@ -3,6 +3,7 @@ using System.Globalization; using NHibernate.Mapping; using NHibernate.Validator.Engine; +using NHibernate.Validator.Util; namespace NHibernate.Validator.Constraints { @@ -10,7 +11,7 @@ namespace NHibernate.Validator.Constraints /// Max restriction on a numeric annotated element /// [Serializable] - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public class DecimalMaxAttribute : EmbeddedRuleArgsAttribute, IRuleArgs, IValidator, IPropertyConstraint { private string message = "{validator.max}"; @@ -98,6 +99,9 @@ public bool IsValid(object value, IConstraintValidatorContext validatorContext) public void Apply(Property property) { + if (AttributeUtils.AttributeUsedMultipleTimesOnProperty(property, GetType())) + return; + IEnumerator ie = property.ColumnIterator.GetEnumerator(); ie.MoveNext(); var col = (Column)ie.Current; diff --git a/src/NHibernate.Validator/Constraints/DecimalMinAttribute.cs b/src/NHibernate.Validator/Constraints/DecimalMinAttribute.cs index fae2039..5d69032 100644 --- a/src/NHibernate.Validator/Constraints/DecimalMinAttribute.cs +++ b/src/NHibernate.Validator/Constraints/DecimalMinAttribute.cs @@ -3,6 +3,7 @@ using System.Globalization; using NHibernate.Mapping; using NHibernate.Validator.Engine; +using NHibernate.Validator.Util; namespace NHibernate.Validator.Constraints { @@ -10,7 +11,7 @@ namespace NHibernate.Validator.Constraints /// Min restriction on a numeric annotated element (or the string representation of a numeric) /// [Serializable] - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public class DecimalMinAttribute : EmbeddedRuleArgsAttribute, IRuleArgs, IValidator, IPropertyConstraint { private string message = "{validator.min}"; @@ -95,6 +96,9 @@ public bool IsValid(object value, IConstraintValidatorContext validatorContext) public void Apply(Property property) { + if (AttributeUtils.AttributeUsedMultipleTimesOnProperty(property, GetType())) + return; + IEnumerator ie = property.ColumnIterator.GetEnumerator(); ie.MoveNext(); var col = (Column)ie.Current; diff --git a/src/NHibernate.Validator/Constraints/DelegatedValidatorAttribute.cs b/src/NHibernate.Validator/Constraints/DelegatedValidatorAttribute.cs index 8b81acb..3e6a199 100644 --- a/src/NHibernate.Validator/Constraints/DelegatedValidatorAttribute.cs +++ b/src/NHibernate.Validator/Constraints/DelegatedValidatorAttribute.cs @@ -1,10 +1,12 @@ using System; using NHibernate.Validator.Engine; +using NHibernate.Validator.Util; namespace NHibernate.Validator.Constraints { [Serializable] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + [AllowsMultipleWithIntersectingTags] [CLSCompliant(false)] public class DelegatedValidatorAttribute : EmbeddedRuleArgsAttribute, IValidatorInstanceProvider, IRuleArgs { @@ -23,4 +25,4 @@ public IValidator Validator get { return validatorInstance; } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator/Constraints/LengthAttribute.cs b/src/NHibernate.Validator/Constraints/LengthAttribute.cs index f4e138e..c36a952 100644 --- a/src/NHibernate.Validator/Constraints/LengthAttribute.cs +++ b/src/NHibernate.Validator/Constraints/LengthAttribute.cs @@ -1,7 +1,9 @@ using System; using System.Collections; +using System.Reflection; using NHibernate.Mapping; using NHibernate.Validator.Engine; +using NHibernate.Validator.Util; namespace NHibernate.Validator.Constraints { @@ -9,7 +11,7 @@ namespace NHibernate.Validator.Constraints /// Apply some length restrictions to the annotated element. It has to be a string /// [Serializable] - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public class LengthAttribute : EmbeddedRuleArgsAttribute, IRuleArgs, IValidator, IPropertyConstraint { private string message = "{validator.length}"; @@ -67,6 +69,9 @@ public bool IsValid(object value, IConstraintValidatorContext validatorContext) public void Apply(Property property) { + if (AttributeUtils.AttributeUsedMultipleTimesOnProperty(property, GetType())) + return; + IEnumerator ie = property.ColumnIterator.GetEnumerator(); ie.MoveNext(); var col = (Column)ie.Current; @@ -79,4 +84,4 @@ public void Apply(Property property) #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator/Constraints/MaxAttribute.cs b/src/NHibernate.Validator/Constraints/MaxAttribute.cs index ffdbd4c..678b65c 100644 --- a/src/NHibernate.Validator/Constraints/MaxAttribute.cs +++ b/src/NHibernate.Validator/Constraints/MaxAttribute.cs @@ -2,6 +2,7 @@ using System.Collections; using NHibernate.Mapping; using NHibernate.Validator.Engine; +using NHibernate.Validator.Util; namespace NHibernate.Validator.Constraints { @@ -9,7 +10,7 @@ namespace NHibernate.Validator.Constraints /// Max restriction on a numeric annotated element /// [Serializable] - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public class MaxAttribute : EmbeddedRuleArgsAttribute, IRuleArgs, IValidator, IPropertyConstraint { private string message = "{validator.max}"; @@ -70,6 +71,9 @@ public bool IsValid(object value, IConstraintValidatorContext validatorContext) public void Apply(Property property) { + if (AttributeUtils.AttributeUsedMultipleTimesOnProperty(property, GetType())) + return; + IEnumerator ie = property.ColumnIterator.GetEnumerator(); ie.MoveNext(); var col = (Column)ie.Current; @@ -78,4 +82,4 @@ public void Apply(Property property) #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator/Constraints/MinAttribute.cs b/src/NHibernate.Validator/Constraints/MinAttribute.cs index 185f4db..debae4c 100644 --- a/src/NHibernate.Validator/Constraints/MinAttribute.cs +++ b/src/NHibernate.Validator/Constraints/MinAttribute.cs @@ -2,6 +2,7 @@ using System.Collections; using NHibernate.Mapping; using NHibernate.Validator.Engine; +using NHibernate.Validator.Util; namespace NHibernate.Validator.Constraints { @@ -9,7 +10,7 @@ namespace NHibernate.Validator.Constraints /// Min restriction on a numeric annotated element (or the string representation of a numeric) /// [Serializable] - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public class MinAttribute : EmbeddedRuleArgsAttribute, IRuleArgs, IValidator, IPropertyConstraint { private string message = "{validator.min}"; @@ -64,10 +65,13 @@ public bool IsValid(object value, IConstraintValidatorContext validatorContext) public void Apply(Property property) { + if (AttributeUtils.AttributeUsedMultipleTimesOnProperty(property, GetType())) + return; + IEnumerator ie = property.ColumnIterator.GetEnumerator(); ie.MoveNext(); var col = (Column)ie.Current; col.CheckConstraint = col.Name + ">=" + Value; } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator/Constraints/PatternAttribute.cs b/src/NHibernate.Validator/Constraints/PatternAttribute.cs index 021bce7..b39ce92 100644 --- a/src/NHibernate.Validator/Constraints/PatternAttribute.cs +++ b/src/NHibernate.Validator/Constraints/PatternAttribute.cs @@ -1,6 +1,7 @@ using System; using System.Text.RegularExpressions; using NHibernate.Validator.Engine; +using NHibernate.Validator.Util; namespace NHibernate.Validator.Constraints { @@ -9,6 +10,7 @@ namespace NHibernate.Validator.Constraints /// [Serializable] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + [AllowsMultipleWithIntersectingTags] public class PatternAttribute : EmbeddedRuleArgsAttribute, IRuleArgs, IValidator { private RegexOptions flags = RegexOptions.Compiled; @@ -66,4 +68,4 @@ protected Regex GetRegex() #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator/Constraints/RangeAttribute.cs b/src/NHibernate.Validator/Constraints/RangeAttribute.cs index 9e28605..82187fb 100644 --- a/src/NHibernate.Validator/Constraints/RangeAttribute.cs +++ b/src/NHibernate.Validator/Constraints/RangeAttribute.cs @@ -2,6 +2,7 @@ using System.Collections; using NHibernate.Mapping; using NHibernate.Validator.Engine; +using NHibernate.Validator.Util; namespace NHibernate.Validator.Constraints { @@ -10,7 +11,7 @@ namespace NHibernate.Validator.Constraints /// representation of the numeric value. /// [Serializable] - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public class RangeAttribute : EmbeddedRuleArgsAttribute, IRuleArgs, IValidator, IPropertyConstraint { private long max = long.MaxValue; @@ -94,6 +95,10 @@ public bool IsValid(object value, IConstraintValidatorContext validatorContext) public void Apply(Property property) { + if (AttributeUtils.AttributeUsedMultipleTimesOnProperty(property, GetType())) + return; + + IEnumerator ie = property.ColumnIterator.GetEnumerator(); ie.MoveNext(); var col = (Column)ie.Current; @@ -116,4 +121,4 @@ public void Apply(Property property) #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator/Constraints/SizeAttribute.cs b/src/NHibernate.Validator/Constraints/SizeAttribute.cs index 8822830..2249ce7 100644 --- a/src/NHibernate.Validator/Constraints/SizeAttribute.cs +++ b/src/NHibernate.Validator/Constraints/SizeAttribute.cs @@ -18,7 +18,7 @@ namespace NHibernate.Validator.Constraints /// /// [Serializable] - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public class SizeAttribute : EmbeddedRuleArgsAttribute, IRuleArgs, IValidator { private int max = int.MaxValue; @@ -92,4 +92,4 @@ public bool IsValid(object value, IConstraintValidatorContext validatorContext) #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator/Constraints/WithinAttribute.cs b/src/NHibernate.Validator/Constraints/WithinAttribute.cs index f4bc3bf..3f5a56c 100644 --- a/src/NHibernate.Validator/Constraints/WithinAttribute.cs +++ b/src/NHibernate.Validator/Constraints/WithinAttribute.cs @@ -3,15 +3,16 @@ using System.Text; using NHibernate.Mapping; using NHibernate.Validator.Engine; +using NHibernate.Validator.Util; namespace NHibernate.Validator.Constraints { /// - /// The annotated elemnt has to be in the appropriate range (excluding both limits). + /// The annotated element has to be in the appropriate range (excluding both limits). /// Apply on numeric values can be converted to double (). /// [Serializable] - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public class WithinAttribute : EmbeddedRuleArgsAttribute, IRuleArgs, IValidator, IPropertyConstraint { private string message = "{validator.within}"; @@ -88,6 +89,9 @@ public bool IsValid(object value, IConstraintValidatorContext constraintValidato public void Apply(Property property) { + if (AttributeUtils.AttributeUsedMultipleTimesOnProperty(property, GetType())) + return; + var col = property.ColumnIterator.OfType().First(); var check = new StringBuilder(80); @@ -108,4 +112,4 @@ public void Apply(Property property) #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator/Engine/ClassValidator.cs b/src/NHibernate.Validator/Engine/ClassValidator.cs index df90c7b..404cffe 100644 --- a/src/NHibernate.Validator/Engine/ClassValidator.cs +++ b/src/NHibernate.Validator/Engine/ClassValidator.cs @@ -207,6 +207,9 @@ private void InitValidator(System.Type clazz, IDictionary(); + foreach (MemberInfo member in map.GetMembers()) { var memberAttributes = map.GetMemberAttributes(member); @@ -221,7 +224,10 @@ private void InitValidator(System.Type clazz, IDictionary 0) + { + membersToValidate.AddRange(membersToValidateFor1Memeber); + membersToValidateFor1Memeber.Clear(); + } } } + private void RemoveSameTypeValidatorWithIntersectingTags(List membersToValidateFor1Memeber, ITagableRule newAttr) + { + membersToValidateFor1Memeber.RemoveAll(x => x.ValidatorDef.Validator.GetType() == newAttr.GetType() + && ((x.ValidatorDef.Tags == null && newAttr.TagCollection == null) + || x.ValidatorDef.Tags.Intersect(newAttr.TagCollection).Any() + )); + } + private void AddAttributeToMember(MemberInfo currentMember, Attribute thisattribute) { #if NETFX diff --git a/src/NHibernate.Validator/Mappings/MixedClassMapping.cs b/src/NHibernate.Validator/Mappings/MixedClassMapping.cs index 5196614..a5f62a1 100644 --- a/src/NHibernate.Validator/Mappings/MixedClassMapping.cs +++ b/src/NHibernate.Validator/Mappings/MixedClassMapping.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; +using NHibernate.Validator.Engine; using NHibernate.Validator.Util; namespace NHibernate.Validator.Mappings @@ -22,37 +24,59 @@ protected virtual void InitializeClassAttributes(IClassMapping baseMap, IClassMa protected void MixMembersWith(HashSet lmembers, IClassMapping mapping) { - foreach (MemberInfo info in mapping.GetMembers()) + foreach (var info in mapping.GetMembers()) { lmembers.Add(info); - IEnumerable mas = mapping.GetMemberAttributes(info); - if (mas != null) + var mas = mapping.GetMemberAttributes(info); + if (mas == null) + continue; + + if (!membersAttributesDictionary.TryGetValue(info, out var attrs)) { - List attrs; - if (!membersAttributesDictionary.TryGetValue(info, out attrs)) - { - membersAttributesDictionary[info] = new List(mas); - } - else - { - CombineAttribute(mas, attrs); - membersAttributesDictionary[info] = attrs; - } + membersAttributesDictionary[info] = new List(mas); + } + else + { + CombineAttribute(mas, attrs); + membersAttributesDictionary[info] = attrs; } } } protected static void CombineAttribute(IEnumerable origin, List dest) { - foreach (Attribute ma in origin) + foreach (var ma in origin) { - Attribute found = dest.Find(attribute => ma.TypeId.Equals(attribute.TypeId)); + var founded = dest.FindAll(attribute => ma.TypeId.Equals(attribute.TypeId)); - if (found != null && !AttributeUtils.AttributeAllowsMultiple(ma)) - dest.Remove(found); + if (founded.Count > 0) + { + if (!AttributeUtils.AttributeAllowsMultiple(ma)) + { + dest.Remove(founded[0]); + } + else + { + if ((ma is ITagableRule matr) && !AttributeUtils.AttributeAllowsMultipleWithIntersectingTags(ma)) + { + foreach (var fd in founded) + { + if (!(fd is ITagableRule fdtr)) + continue; + + if ((fdtr.TagCollection.Count == 0 && matr.TagCollection.Count == 0) + || fdtr.TagCollection.Intersect(matr.TagCollection).Any() + ) + { + dest.Remove(fd); + } + } + } + } + } dest.Add(ma); } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Validator/Mappings/OpenClassMapping.cs b/src/NHibernate.Validator/Mappings/OpenClassMapping.cs index 1411d62..f3271c2 100644 --- a/src/NHibernate.Validator/Mappings/OpenClassMapping.cs +++ b/src/NHibernate.Validator/Mappings/OpenClassMapping.cs @@ -55,7 +55,7 @@ public void AddEntityValidator(Attribute attribute) { if (attribute == null) { - throw new ArgumentNullException("attribute"); + throw new ArgumentNullException(nameof(attribute)); } // TODO : check attribute in order to validate that it is a valid Entity-Validator attribute classAttributes.Add(attribute); @@ -65,11 +65,11 @@ public void AddConstraint(PropertyInfo property, Attribute attribute) { if (property == null) { - throw new ArgumentNullException("property"); + throw new ArgumentNullException(nameof(property)); } if (attribute == null) { - throw new ArgumentNullException("attribute"); + throw new ArgumentNullException(nameof(attribute)); } AddMemberConstraint(property, attribute); } @@ -78,41 +78,41 @@ public void AddConstraint(FieldInfo field, Attribute attribute) { if (field == null) { - throw new ArgumentNullException("field"); + throw new ArgumentNullException(nameof(field)); } if (attribute == null) { - throw new ArgumentNullException("attribute"); + throw new ArgumentNullException(nameof(attribute)); } AddMemberConstraint(field, attribute); } public void AddMemberConstraint(MemberInfo member, Attribute attribute) { - List constraints; - - if (!membersAttributesDictionary.TryGetValue(member, out constraints)) + if (!membersAttributesDictionary.TryGetValue(member, out var constraints)) { constraints = new List(); membersAttributesDictionary.Add(member, constraints); } - Attribute found = constraints.Find(x => x.TypeId.Equals(attribute.TypeId)); + var found = constraints.Find(x => x.TypeId.Equals(attribute.TypeId)); if (found == null || AttributeUtils.AttributeAllowsMultiple(attribute)) { + constraints.Add(attribute); + //Not possible to stop adding constraints here based on intersecting tags, because Tags not yet assigned when this code is executed + #if NETFX log.Debug(string.Format("For class {0} Adding member {1} to dictionary with attribute {2}", EntityType.FullName, - member.Name, attribute)); + member.Name, attribute)); #else Log.Debug("For class {0} Adding member {1} to dictionary with attribute {2}", EntityType.FullName, member.Name, attribute); #endif - membersAttributesDictionary[member].Add(attribute); } else { #if NETFX - log.Debug("Duplicated Attribute avoided: Class:" + typeof(T).FullName + " Member:" + member.Name + " Attribute:" - + attribute); + log.Debug("Duplicated Attribute avoided: Class:" + typeof(T).FullName + " Member:" + member.Name + " Attribute:" + + attribute); #else Log.Debug("Duplicated Attribute avoided: Class: {0} Member: {1} Attribute: {2}", typeof(T).FullName, member.Name, attribute); diff --git a/src/NHibernate.Validator/Util/AllowsMultipleWithIntersectingTagsAttribute.cs b/src/NHibernate.Validator/Util/AllowsMultipleWithIntersectingTagsAttribute.cs new file mode 100644 index 0000000..2effb87 --- /dev/null +++ b/src/NHibernate.Validator/Util/AllowsMultipleWithIntersectingTagsAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace NHibernate.Validator.Util +{ + /// + /// Specify if constraint attribute can be used multiple times with intersecting tags. (No tags, or same tags) + /// + [AttributeUsage(AttributeTargets.Class)] + public class AllowsMultipleWithIntersectingTagsAttribute : Attribute + { + } +} diff --git a/src/NHibernate.Validator/Util/AttributeUtils.cs b/src/NHibernate.Validator/Util/AttributeUtils.cs index 887fb52..aedf6f1 100644 --- a/src/NHibernate.Validator/Util/AttributeUtils.cs +++ b/src/NHibernate.Validator/Util/AttributeUtils.cs @@ -1,4 +1,6 @@ using System; +using System.Reflection; +using NHibernate.Mapping; namespace NHibernate.Validator.Util { @@ -14,8 +16,46 @@ public static class AttributeUtils /// public static bool AttributeAllowsMultiple(Attribute attribute) { - Attribute usageAttribute = Attribute.GetCustomAttribute(attribute.GetType(), typeof(AttributeUsageAttribute)); + var usageAttribute = Attribute.GetCustomAttribute(attribute.GetType(), typeof(AttributeUsageAttribute)); return ((AttributeUsageAttribute)usageAttribute).AllowMultiple; } + + /// + /// Returns true if the attribute can be declared more than one time for the same element and this attribute marked with [AllowsMultipleWithSameTags] + /// + /// + /// + public static bool AttributeAllowsMultipleWithIntersectingTags(Attribute attribute) + { + var attr = Attribute.GetCustomAttribute(attribute.GetType(), typeof(AllowsMultipleWithIntersectingTagsAttribute)); + return AttributeAllowsMultiple(attribute) && attr != null; + } + + /// + /// Return true if same attribute applied to property or field more then 1 time. It can happens when attribute used with different tags + /// + /// + /// + /// + public static bool AttributeUsedMultipleTimesOnProperty(Property property, System.Type attributeType) + { + //Next happens for component properties. Not possible to access component class metadata back from Property in NHib 5.0 + //TODO: How to fix this? + if (property.PersistentClass == null) + return false; + + var tp = System.Type.GetType(property.PersistentClass.ClassName); + var member = (MemberInfo) tp.GetProperty(property.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + if (member == null) + member = tp.GetField(property.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + if (member == null) + return false; + + var attributes = member.GetCustomAttributes(attributeType, true); + return attributes.Length > 1; + + } } }