Fixing an issue with output parameters that are of type IntPtr
[castle.git] / ActiveRecord / Castle.ActiveRecord / Framework / ActiveRecordValidationBase.cs
blob4da25b0d2bda0bdc7d21bc1e159187ddd153c0e3
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.ActiveRecord
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Reflection;
21 using Castle.ActiveRecord.Framework.Internal;
22 using Castle.Components.Validator;
24 /// <summary>
25 /// Extends <see cref="ActiveRecordBase"/> adding automatic validation support.
26 /// </summary>
27 /// <example>
28 /// <code>
29 /// using Castle.Components.Validator;
30 ///
31 /// public class Customer : ActiveRecordBase
32 /// {
33 /// ...
34 ///
35 /// [Property, ValidateNonEmpty]
36 /// public int Name
37 /// {
38 /// get { return _name; }
39 /// set { _name = value; }
40 /// }
41 ///
42 /// [Property, ValidateNonEmpty, ValidateEmail]
43 /// public int Email
44 /// {
45 /// get { return _email; }
46 /// set { _email = value; }
47 /// }
48 /// </code>
49 /// </example>
50 [Serializable]
51 public abstract class ActiveRecordValidationBase : ActiveRecordBase
53 [NonSerialized]
54 private ValidatorRunner __runner = new ValidatorRunner(ActiveRecordModelBuilder.ValidatorRegistry);
56 [System.Xml.Serialization.XmlIgnore]
57 private IDictionary __failedProperties;
59 /// <summary>
60 /// Constructs an ActiveRecordValidationBase
61 /// </summary>
62 public ActiveRecordValidationBase()
66 /// <summary>
67 /// Performs the fields validation. Returns true if no
68 /// validation error was found.
69 /// </summary>
70 /// <returns></returns>
71 public virtual bool IsValid()
73 return IsValid(RunWhen.Everytime);
76 /// <summary>
77 /// Performs the fields validation for the specified action.
78 /// </summary>
79 /// <param name="runWhen">Use validators appropriate to the action being performed.</param>
80 /// <returns>True if no validation error was found</returns>
81 public virtual bool IsValid(RunWhen runWhen)
83 __failedProperties = new Hashtable();
85 if (__runner == null)
87 __runner = new ValidatorRunner(ActiveRecordModelBuilder.ValidatorRegistry);
90 bool returnValue = __runner.IsValid(this, runWhen);
92 foreach (PropertyInfo propinfo in GetNestedPropertiesToValidate(this)) {
93 object propval = propinfo.GetValue(this, null);
95 if (propval != null) {
96 bool tmp = __runner.IsValid(propval, runWhen);
97 if (!tmp)
98 __failedProperties.Add(propinfo,
99 new ArrayList(__runner.GetErrorSummary(propval).GetErrorsForProperty(propinfo.Name)));
100 returnValue &= tmp;
104 if (!returnValue)
106 Type type = GetType();
107 ErrorSummary summary = __runner.GetErrorSummary(this);
109 foreach(string property in summary.InvalidProperties)
111 __failedProperties.Add(type.GetProperty(property), new ArrayList(summary.GetErrorsForProperty(property)));
115 return returnValue;
118 /// <summary>
119 /// Returns a list of current validation errors messages.
120 /// </summary>
121 public virtual String[] ValidationErrorMessages
125 if (__runner == null)
127 __runner = new ValidatorRunner(ActiveRecordModelBuilder.ValidatorRegistry);
130 if (__runner.GetErrorSummary(this) == null)
132 IsValid();
135 List<string> errorMessages = new List<string>(__runner.GetErrorSummary(this).ErrorMessages);
137 AddNestedPropertyValidationErrorMessages(errorMessages, this, __runner);
139 return errorMessages.ToArray();
143 /// <summary>
144 /// Maps a specific PropertyInfo to a list of
145 /// error messages. Useful for frameworks.
146 /// </summary>
147 [System.Xml.Serialization.XmlIgnore]
148 public virtual IDictionary PropertiesValidationErrorMessage
150 get { return __failedProperties; }
153 /// <summary>
154 /// Override the base hook to call validators required for create.
155 /// </summary>
156 /// <param name="state">The current state of the object</param>
157 /// <returns>Returns true if the state has changed otherwise false</returns>
158 protected internal override bool BeforeSave(IDictionary state)
160 if (!IsValid(RunWhen.Insert))
162 OnNotValid();
165 return base.BeforeSave(state);
168 /// <summary>
169 /// Override the base hook to call validators required for update.
170 /// </summary>
171 /// <param name="id">object id</param>
172 /// <param name="previousState">The previous state of the object</param>
173 /// <param name="currentState">The current state of the object</param>
174 /// <param name="types">Property types</param>
175 /// <returns>Returns true if the state has changed otherwise false</returns>
176 protected internal override bool OnFlushDirty(object id, IDictionary previousState, IDictionary currentState, NHibernate.Type.IType[] types)
178 if (!IsValid(RunWhen.Update))
180 OnNotValid();
183 return base.OnFlushDirty(id, previousState, currentState, types);
186 /// <summary>
187 /// Throws an exception explaining why the save or update
188 /// cannot be executed when fields are not ok to pass.
189 /// </summary>
190 /// <remarks>
191 /// You can override this method to declare a better behavior.
192 /// </remarks>
193 protected virtual void OnNotValid()
195 throw new ValidationException("Can't save or update as there is one (or more) " +
196 "field that has not passed the validation test", ValidationErrorMessages);
199 internal static IEnumerable GetNestedPropertiesToValidate(object instance)
201 Type type = instance.GetType();
202 ActiveRecordModel me = GetModel(type);
204 foreach (NestedModel component in me.Components) {
205 yield return component.Property;
209 internal static void AddNestedPropertyValidationErrorMessages(List<string> errorMessages, object instance, ValidatorRunner runner) {
210 foreach (PropertyInfo propinfo in GetNestedPropertiesToValidate(instance)) {
211 object propval = propinfo.GetValue(instance, null);
212 if (propval != null) {
213 errorMessages.AddRange(
214 runner.GetErrorSummary(propval).ErrorMessages);