1
// Copyright 2004-2008 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
.MonoRail
.Framework
.Helpers
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
20 using System
.Collections
.Specialized
;
22 using System
.Reflection
;
24 using Castle
.Components
.Binder
;
25 using Castle
.Components
.Validator
;
26 using Castle
.Core
.Logging
;
29 using ValidationStrategy
;
32 /// Represents all scopes that the <see cref="FormHelper"/>
33 /// uses to search for root values
35 public enum RequestContext
38 /// All scopes should be searched
42 /// Only PropertyBag should be searched
46 /// Only Flash should be searched
50 /// Only Session should be searched
54 /// Only Request should be searched
58 /// Only Params should be searched
64 /// Base class that exposes common operations for form handling, field bindings and so on.
66 public abstract class AbstractFormRelatedHelper
: AbstractHelper
69 /// Common property flags for reflection
71 protected static readonly BindingFlags PropertyFlags
= BindingFlags
.GetProperty
| BindingFlags
.Public
| BindingFlags
.Instance
| BindingFlags
.IgnoreCase
;
73 /// Common property flags for reflection (with declared only)
75 protected static readonly BindingFlags propertyFlagsDeclaredOnly
= BindingFlags
.GetProperty
| BindingFlags
.Public
| BindingFlags
.Instance
| BindingFlags
.IgnoreCase
| BindingFlags
.DeclaredOnly
;
77 /// Common field flags for reflection
79 protected static readonly BindingFlags FieldFlags
= BindingFlags
.GetField
| BindingFlags
.Public
| BindingFlags
.Instance
| BindingFlags
.IgnoreCase
;
84 protected static ILogger logger
= NullLogger
.Instance
;
89 protected BrowserValidationConfiguration validationConfig
;
93 private IBrowserValidatorProvider validatorProvider
;
97 protected readonly Stack objectStack
= new Stack();
99 private IValidatorRegistry validatorRegistry
;
100 private ValidatorRunner validatorRunner
;
101 private bool isValidationDisabled
;
104 /// Initializes a new instance of the <see cref="AbstractFormRelatedHelper"/> class.
106 protected AbstractFormRelatedHelper()
111 /// Initializes a new instance of the <see cref="AbstractFormRelatedHelper"/> class.
113 /// <param name="validatorRegistry">The validator registry.</param>
114 /// <param name="validatorRunner">The validator runner.</param>
115 protected AbstractFormRelatedHelper(IValidatorRegistry validatorRegistry
, ValidatorRunner validatorRunner
)
117 this.validatorRegistry
= validatorRegistry
;
118 this.validatorRunner
= validatorRunner
;
122 /// Initializes a new instance of the <see cref="AbstractFormRelatedHelper"/> class.
124 /// <param name="engineContext">The engine context.</param>
125 protected AbstractFormRelatedHelper(IEngineContext engineContext
) : base(engineContext
)
130 /// Gets or sets the validator provider.
132 /// <value>The validator provider.</value>
133 public IBrowserValidatorProvider ValidatorProvider
135 get { return validatorProvider; }
136 set { validatorProvider = value; }
140 /// Gets or sets the validator runner.
142 /// <value>The validator runner.</value>
143 public ValidatorRunner ValidatorRunner
145 get { return validatorRunner; }
146 set { validatorRunner = value; }
150 /// Gets or sets the validator registry.
152 /// <value>The validator registry.</value>
153 public IValidatorRegistry ValidatorRegistry
155 get { return validatorRegistry; }
156 set { validatorRegistry = value; }
159 #region protected members
162 /// Rewrites the target if within object scope.
164 /// <param name="target">The target.</param>
165 /// <returns></returns>
166 protected string RewriteTargetIfWithinObjectScope(string target
)
168 if (objectStack
.Count
== 0)
174 return ((FormScopeInfo
) objectStack
.Peek()).RootTarget
+ "." + target
;
179 /// Creates the specified input element
180 /// using the specified parameters to supply the name, value, id and others
183 /// <param name="type"></param>
184 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
185 /// <param name="value"></param>
186 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
187 /// <returns>The generated form element</returns>
188 protected virtual string CreateInputElement(string type
, string target
, Object
value, IDictionary attributes
)
192 value = CommonUtils
.ObtainEntryAndRemove(attributes
, "defaultValue");
195 string id
= CreateHtmlId(attributes
, target
);
197 return CreateInputElement(type
, id
, target
, FormatIfNecessary(value, attributes
), attributes
);
201 /// Creates the specified input element
202 /// using the specified parameters to supply the name, value, id and others
205 /// <param name="type"></param>
206 /// <param name="id"></param>
207 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
208 /// <param name="value"></param>
209 /// <param name="attributes">Attributes for the FormHelper method and for the html element it generates</param>
210 /// <returns>The generated form element</returns>
211 protected virtual string CreateInputElement(string type
, string id
, string target
, string value, IDictionary attributes
)
213 value = FormatIfNecessary(value, attributes
);
215 value = SafeHtmlEncode(value);
217 if (attributes
!= null && attributes
.Contains("mask"))
219 string mask
= CommonUtils
.ObtainEntryAndRemove(attributes
, "mask");
220 string maskSep
= CommonUtils
.ObtainEntryAndRemove(attributes
, "mask_separator", "-");
222 string onBlur
= CommonUtils
.ObtainEntryAndRemove(attributes
, "onBlur", "void(0)");
223 string onKeyUp
= CommonUtils
.ObtainEntryAndRemove(attributes
, "onKeyUp", "void(0)");
225 string js
= "return monorail_formhelper_mask(event,this,'" + mask
+ "','" + maskSep
+ "');";
227 attributes
["onBlur"] = "javascript:" + onBlur
+ ";" + js
;
228 attributes
["onKeyUp"] = "javascript:" + onKeyUp
+ ";" + js
;
231 return String
.Format("<input type=\"{0}\" id=\"{1}\" name=\"{2}\" value=\"{3}\" {4}/>",
232 type
, id
, target
, value, GetAttributes(attributes
));
236 /// Creates the input element.
238 /// <param name="type">The type.</param>
239 /// <param name="value">The value.</param>
240 /// <param name="attributes">The attributes.</param>
241 /// <returns></returns>
242 protected virtual string CreateInputElement(string type
, string value, IDictionary attributes
)
244 return String
.Format("<input type=\"{0}\" value=\"{1}\" {2}/>",
245 type
, FormatIfNecessary(value, attributes
), GetAttributes(attributes
));
249 /// Formats if necessary.
251 /// <param name="value">The value.</param>
252 /// <param name="attributes">The attributes.</param>
253 /// <returns></returns>
254 protected static string FormatIfNecessary(object value, IDictionary attributes
)
256 string formatString
= CommonUtils
.ObtainEntryAndRemove(attributes
, "textformat");
258 if (value != null && formatString
!= null)
260 IFormattable formattable
= value as IFormattable
;
262 if (formattable
!= null)
264 value = formattable
.ToString(formatString
, null);
267 else if (value == null)
269 value = String
.Empty
;
272 return value.ToString();
276 /// Obtains the target property.
278 /// <param name="context">The context.</param>
279 /// <param name="target">The target.</param>
280 /// <param name="action">The action.</param>
281 /// <returns></returns>
282 protected PropertyInfo
ObtainTargetProperty(RequestContext context
, string target
, Action
<PropertyInfo
> action
)
286 Type root
= ObtainRootType(context
, target
, out pieces
);
288 if (root
!= null && pieces
.Length
> 1)
290 return QueryPropertyInfoRecursive(root
, pieces
, 1, action
);
297 /// Queries the context for the target value
299 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
300 /// <returns>The generated form element</returns>
301 protected object ObtainValue(string target
)
303 return ObtainValue(RequestContext
.All
, target
);
307 /// Queries the context for the target value
309 /// <param name="context"></param>
310 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
311 /// <returns>The generated form element</returns>
312 protected object ObtainValue(RequestContext context
, string target
)
316 object rootInstance
= ObtainRootInstance(context
, target
, out pieces
);
318 if (rootInstance
!= null && pieces
.Length
> 1)
320 return QueryPropertyRecursive(rootInstance
, pieces
, 1);
327 /// Obtains the root instance.
329 /// <param name="context">The context.</param>
330 /// <param name="target">The object to get the value from and to be based on to create the element name.</param>
331 /// <returns>The generated form element</returns>
332 protected object ObtainRootInstance(RequestContext context
, string target
)
334 object rootInstance
= null;
336 if (context
== RequestContext
.All
|| context
== RequestContext
.PropertyBag
)
338 rootInstance
= ControllerContext
.PropertyBag
[target
];
340 if (rootInstance
== null && (context
== RequestContext
.All
|| context
== RequestContext
.Flash
))
342 rootInstance
= Context
.Flash
[target
];
344 if (rootInstance
== null && (context
== RequestContext
.All
|| context
== RequestContext
.Session
))
346 rootInstance
= Context
.Session
[target
];
348 if (rootInstance
== null && (context
== RequestContext
.All
|| context
== RequestContext
.Params
))
350 rootInstance
= Context
.Request
.Params
[target
];
352 if (rootInstance
== null && (context
== RequestContext
.All
|| context
== RequestContext
.Request
))
354 rootInstance
= Context
.Items
[target
];
361 /// Obtains the root instance.
363 /// <param name="context">The context.</param>
364 /// <param name="target">The target.</param>
365 /// <param name="pieces">The pieces.</param>
366 /// <returns></returns>
367 protected object ObtainRootInstance(RequestContext context
, string target
, out string[] pieces
)
369 pieces
= target
.Split(new char[] { '.' }
);
371 string root
= pieces
[0];
375 bool isIndexed
= CheckForExistenceAndExtractIndex(ref root
, out index
);
377 object rootInstance
= ObtainRootInstance(context
, root
);
379 if (rootInstance
== null)
386 AssertIsValidArray(rootInstance
, root
, index
);
389 if (!isIndexed
&& pieces
.Length
== 1)
395 rootInstance
= GetArrayElement(rootInstance
, index
);
402 /// Obtains the type of the root.
404 /// <param name="context">The context.</param>
405 /// <param name="target">The target.</param>
406 /// <param name="pieces">The pieces.</param>
407 /// <returns></returns>
408 private Type
ObtainRootType(RequestContext context
, string target
, out string[] pieces
)
410 pieces
= target
.Split(new char[] { '.' }
);
412 Type foundType
= (Type
) ControllerContext
.PropertyBag
[pieces
[0] + "type"];
414 if (foundType
== null)
416 string trimmed
= pieces
[0].Split('[')[0];
418 foundType
= (Type
) ControllerContext
.PropertyBag
[trimmed
+ "type"];
420 if (foundType
== null)
422 object root
= ObtainRootInstance(context
, target
, out pieces
);
426 foundType
= root
.GetType();
435 /// Queries the property info recursive.
437 /// <param name="type">The type.</param>
438 /// <param name="propertyPath">The property path.</param>
439 /// <returns></returns>
440 protected static PropertyInfo
QueryPropertyInfoRecursive(Type type
, string[] propertyPath
)
442 return QueryPropertyInfoRecursive(type
, propertyPath
, 0, null);
446 /// Queries the property info recursive.
448 /// <param name="type">The type.</param>
449 /// <param name="propertyPath">The property path.</param>
450 /// <param name="piece">The piece.</param>
451 /// <param name="action">The action.</param>
452 /// <returns></returns>
453 protected static PropertyInfo
QueryPropertyInfoRecursive(Type type
, string[] propertyPath
, int piece
, Action
<PropertyInfo
> action
)
455 string property
= propertyPath
[piece
];
458 bool isIndexed
= CheckForExistenceAndExtractIndex(ref property
, out index
);
460 //PropertyInfo propertyInfo = type.GetProperty(property, ResolveFlagsToUse(type));
461 PropertyInfo propertyInfo
= GetPropertyInfo(type
, property
);
463 if (propertyInfo
== null)
465 if (logger
.IsErrorEnabled
)
467 logger
.Error("No public property '{0}' found on type '{1}'", property
, type
.FullName
);
473 if (!propertyInfo
.CanRead
)
475 throw new BindingException("Property '{0}' for type '{1}' can not be read",
476 propertyInfo
.Name
, type
.FullName
);
479 if (propertyInfo
.GetIndexParameters().Length
!= 0)
481 throw new BindingException("Property '{0}' for type '{1}' has indexes, which are not supported",
482 propertyInfo
.Name
, type
.FullName
);
487 action(propertyInfo
);
490 type
= propertyInfo
.PropertyType
;
492 if (typeof(ICollection
).IsAssignableFrom(type
))
499 if (type
.IsGenericType
)
501 Type
[] args
= type
.GetGenericArguments();
502 if (args
.Length
!= 1)
503 throw new BindingException("Expected the generic indexed property '{0}' to be of 1 element", type
.Name
);
509 type
= type
.GetElementType();
513 if (piece
+ 1 == propertyPath
.Length
)
518 return QueryPropertyInfoRecursive(type
, propertyPath
, piece
+ 1, action
);
522 /// Query property paths agains the rootInstance type
524 /// <param name="rootInstance">the object to query</param>
525 /// <param name="propertyPath">property path</param>
526 /// <returns>The generated form element</returns>
527 protected static object QueryPropertyRecursive(object rootInstance
, string[] propertyPath
)
529 return QueryPropertyRecursive(rootInstance
, propertyPath
, 0);
533 /// Query property paths agains the rootInstance type
535 /// <param name="rootInstance">the object to query</param>
536 /// <param name="propertyPath">property path</param>
537 /// <param name="piece">start index</param>
538 /// <returns>The generated form element</returns>
539 protected static object QueryPropertyRecursive(object rootInstance
, string[] propertyPath
, int piece
)
541 string property
= propertyPath
[piece
];
544 Type instanceType
= rootInstance
.GetType();
546 bool isIndexed
= CheckForExistenceAndExtractIndex(ref property
, out index
);
548 //PropertyInfo propertyInfo = instanceType.GetProperty(property, ResolveFlagsToUse(instanceType));
549 PropertyInfo propertyInfo
= GetPropertyInfo(instanceType
, property
);
551 object instance
= null;
553 if (propertyInfo
== null)
555 FieldInfo fieldInfo
= instanceType
.GetField(property
, FieldFlags
);
557 if (fieldInfo
!= null)
559 instance
= fieldInfo
.GetValue(rootInstance
);
564 if (!propertyInfo
.CanRead
)
566 throw new BindingException("Property '{0}' for type '{1}' can not be read",
567 propertyInfo
.Name
, instanceType
.FullName
);
570 if (propertyInfo
.GetIndexParameters().Length
!= 0)
572 throw new BindingException("Property '{0}' for type '{1}' has indexes, which are not supported",
573 propertyInfo
.Name
, instanceType
.FullName
);
576 instance
= propertyInfo
.GetValue(rootInstance
, null);
579 if (isIndexed
&& instance
!= null)
581 AssertIsValidArray(instance
, property
, index
);
583 instance
= GetArrayElement(instance
, index
);
586 if (instance
== null || piece
+ 1 == propertyPath
.Length
)
591 return QueryPropertyRecursive(instance
, propertyPath
, piece
+ 1);
595 /// Creates the HTML id.
597 /// <param name="attributes">The attributes.</param>
598 /// <param name="target">The target.</param>
599 /// <returns>The generated form element</returns>
600 protected static string CreateHtmlId(IDictionary attributes
, string target
)
602 return CreateHtmlId(attributes
, target
, true);
606 /// Creates the HTML id.
608 /// <param name="attributes">The attributes.</param>
609 /// <param name="target">The target.</param>
610 /// <param name="removeEntry">if set to <c>true</c> [remove entry].</param>
611 /// <returns>The generated form element</returns>
612 protected static string CreateHtmlId(IDictionary attributes
, string target
, bool removeEntry
)
618 id
= CommonUtils
.ObtainEntryAndRemove(attributes
, "id");
622 id
= CommonUtils
.ObtainEntry(attributes
, "id");
627 id
= CreateHtmlId(target
);
639 /// Disables the validation.
641 public virtual void DisableValidation()
643 isValidationDisabled
= true;
647 /// Applies the validation.
649 /// <param name="inputType">Type of the input.</param>
650 /// <param name="target">The target.</param>
651 /// <param name="attributes">The attributes.</param>
652 protected virtual void ApplyValidation(InputElementType inputType
, string target
, ref IDictionary attributes
)
654 bool disableValidation
= CommonUtils
.ObtainEntryAndRemove(attributes
, "disablevalidation", "false") == "true";
656 if (!IsValidationEnabled
|| disableValidation
)
661 if (validatorRegistry
== null || validationConfig
== null)
666 if (attributes
== null)
668 attributes
= new HybridDictionary(true);
671 IValidator
[] validators
= CollectValidators(RequestContext
.All
, target
);
673 IBrowserValidationGenerator generator
= validatorProvider
.CreateGenerator(validationConfig
, inputType
, attributes
);
675 string id
= CreateHtmlId(attributes
, target
, false);
677 foreach (IValidator validator
in validators
)
679 if (validator
.SupportsBrowserValidation
)
681 validator
.ApplyBrowserValidation(validationConfig
, inputType
, generator
, attributes
, id
);
686 private IValidator
[] CollectValidators(RequestContext requestContext
, string target
)
688 List
<IValidator
> validators
= new List
<IValidator
>();
690 ObtainTargetProperty(requestContext
, target
, delegate(PropertyInfo property
)
692 validators
.AddRange(validatorRegistry
.GetValidators(validatorRunner
, property
.DeclaringType
, property
, RunWhen
.Everytime
));
695 return validators
.ToArray();
699 /// Gets a value indicating whether validation is enabled.
702 /// <c>true</c> if validation is enabled; otherwise, <c>false</c>.
704 protected bool IsValidationEnabled
708 if (isValidationDisabled
)
711 if (objectStack
.Count
== 0)
714 return ((FormScopeInfo
) objectStack
.Peek()).IsValidationEnabled
;
720 #region private helpers
722 private static void AssertIsValidArray(object instance
, string property
, int index
)
724 Type instanceType
= instance
.GetType();
726 IList list
= instance
as IList
;
728 bool validList
= false;
730 if (list
== null && instanceType
.IsGenericType
)
732 Type
[] genArgs
= instanceType
.GetGenericArguments();
734 Type genList
= typeof(System
.Collections
.Generic
.IList
<>).MakeGenericType(genArgs
);
735 Type genTypeDef
= instanceType
.GetGenericTypeDefinition().MakeGenericType(genArgs
);
737 validList
= genList
.IsAssignableFrom(genTypeDef
);
740 if (!validList
&& list
== null)
742 throw new MonoRailException("The property {0} is being accessed as " +
743 "an indexed property but does not seem to implement IList. " +
744 "In fact the type is {1}", property
, instanceType
.FullName
);
749 throw new MonoRailException("The specified index '{0}' is outside the bounds " +
750 "of the array. Property {1}", index
, property
);
754 private static object GetArrayElement(object instance
, int index
)
756 IList list
= instance
as IList
;
758 if (list
== null && instance
!= null && instance
.GetType().IsGenericType
)
760 Type instanceType
= instance
.GetType();
762 Type
[] genArguments
= instanceType
.GetGenericArguments();
764 Type genType
= instanceType
.GetGenericTypeDefinition().MakeGenericType(genArguments
);
766 // I'm not going to retest for IList implementation as
767 // if we got here, the AssertIsValidArray has run successfully
769 PropertyInfo countPropInfo
= genType
.GetProperty("Count");
771 int count
= (int) countPropInfo
.GetValue(instance
, null);
773 if (count
== 0 || index
+ 1 > count
)
778 PropertyInfo indexerPropInfo
= genType
.GetProperty("Item");
780 return indexerPropInfo
.GetValue(instance
, new object[] { index }
);
783 if (list
== null || list
.Count
== 0 || index
+ 1 > list
.Count
)
791 private static bool CheckForExistenceAndExtractIndex(ref string property
, out int index
)
793 bool isIndexed
= property
.IndexOf('[') != -1;
799 int start
= property
.IndexOf('[') + 1;
800 int len
= property
.IndexOf(']', start
) - start
;
802 string indexStr
= property
.Substring(start
, len
);
806 index
= Convert
.ToInt32(indexStr
);
810 throw new MonoRailException("Could not convert (param {0}) index to Int32. Value is {1}",
814 property
= property
.Substring(0, start
- 1);
821 /// Compares the left and right value for equality.
823 /// <param name="left">The left.</param>
824 /// <param name="right">The right.</param>
825 /// <returns></returns>
826 protected static bool AreEqual(object left
, object right
)
828 if (left
== null || right
== null)
831 if (left
is string && right
is String
)
833 return String
.Compare(left
.ToString(), right
.ToString()) == 0;
836 if (left
.GetType() == right
.GetType())
838 return right
.Equals(left
);
841 IConvertible convertible
= left
as IConvertible
;
843 if (convertible
!= null)
847 object newleft
= convertible
.ToType(right
.GetType(), null);
848 return (newleft
.Equals(right
));
856 return left
.ToString().Equals(right
.ToString());
860 /// Encodes the content if there is a valid context set for this helper.
862 /// <param name="content">The content to be encoded.</param>
863 /// <returns></returns>
864 protected string SafeHtmlEncode(string content
)
868 return HtmlEncode(content
);
875 /// Determines whether the present value matches the value on
876 /// the initialSetValue (which can be a single value or a set)
878 /// <param name="value">Value from the datasource</param>
879 /// <param name="initialSetValue">Value from the initial selection set</param>
880 /// <param name="propertyOnInitialSet">Optional. Property to obtain the value from</param>
881 /// <param name="isMultiple"><c>true</c> if the initial selection is a set</param>
882 /// <returns><c>true</c> if it's selected</returns>
883 protected internal static bool IsPresent(object value, object initialSetValue
,
884 ValueGetter propertyOnInitialSet
, bool isMultiple
)
888 object valueToCompare
= initialSetValue
;
890 if (propertyOnInitialSet
!= null)
892 // propertyOnInitialSet.GetValue(initialSetValue, null);
893 valueToCompare
= propertyOnInitialSet
.GetValue(initialSetValue
);
896 return AreEqual(value, valueToCompare
);
900 foreach (object item
in (IEnumerable
) initialSetValue
)
902 object valueToCompare
= item
;
904 if (propertyOnInitialSet
!= null)
906 // valueToCompare = propertyOnInitialSet.GetValue(item, null);
907 valueToCompare
= propertyOnInitialSet
.GetValue(item
);
910 if (AreEqual(value, valueToCompare
))
921 /// Adds the checked attribute to the attributes.
923 /// <param name="attributes">The attributes.</param>
924 protected static void AddChecked(IDictionary attributes
)
926 attributes
["checked"] = "checked";
930 /// Removes the checked attribute from the dictionary.
932 /// <param name="attributes">The attributes.</param>
933 protected static void RemoveChecked(IDictionary attributes
)
935 attributes
.Remove("checked");
939 /// Uses the specified name to create a valid id for the element
941 /// <param name="name">The name.</param>
942 /// <returns></returns>
943 protected static string CreateHtmlId(string name
)
945 StringBuilder sb
= new StringBuilder(name
.Length
);
947 bool canUseUnderline
= false;
949 foreach (char c
in name
.ToCharArray())
959 canUseUnderline
= false;
963 canUseUnderline
= true;
970 return sb
.ToString();
974 /// Abstracts the approach to access values on objects.
976 public abstract class ValueGetter
981 /// <value>The name.</value>
982 public abstract string Name { get; }
987 /// <param name="instance">The instance.</param>
988 /// <returns></returns>
989 public abstract object GetValue(object instance
);
993 /// Implementation of <see cref="ValueGetter"/>
994 /// that uses reflection to access values
996 public class ReflectionValueGetter
: ValueGetter
998 private PropertyInfo propInfo
;
1001 /// Initializes a new instance of the <see cref="ReflectionValueGetter"/> class.
1003 /// <param name="propInfo">The prop info.</param>
1004 public ReflectionValueGetter(PropertyInfo propInfo
)
1006 this.propInfo
= propInfo
;
1012 /// <value>The name.</value>
1013 public override string Name
1015 get { return propInfo.Name; }
1021 /// <param name="instance">The instance.</param>
1022 /// <returns></returns>
1023 public override object GetValue(object instance
)
1027 return propInfo
.GetValue(instance
, null);
1029 catch (TargetException
)
1031 PropertyInfo tempProp
= instance
.GetType().GetProperty(Name
);
1033 if (tempProp
== null)
1038 return tempProp
.GetValue(instance
, null);
1044 /// Implementation of <see cref="ValueGetter"/>
1045 /// that uses reflection and recusion to access values
1047 public class RecursiveReflectionValueGetter
: ValueGetter
1049 private readonly string[] keyName
;
1050 private readonly string name
= string.Empty
;
1053 /// Initializes a new instance of the <see cref="RecursiveReflectionValueGetter"/> class.
1055 /// <param name="targetType">The target type to query</param>
1056 /// <param name="keyName">the property path</param>
1057 public RecursiveReflectionValueGetter(Type targetType
, string keyName
)
1059 this.keyName
= keyName
.Split('.');
1060 name
= QueryPropertyInfoRecursive(targetType
, this.keyName
).Name
;
1066 /// <value>The name.</value>
1067 public override string Name
1069 get { return name; }
1075 /// <param name="instance">The instance.</param>
1076 /// <returns></returns>
1077 public override object GetValue(object instance
)
1081 return QueryPropertyRecursive(instance
, keyName
);
1083 catch (TargetException
)
1085 PropertyInfo tempProp
= instance
.GetType().GetProperty(Name
);
1087 if (tempProp
== null)
1092 return tempProp
.GetValue(instance
, null);
1098 /// Implementation of <see cref="ValueGetter"/>
1099 /// to access DataRow's value
1101 public class DataRowValueGetter
: ValueGetter
1103 private readonly string columnName
;
1106 /// Initializes a new instance of the <see cref="DataRowValueGetter"/> class.
1108 /// <param name="columnName">Name of the column.</param>
1109 public DataRowValueGetter(string columnName
)
1111 this.columnName
= columnName
;
1117 /// <value>The name.</value>
1118 public override string Name
1120 get { return columnName; }
1126 /// <param name="instance">The instance.</param>
1127 /// <returns></returns>
1128 public override object GetValue(object instance
)
1130 DataRow row
= (DataRow
) instance
;
1132 return row
[columnName
];
1137 /// Implementation of <see cref="ValueGetter"/>
1138 /// to access DataRowView's value
1140 public class DataRowViewValueGetter
: ValueGetter
1142 private readonly string columnName
;
1145 /// Initializes a new instance of the <see cref="DataRowViewValueGetter"/> class.
1147 /// <param name="columnName">Name of the column.</param>
1148 public DataRowViewValueGetter(string columnName
)
1150 this.columnName
= columnName
;
1156 /// <value>The name.</value>
1157 public override string Name
1159 get { return columnName; }
1165 /// <param name="instance">The instance.</param>
1166 /// <returns></returns>
1167 public override object GetValue(object instance
)
1169 DataRowView row
= (DataRowView
) instance
;
1171 return row
[columnName
];
1176 /// Empty implementation of a <see cref="ValueGetter"/>
1178 public class NoActionGetter
: ValueGetter
1183 /// <value>The name.</value>
1184 public override string Name
1186 get { return string.Empty; }
1192 /// <param name="instance">The instance.</param>
1193 /// <returns></returns>
1194 public override object GetValue(object instance
)
1201 /// Implementation of <see cref="ValueGetter"/>
1202 /// to access enum fields
1204 public class EnumValueGetter
: ValueGetter
1206 private readonly Type enumType
;
1209 /// Initializes a new instance of the <see cref="EnumValueGetter"/> class.
1211 /// <param name="enumType">Type of the enum.</param>
1212 public EnumValueGetter(Type enumType
)
1214 this.enumType
= enumType
;
1220 /// <value>The name.</value>
1221 public override string Name
1223 get { return string.Empty; }
1229 /// <param name="instance">The instance.</param>
1230 /// <returns></returns>
1231 public override object GetValue(object instance
)
1233 return Convert
.ToDecimal(Enum
.Format(enumType
, Enum
.Parse(enumType
, Convert
.ToString(instance
)), "d"));
1238 /// Abstract factory for <see cref="ValueGetter"/> implementations
1240 public class ValueGetterAbstractFactory
1243 /// Creates the specified target type.
1245 /// <param name="targetType">Type of the target.</param>
1246 /// <param name="keyName">Name of the key.</param>
1247 /// <returns></returns>
1248 public static ValueGetter
Create(Type targetType
, string keyName
)
1250 if (targetType
== null)
1252 return new NoActionGetter();
1254 else if (targetType
== typeof(DataRow
))
1256 return new DataRowValueGetter(keyName
);
1258 else if (targetType
== typeof(DataRowView
))
1260 return new DataRowViewValueGetter(keyName
);
1262 else if (targetType
.IsEnum
)
1264 return new EnumValueGetter(targetType
);
1268 PropertyInfo info
= null;
1270 // check for recursion
1271 if (keyName
.Contains("."))
1273 info
= QueryPropertyInfoRecursive(targetType
, keyName
.Split('.'));
1277 return new RecursiveReflectionValueGetter(targetType
, keyName
);
1282 info
= GetPropertyInfo(targetType
, keyName
);
1286 return new ReflectionValueGetter(info
);
1296 /// Gets the property info for the property with the <paramref name="propertyName"/>.
1298 /// <param name="type">Type that has the property.</param>
1299 /// <param name="propertyName">Name of the property.</param>
1300 /// <returns></returns>
1301 protected static PropertyInfo
GetPropertyInfo(Type type
, string propertyName
)
1303 PropertyInfo info
= null;
1307 info
= type
.GetProperty(propertyName
, ResolveFlagsToUse(type
));
1309 catch (AmbiguousMatchException amex
)
1311 // This is kind of an edge case, so tried it the normal way first,
1312 // seems to happen if you override a generic property
1314 if (logger
.IsDebugEnabled
)
1316 logger
.DebugFormat(amex
, "Retrieving property {0} on type {1} raised a {2}. Maybe it is generic and overriden. Will try to get the most specific property now.",
1317 propertyName
, type
, amex
.GetType().Name
);
1320 // Try again on instance only, loop through base type hierarchy
1321 Type baseType
= type
;
1322 while (info
== null)
1324 info
= baseType
.GetProperty(propertyName
, propertyFlagsDeclaredOnly
);
1326 baseType
= baseType
.BaseType
;
1327 if (baseType
== typeof(object) || baseType
== null)
1337 private static BindingFlags
ResolveFlagsToUse(Type type
)
1339 if (type
.Assembly
.FullName
.StartsWith("DynamicAssemblyProxyGen") || type
.Assembly
.FullName
.StartsWith("DynamicProxyGenAssembly2"))
1341 return propertyFlagsDeclaredOnly
;
1344 return PropertyFlags
;
1349 #region FormScopeInfo
1352 /// Groups validation enabled configuration for a target set
1354 protected class FormScopeInfo
1356 private readonly string target
;
1357 private readonly bool isValidationEnabled
;
1360 /// Initializes a new instance of the <see cref="FormScopeInfo"/> class.
1362 /// <param name="target">The target.</param>
1363 /// <param name="isValidationEnabled">if set to <c>true</c> [is validation enabled].</param>
1364 public FormScopeInfo(string target
, bool isValidationEnabled
)
1366 this.target
= target
;
1367 this.isValidationEnabled
= isValidationEnabled
;
1371 /// Gets the root target.
1373 /// <value>The root target.</value>
1374 public string RootTarget
1376 get { return target; }
1380 /// Gets a value indicating whether this instance is validation enabled.
1383 /// <c>true</c> if this instance is validation enabled; otherwise, <c>false</c>.
1385 public bool IsValidationEnabled
1387 get { return isValidationEnabled; }