Fixing an issue with output parameters that are of type IntPtr
[castle.git] / Components / Binder / Castle.Components.Binder / DataBinder.cs
blob3dc544800e352ca2ee4dccf8f064be6bf5eb3b20
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.Components.Binder
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using System.Reflection;
22 using System.Web;
23 using Castle.Components.Validator;
24 using Castle.Core;
25 using Castle.Core.Logging;
27 /// <summary>
28 /// </summary>
29 [Serializable]
30 public class DataBinder : MarshalByRefObject, IDataBinder, IServiceEnabledComponent
32 protected internal static readonly BindingFlags PropertiesBindingFlags =
33 BindingFlags.Instance | BindingFlags.Public;
35 private IConverter converter = new DefaultConverter();
37 private ValidatorRunner validator;
39 /// <summary>Collect the databind errors</summary>
40 protected IList errors;
42 /// <summary>Holds a sorted array of properties names that should be ignored</summary>
43 private string[] excludedPropertyList;
45 /// <summary>Holds a sorted array of properties names that are on the white list</summary>
46 private string[] allowedPropertyList;
48 private Stack instanceStack;
49 private Stack<string> prefixStack;
51 private IDictionary<object, ErrorSummary> validationErrorSummaryPerInstance = new Dictionary<object, ErrorSummary>();
53 private IBinderTranslator binderTranslator;
55 private ILogger logger = NullLogger.Instance;
57 public event BinderHandler OnBeforeBinding;
59 public event BinderHandler OnAfterBinding;
61 /// <summary>
62 /// Initializes a new instance of the <see cref="DataBinder"/> class.
63 /// </summary>
64 public DataBinder()
68 #region IServiceEnabledComponent
70 public void Service(IServiceProvider provider)
72 ILoggerFactory loggerFactory = (ILoggerFactory) provider.GetService(typeof(ILoggerFactory));
74 if (loggerFactory != null)
76 logger = loggerFactory.Create(typeof(DataBinder));
80 #endregion
82 #region IDataBinder
84 public bool CanBindParameter(Type desiredType, String paramName, CompositeNode treeRoot)
86 bool canConvert;
88 Node childNode = treeRoot.GetChildNode(paramName);
90 if (childNode != null)
92 canConvert = true;
94 else if (desiredType == typeof(DateTime))
96 TrySpecialDateTimeBinding(desiredType, treeRoot, paramName, out canConvert);
98 else
100 canConvert = false;
103 return canConvert;
106 public bool CanBindObject(Type targetType, String prefix, CompositeNode treeRoot)
108 Node childNode = treeRoot.GetChildNode(prefix);
110 return childNode != null;
113 public object BindParameter(Type desiredType, String paramName, CompositeNode treeRoot)
115 bool conversionSucceeded;
116 object result;
120 if (desiredType.IsArray)
122 Node childNode = treeRoot.GetChildNode(paramName);
124 result = ConvertToArray(desiredType, paramName, childNode, out conversionSucceeded);
126 else
128 result = ConvertToSimpleValue(desiredType, paramName, treeRoot, out conversionSucceeded);
131 catch(Exception ex)
133 // Something unexpected during convertion
134 // throw new exception with paramName specified
136 throw new BindingException(
137 "Exception converting param '" + paramName + "' to " + desiredType + ". Check inner exception for details", ex);
140 return result;
143 public object BindObject(Type targetType, string prefix, CompositeNode treeRoot)
145 return BindObject(targetType, prefix, null, null, treeRoot);
148 public object BindObject(Type targetType, string prefix, string excludedProperties, string allowedProperties,
149 CompositeNode treeRoot)
151 if (targetType == null)
153 throw new ArgumentNullException("targetType");
155 if (prefix == null)
157 throw new ArgumentNullException("prefix");
159 if (treeRoot == null)
161 throw new ArgumentNullException("treeRoot");
164 errors = new ArrayList();
165 instanceStack = new Stack();
166 prefixStack = new Stack<string>();
168 excludedPropertyList = CreateNormalizedList(excludedProperties);
169 allowedPropertyList = CreateNormalizedList(allowedProperties);
171 return InternalBindObject(targetType, prefix, treeRoot.GetChildNode(prefix));
174 public void BindObjectInstance(object instance, string prefix, CompositeNode treeRoot)
176 BindObjectInstance(instance, prefix, null, null, treeRoot);
179 public void BindObjectInstance(object instance, string prefix, string excludedProperties, string allowedProperties,
180 CompositeNode treeRoot)
182 if (instance == null)
184 throw new ArgumentNullException("instance");
186 if (prefix == null)
188 throw new ArgumentNullException("prefix");
190 if (treeRoot == null)
192 throw new ArgumentNullException("treeRoot");
195 errors = new ArrayList();
196 instanceStack = new Stack();
197 prefixStack = new Stack<string>();
199 excludedPropertyList = CreateNormalizedList(excludedProperties);
200 allowedPropertyList = CreateNormalizedList(allowedProperties);
202 InternalRecursiveBindObjectInstance(instance, prefix, treeRoot.GetChildNode(prefix));
205 /// <summary>
206 /// Represents the databind errors
207 /// </summary>
208 public ErrorList ErrorList
210 get { return new ErrorList(errors); }
213 public IConverter Converter
215 get { return converter; }
216 set { converter = value; }
219 public ValidatorRunner Validator
221 get { return validator; }
222 set { validator = value; }
225 public IBinderTranslator Translator
227 get { return binderTranslator; }
228 set { binderTranslator = value; }
231 /// <summary>
232 /// Gets the validation error summary.
233 /// </summary>
234 /// <param name="instance">The instance.</param>
235 public ErrorSummary GetValidationSummary(object instance)
237 return validationErrorSummaryPerInstance.ContainsKey(instance) ? validationErrorSummaryPerInstance[instance] : null;
240 #endregion
242 #region Implementation
244 protected object InternalBindObject(Type instanceType, String paramPrefix, Node node)
246 bool succeeded;
247 return InternalBindObject(instanceType, paramPrefix, node, out succeeded);
250 protected object InternalBindObject(Type instanceType, String paramPrefix, Node node, out bool succeeded)
252 succeeded = false;
254 if (IsSpecialType(instanceType))
256 return BindSpecialObjectInstance(instanceType, paramPrefix, node, out succeeded);
259 if (ShouldIgnoreType(instanceType))
261 return null;
264 if (instanceType.IsArray)
266 return InternalBindObjectArray(instanceType, paramPrefix, node, out succeeded);
268 else if (IsGenericList(instanceType))
270 return InternalBindGenericList(instanceType, paramPrefix, node, out succeeded);
272 else
274 succeeded = true;
275 object instance = CreateInstance(instanceType, paramPrefix, node);
276 InternalRecursiveBindObjectInstance(instance, paramPrefix, node);
277 return instance;
281 protected void InternalRecursiveBindObjectInstance(object instance, String prefix, Node node)
283 if (node == null)
285 return;
288 if (node.NodeType != NodeType.Composite && node.NodeType != NodeType.Indexed)
290 throw new BindingException(
291 "Non-composite node passed to InternalRecursiveBindObjectInstance while binding {0} with prefix {1}", instance,
292 prefix);
295 InternalRecursiveBindObjectInstance(instance, prefix, (CompositeNode) node);
298 protected void InternalRecursiveBindObjectInstance(object instance, String prefix, CompositeNode node)
300 if (node == null || instance == null)
302 return;
305 BeforeBinding(instance, prefix, node);
307 if (PerformCustomBinding(instance, prefix, node))
309 return;
312 PushInstance(instance, prefix);
314 ErrorSummary summary = new ErrorSummary();
316 validationErrorSummaryPerInstance[instance] = summary;
318 Type instanceType = instance.GetType();
320 PropertyInfo[] props = instanceType.GetProperties(PropertiesBindingFlags);
322 string nodeFullName = node.FullName;
324 foreach(PropertyInfo prop in props)
326 if (ShouldIgnoreProperty(prop, nodeFullName))
328 continue;
331 Type propType = prop.PropertyType;
332 String paramName = prop.Name;
334 String translatedParamName = Translate(instanceType, paramName);
336 if (translatedParamName == null)
338 continue;
341 bool isSimpleProperty = IsSimpleProperty(propType);
343 // There are some caveats by running the validators here.
344 // We should follow the validator's execution order...
345 if (isSimpleProperty)
347 if (CheckForValidationFailures(instance, instanceType, prop, node, translatedParamName, prefix, summary))
349 continue;
353 BeforeBindingProperty(instance, prop, prefix, node);
357 bool conversionSucceeded;
359 if (isSimpleProperty)
361 object value = ConvertToSimpleValue(propType, translatedParamName, node, out conversionSucceeded);
363 if (conversionSucceeded)
365 SetPropertyValue(instance, prop, value);
368 else
370 // if the property is an object, we look if it is already instanciated
371 object value = prop.GetValue(instance, null);
373 Node nestedNode = node.GetChildNode(paramName);
375 if (nestedNode != null)
377 if (ShouldRecreateInstance(value, propType, paramName, nestedNode))
379 value = InternalBindObject(propType, paramName, nestedNode, out conversionSucceeded);
381 if (conversionSucceeded)
383 SetPropertyValue(instance, prop, value);
386 else
388 InternalRecursiveBindObjectInstance(value, paramName, nestedNode);
392 CheckForValidationFailures(instance, instanceType, prop, value, translatedParamName, prefix, summary);
395 catch(Exception ex)
397 errors.Add(new DataBindError(prefix, prop.Name, ex));
401 PopInstance(instance, prefix);
403 AfterBinding(instance, prefix, node);
406 /// <summary>
407 /// Sets the property value of the object we are binding.
408 /// Databinders that require different ways to access properties
409 /// can override this method.
410 /// </summary>
411 /// <param name="instance"></param>
412 /// <param name="prop"></param>
413 /// <param name="value"></param>
414 protected virtual void SetPropertyValue(object instance, PropertyInfo prop, object value)
416 if (!prop.CanWrite)
418 return;
420 prop.SetValue(instance, value, null);
423 protected bool CheckForValidationFailures(object instance, Type instanceType,
424 PropertyInfo prop, CompositeNode node,
425 string name, string prefix,
426 ErrorSummary summary)
428 object value = null;
430 if (validator == null)
432 return false;
435 IValidator[] validators = validator.GetValidators(instanceType, prop);
437 if (validators.Length != 0)
439 Node valNode = node.GetChildNode(name);
441 if (valNode != null && valNode.NodeType == NodeType.Leaf)
443 value = ((LeafNode)valNode).Value;
446 if (value == null && IsDateTimeType(prop.PropertyType))
448 bool conversionSucceeded;
449 value = TryGetDateWithUTCFormat(node, name, out conversionSucceeded);
452 if (value == null && valNode == null)
454 // Value was not present on the data source. Skip validation
455 return false;
459 return CheckForValidationFailures(instance, instanceType, prop, value, name, prefix, summary);
462 protected bool CheckForValidationFailures(object instance, Type instanceType,
463 PropertyInfo prop, object value,
464 string name, string prefix,
465 ErrorSummary summary)
467 bool hasFailure = false;
469 if (validator == null)
471 return false;
474 IValidator[] validators = validator.GetValidators(instanceType, prop);
476 foreach(IValidator validatorItem in validators)
478 if (!validatorItem.IsValid(instance, value))
480 string propName = validatorItem.FriendlyName ?? validatorItem.Name;
482 errors.Add(new DataBindError(prefix, prop.Name, validatorItem.ErrorMessage));
484 summary.RegisterErrorMessage(propName, validatorItem.ErrorMessage);
486 hasFailure = true;
490 return hasFailure;
493 protected int StackDepth
495 get { return instanceStack.Count; }
498 private string Translate(Type instanceType, string paramName)
500 if (binderTranslator != null)
502 return binderTranslator.Translate(instanceType, paramName);
505 return paramName;
508 private object InternalBindObjectArray(Type instanceType, String paramPrefix, Node node, out bool succeeded)
510 succeeded = false;
512 if (node == null)
514 return Array.CreateInstance(instanceType.GetElementType(), 0);
517 return ConvertToArray(instanceType, paramPrefix, node, out succeeded);
520 internal static bool IsGenericList(Type instanceType)
522 if (!instanceType.IsGenericType)
524 return false;
526 if (typeof(IList).IsAssignableFrom(instanceType))
528 return true;
531 Type[] genericArgs = instanceType.GetGenericArguments();
533 if (genericArgs.Length == 0)
535 return false;
537 Type listType = typeof(IList<>).MakeGenericType(genericArgs[0]);
538 return listType.IsAssignableFrom(instanceType);
541 private object InternalBindGenericList(Type instanceType, string paramPrefix, Node node, out bool succeeded)
543 succeeded = false;
545 if (node == null)
547 return CreateInstance(instanceType, paramPrefix, node);
550 return ConvertToGenericList(instanceType, paramPrefix, node, out succeeded);
553 #endregion
555 #region CreateInstance
557 protected virtual object CreateInstance(Type instanceType, String paramPrefix, Node node)
559 const BindingFlags creationFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
561 return Activator.CreateInstance(instanceType, creationFlags, null, null, null);
564 #endregion
566 #region Overridables
568 protected virtual void AfterBinding(object instance, String prefix, Node node)
570 if (OnAfterBinding != null)
572 OnAfterBinding(instance, prefix, node);
576 protected virtual void BeforeBinding(object instance, String prefix, Node node)
578 if (OnBeforeBinding != null)
580 OnBeforeBinding(instance, prefix, node);
584 protected virtual void BeforeBindingProperty(object instance, PropertyInfo prop, string prefix, CompositeNode node)
588 protected virtual bool ShouldRecreateInstance(object value, Type type, String prefix, Node node)
590 return value == null || type.IsArray || IsGenericList(type);
593 protected virtual bool ShouldIgnoreType(Type instanceType)
595 bool ignore = instanceType.IsAbstract || instanceType.IsInterface;
597 if (ignore && instanceType.IsGenericType)
599 ignore = !IsGenericList(instanceType);
602 return ignore;
605 protected virtual bool PerformCustomBinding(object instance, string prefix, Node node)
607 return false;
610 /// <summary>
611 /// Implementations will bound the instance itself.
612 /// <seealso cref="IsSpecialType"/>
613 /// </summary>
614 /// <remarks>
615 /// <seealso cref="IsSpecialType"/>
616 /// </remarks>
617 /// <param name="instanceType"></param>
618 /// <param name="prefix"></param>
619 /// <param name="node"></param>
620 /// <param name="succeeded"></param>
621 protected virtual object BindSpecialObjectInstance(Type instanceType, String prefix,
622 Node node, out bool succeeded)
624 succeeded = false;
626 return null;
629 /// <summary>
630 /// Invoked during object binding to allow
631 /// subclasses to have a chance of binding the types itself.
632 /// If the implementation returns <c>true</c>
633 /// the binder will invoke <see cref="BindSpecialObjectInstance"/>
634 /// </summary>
635 /// <param name="instanceType">Type about to be bound</param>
636 /// <returns><c>true</c> if subclass wants to handle binding</returns>
637 protected virtual bool IsSpecialType(Type instanceType)
639 return false;
642 #endregion
644 protected ILogger Logger
646 get { return logger; }
649 protected object ConvertLeafNode(Type desiredType, LeafNode lNode, out bool conversionSucceeded)
651 return Converter.Convert(desiredType, lNode.ValueType, lNode.Value, out conversionSucceeded);
654 private object ConvertToSimpleValue(Type desiredType, string key, CompositeNode parent, out bool conversionSucceeded)
656 conversionSucceeded = false;
658 Node childNode = parent.GetChildNode(key);
660 if (childNode == null && IsDateTimeType(desiredType))
662 return TrySpecialDateTimeBinding(desiredType, parent, key, out conversionSucceeded);
664 else if (childNode == null)
666 return null;
668 else if (childNode.NodeType == NodeType.Leaf)
670 return ConvertLeafNode(desiredType, (LeafNode) childNode, out conversionSucceeded);
672 else
674 throw new BindingException("Could not convert param as the node related " +
675 "to the param is not a leaf node. Param {0} parent node: {1}", key, parent.Name);
679 private object ConvertToArray(Type desiredType, String key, Node node, out bool conversionSucceeded)
681 Type elemType = desiredType.GetElementType();
683 if (node == null)
685 conversionSucceeded = false;
686 return Array.CreateInstance(elemType, 0);
688 else if (node.NodeType == NodeType.Leaf)
690 LeafNode leafNode = node as LeafNode;
692 return Converter.Convert(desiredType, leafNode.ValueType, leafNode.Value, out conversionSucceeded);
694 else if (node.NodeType == NodeType.Indexed)
696 IndexedNode indexedNode = node as IndexedNode;
698 if (IsSimpleProperty(elemType))
700 return ConvertFlatNodesToArray(desiredType, indexedNode.ChildNodes, out conversionSucceeded);
702 else
704 return ConvertComplexNodesToArray(desiredType, indexedNode, out conversionSucceeded);
707 else
709 throw new BindingException("Could not convert param to array as the node related " +
710 "to the param is not a leaf node nor an indexed node. Key {0}", key);
714 private object ConvertComplexNodesToArray(Type desiredType, IndexedNode parent, out bool conversionSucceeded)
716 Type arrayElemType = desiredType.GetElementType();
718 ArrayList validItems = ConvertComplexNodesToList(arrayElemType, parent, out conversionSucceeded);
720 return conversionSucceeded ? validItems.ToArray(arrayElemType) : Array.CreateInstance(arrayElemType, 0);
723 private ArrayList ConvertComplexNodesToList(Type elemType, IndexedNode parent, out bool conversionSucceeded)
725 conversionSucceeded = true;
727 ArrayList validItems = new ArrayList();
729 foreach(Node node in parent.ChildNodes)
731 if (node.NodeType == NodeType.Composite)
733 CompositeNode lnode = node as CompositeNode;
735 validItems.Add(InternalBindObject(elemType, parent.Name, lnode, out conversionSucceeded));
737 if (!conversionSucceeded)
739 break;
744 return validItems;
747 private object ConvertFlatNodesToArray(Type desiredType, Node[] nodes, out bool conversionSucceeded)
749 Type arrayElemType = desiredType.GetElementType();
751 ArrayList validItems = ConvertFlatNodesToList(arrayElemType, nodes, out conversionSucceeded);
753 return conversionSucceeded ? validItems.ToArray(arrayElemType) : Array.CreateInstance(arrayElemType, 0);
756 private ArrayList ConvertFlatNodesToList(Type elemType, Node[] nodes, out bool conversionSucceeded)
758 conversionSucceeded = true;
760 ArrayList validItems = new ArrayList();
762 foreach(Node node in nodes)
764 if (node.Name != String.Empty)
766 throw new BindingException("Unexpected non-flat node found: {0}", node.Name);
769 if (node.NodeType == NodeType.Leaf)
771 LeafNode lnode = node as LeafNode;
773 validItems.Add(ConvertLeafNode(elemType, lnode, out conversionSucceeded));
775 if (!conversionSucceeded)
777 break;
782 return validItems;
785 private object ConvertToGenericList(Type desiredType, String key, Node node, out bool conversionSucceeded)
787 Type[] genericArgs = desiredType.GetGenericArguments();
789 if (genericArgs.Length == 0)
791 throw new BindingException("Can't infer the Generics placeholders (type parameters). Key {0}.", key);
794 Type elemType = genericArgs[0];
796 if (node == null)
798 conversionSucceeded = false;
799 return CreateInstance(desiredType, key, node);
801 else if (node.NodeType == NodeType.Leaf)
803 LeafNode leafNode = node as LeafNode;
805 return Converter.Convert(desiredType, leafNode.ValueType, leafNode.Value, out conversionSucceeded);
807 else if (node.NodeType == NodeType.Indexed)
809 IndexedNode indexedNode = node as IndexedNode;
811 IList convertedNodes;
813 if (IsSimpleProperty(elemType))
815 convertedNodes = ConvertFlatNodesToList(elemType, indexedNode.ChildNodes, out conversionSucceeded);
817 else
819 convertedNodes = ConvertComplexNodesToList(elemType, indexedNode, out conversionSucceeded);
822 Type desiredImplType = desiredType.IsInterface
823 ? typeof(List<>).MakeGenericType(elemType)
824 : desiredType;
825 IList target = (IList)CreateInstance(desiredImplType, key, node);
827 foreach(object elem in convertedNodes)
829 target.Add(elem);
832 return target;
834 else
836 throw new BindingException("Could not convert param to generic list as the node related " +
837 "to the param is not a leaf node nor an indexed node. Key {0}", key);
841 private string TryGetDateWithUTCFormat(CompositeNode treeRoot, string paramName, out bool conversionSucceeded)
843 // YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
844 string fullDateTime = "";
845 conversionSucceeded = false;
847 Node dayNode = treeRoot.GetChildNode(paramName + "day");
848 Node monthNode = treeRoot.GetChildNode(paramName + "month");
849 Node yearNode = treeRoot.GetChildNode(paramName + "year");
851 Node hourNode = treeRoot.GetChildNode(paramName + "hour");
852 Node minuteNode = treeRoot.GetChildNode(paramName + "minute");
853 Node secondNode = treeRoot.GetChildNode(paramName + "second");
855 if (dayNode != null)
857 int day = (int) RelaxedConvertLeafNode(typeof(int), dayNode, 0);
858 int month = (int) RelaxedConvertLeafNode(typeof(int), monthNode, 0);
859 int year = (int) RelaxedConvertLeafNode(typeof(int), yearNode, 0);
861 fullDateTime = string.Format("{0:0000}-{1:00}-{2:00}", year, month, day);
862 conversionSucceeded = true;
865 if (hourNode != null)
867 int hour = (int) RelaxedConvertLeafNode(typeof(int), hourNode, 0);
868 int minute = (int) RelaxedConvertLeafNode(typeof(int), minuteNode, 0);
869 int second = (int) RelaxedConvertLeafNode(typeof(int), secondNode, 0);
871 fullDateTime += string.Format("T{0:00}:{1:00}:{2:00}", hour, minute, second);
872 conversionSucceeded = true;
875 return fullDateTime == "" ? null : fullDateTime;
878 private object TrySpecialDateTimeBinding(Type desiredType, CompositeNode treeRoot,
879 String paramName, out bool conversionSucceeded)
881 string dateUtc = TryGetDateWithUTCFormat(treeRoot, paramName, out conversionSucceeded);
883 if (dateUtc != null)
885 conversionSucceeded = true;
887 DateTime dt = DateTime.Parse(dateUtc);
889 if (desiredType.Name == "NullableDateTime")
891 TypeConverter typeConverter = TypeDescriptor.GetConverter(desiredType);
893 return typeConverter.ConvertFrom(dateUtc);
895 else
897 return DateTime.Parse(dateUtc);
901 conversionSucceeded = false;
902 return null;
905 private object RelaxedConvertLeafNode(Type desiredType, Node node, object defaultValue)
907 if (node == null)
909 return defaultValue;
912 if (node.NodeType != NodeType.Leaf)
914 throw new BindingException("Expected LeafNode, found {0} named {1}", node.NodeType, node.Name);
917 bool conversionSucceeded;
919 object result = ConvertLeafNode(desiredType, (LeafNode) node, out conversionSucceeded);
921 return conversionSucceeded ? result : defaultValue;
924 private bool IsDateTimeType(Type desiredType)
926 if (desiredType == typeof(DateTime))
928 return true;
930 else if (desiredType.Name == "NullableDateTime")
932 return true;
934 else if (desiredType.Name == "Nullable`1")
936 Type[] args = desiredType.GetGenericArguments();
938 return (args.Length == 1 && args[0] == typeof(DateTime));
941 return false;
944 #region Support methods
946 protected object InstanceOnStack
950 if (instanceStack.Count == 0)
952 return null;
955 return instanceStack.Peek();
959 protected string PrefixOnStack
963 if (prefixStack.Count == 0)
965 return null;
968 return prefixStack.Peek();
972 protected string ParentPrefixOnStack
976 if (prefixStack.Count < 2)
978 return null;
981 return prefixStack.ToArray()[prefixStack.Count - 2];
985 protected String[] CreateNormalizedList(String csv)
987 if (csv == null || csv.Trim() == String.Empty)
989 return null;
991 else
993 String[] list = csv.Split(',');
994 NormalizeList(list);
995 return list;
999 protected virtual void PushInstance(object instance, string prefix)
1001 instanceStack.Push(instance);
1002 prefixStack.Push(prefix);
1005 protected virtual void PopInstance(object instance, string prefix)
1007 object actual = instanceStack.Pop();
1009 if (actual != instance)
1011 throw new BindingException("Unexpected item on the stack: found {0}, expecting {1}", actual, instance);
1014 string actualPrefix = prefixStack.Pop();
1016 if (actualPrefix != prefix)
1018 throw new BindingException("Unexpected prefix on the stack: found {0}, expecting {1}", actualPrefix, prefix);
1022 private void NormalizeList(String[] list)
1024 for(int i = 0; i < list.Length; i++)
1026 list[i] = "root." + list[i].Trim();
1029 Array.Sort(list, CaseInsensitiveComparer.Default);
1032 private bool ShouldIgnoreProperty(PropertyInfo prop, string nodeFullName)
1034 bool allowed = true;
1035 bool disallowed = false;
1037 string propId = string.Format("{0}.{1}", nodeFullName, prop.Name);
1039 if (allowedPropertyList != null)
1041 allowed = Array.BinarySearch(allowedPropertyList, propId, CaseInsensitiveComparer.Default) >= 0;
1043 if (excludedPropertyList != null)
1045 disallowed = Array.BinarySearch(excludedPropertyList, propId, CaseInsensitiveComparer.Default) >= 0;
1048 return (!allowed) || (disallowed);
1051 private bool IsSimpleProperty(Type propType)
1053 if (propType.IsArray)
1055 return false;
1058 bool isSimple = propType.IsPrimitive ||
1059 propType.IsEnum ||
1060 propType == typeof(String) ||
1061 propType == typeof(Guid) ||
1062 propType == typeof(DateTime) ||
1063 propType == typeof(Decimal) ||
1064 propType == typeof(HttpPostedFile);
1066 if (isSimple)
1068 return true;
1071 TypeConverter tconverter = TypeDescriptor.GetConverter(propType);
1073 return tconverter.CanConvertFrom(typeof(String));
1076 #endregion