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
.Components
.Binder
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
20 using System
.ComponentModel
;
21 using System
.Reflection
;
23 using Castle
.Components
.Validator
;
25 using Castle
.Core
.Logging
;
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
;
62 /// Initializes a new instance of the <see cref="DataBinder"/> class.
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
));
84 public bool CanBindParameter(Type desiredType
, String paramName
, CompositeNode treeRoot
)
88 Node childNode
= treeRoot
.GetChildNode(paramName
);
90 if (childNode
!= null)
94 else if (desiredType
== typeof(DateTime
))
96 TrySpecialDateTimeBinding(desiredType
, treeRoot
, paramName
, out 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
;
120 if (desiredType
.IsArray
)
122 Node childNode
= treeRoot
.GetChildNode(paramName
);
124 result
= ConvertToArray(desiredType
, paramName
, childNode
, out conversionSucceeded
);
128 result
= ConvertToSimpleValue(desiredType
, paramName
, treeRoot
, out conversionSucceeded
);
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
);
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");
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");
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
));
206 /// Represents the databind errors
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; }
232 /// Gets the validation error summary.
234 /// <param name="instance">The instance.</param>
235 public ErrorSummary
GetValidationSummary(object instance
)
237 return validationErrorSummaryPerInstance
.ContainsKey(instance
) ? validationErrorSummaryPerInstance
[instance
] : null;
242 #region Implementation
244 protected object InternalBindObject(Type instanceType
, String paramPrefix
, Node node
)
247 return InternalBindObject(instanceType
, paramPrefix
, node
, out succeeded
);
250 protected object InternalBindObject(Type instanceType
, String paramPrefix
, Node node
, out bool succeeded
)
254 if (IsSpecialType(instanceType
))
256 return BindSpecialObjectInstance(instanceType
, paramPrefix
, node
, out succeeded
);
259 if (ShouldIgnoreType(instanceType
))
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
);
275 object instance
= CreateInstance(instanceType
, paramPrefix
, node
);
276 InternalRecursiveBindObjectInstance(instance
, paramPrefix
, node
);
281 protected void InternalRecursiveBindObjectInstance(object instance
, String prefix
, Node node
)
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
,
295 InternalRecursiveBindObjectInstance(instance
, prefix
, (CompositeNode
) node
);
298 protected void InternalRecursiveBindObjectInstance(object instance
, String prefix
, CompositeNode node
)
300 if (node
== null || instance
== null)
305 BeforeBinding(instance
, prefix
, node
);
307 if (PerformCustomBinding(instance
, prefix
, node
))
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
))
331 Type propType
= prop
.PropertyType
;
332 String paramName
= prop
.Name
;
334 String translatedParamName
= Translate(instanceType
, paramName
);
336 if (translatedParamName
== null)
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
))
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);
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);
388 InternalRecursiveBindObjectInstance(value, paramName
, nestedNode
);
392 CheckForValidationFailures(instance
, instanceType
, prop
, value, translatedParamName
, prefix
, summary
);
397 errors
.Add(new DataBindError(prefix
, prop
.Name
, ex
));
401 PopInstance(instance
, prefix
);
403 AfterBinding(instance
, prefix
, node
);
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.
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)
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
)
430 if (validator
== null)
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
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)
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
);
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
);
508 private object InternalBindObjectArray(Type instanceType
, String paramPrefix
, Node node
, out bool succeeded
)
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
)
526 if (typeof(IList
).IsAssignableFrom(instanceType
))
531 Type
[] genericArgs
= instanceType
.GetGenericArguments();
533 if (genericArgs
.Length
== 0)
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
)
547 return CreateInstance(instanceType
, paramPrefix
, node
);
550 return ConvertToGenericList(instanceType
, paramPrefix
, node
, out succeeded
);
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);
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
);
605 protected virtual bool PerformCustomBinding(object instance
, string prefix
, Node node
)
611 /// Implementations will bound the instance itself.
612 /// <seealso cref="IsSpecialType"/>
615 /// <seealso cref="IsSpecialType"/>
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
)
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"/>
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
)
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)
668 else if (childNode
.NodeType
== NodeType
.Leaf
)
670 return ConvertLeafNode(desiredType
, (LeafNode
) childNode
, out conversionSucceeded
);
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();
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
);
704 return ConvertComplexNodesToArray(desiredType
, indexedNode
, out conversionSucceeded
);
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
)
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
)
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];
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
);
819 convertedNodes
= ConvertComplexNodesToList(elemType
, indexedNode
, out conversionSucceeded
);
822 Type desiredImplType
= desiredType
.IsInterface
823 ? typeof(List
<>).MakeGenericType(elemType
)
825 IList target
= (IList
)CreateInstance(desiredImplType
, key
, node
);
827 foreach(object elem
in convertedNodes
)
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");
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
);
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
);
897 return DateTime
.Parse(dateUtc
);
901 conversionSucceeded
= false;
905 private object RelaxedConvertLeafNode(Type desiredType
, Node node
, object 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
))
930 else if (desiredType
.Name
== "NullableDateTime")
934 else if (desiredType
.Name
== "Nullable`1")
936 Type
[] args
= desiredType
.GetGenericArguments();
938 return (args
.Length
== 1 && args
[0] == typeof(DateTime
));
944 #region Support methods
946 protected object InstanceOnStack
950 if (instanceStack
.Count
== 0)
955 return instanceStack
.Peek();
959 protected string PrefixOnStack
963 if (prefixStack
.Count
== 0)
968 return prefixStack
.Peek();
972 protected string ParentPrefixOnStack
976 if (prefixStack
.Count
< 2)
981 return prefixStack
.ToArray()[prefixStack
.Count
- 2];
985 protected String
[] CreateNormalizedList(String csv
)
987 if (csv
== null || csv
.Trim() == String
.Empty
)
993 String
[] list
= csv
.Split(',');
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
)
1058 bool isSimple
= propType
.IsPrimitive
||
1060 propType
== typeof(String
) ||
1061 propType
== typeof(Guid
) ||
1062 propType
== typeof(DateTime
) ||
1063 propType
== typeof(Decimal
) ||
1064 propType
== typeof(HttpPostedFile
);
1071 TypeConverter tconverter
= TypeDescriptor
.GetConverter(propType
);
1073 return tconverter
.CanConvertFrom(typeof(String
));