Mercurial > pub > bltoolkit
diff Source/Reflection/TypeAccessor.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Reflection/TypeAccessor.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,1152 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Data.SqlTypes; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; + +using BLToolkit.Common; +#if !SILVERLIGHT && !DATA +using BLToolkit.ComponentModel; +using BLToolkit.EditableObjects; +#endif +using BLToolkit.Mapping; +using BLToolkit.TypeBuilder; +using BLToolkit.TypeBuilder.Builders; + +using JNotNull = JetBrains.Annotations.NotNullAttribute; + +namespace BLToolkit.Reflection +{ + public delegate object NullValueProvider(Type type); + public delegate bool IsNullHandler (object obj); + + [DebuggerDisplay("Type = {Type}, OriginalType = {OriginalType}")] + public abstract class TypeAccessor : ICollection<MemberAccessor> +#if !SILVERLIGHT && !DATA + , ITypeDescriptionProvider +#endif + { + #region Protected Emit Helpers + + protected MemberInfo GetMember(int memberType, string memberName) + { + const BindingFlags allInstaceMembers = + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + MemberInfo mi; + + switch (memberType) + { + case 1: mi = Type.GetField (memberName, allInstaceMembers); break; + case 2: + mi = + Type. GetProperty(memberName, allInstaceMembers) ?? + OriginalType.GetProperty(memberName, allInstaceMembers); + break; + default: + throw new InvalidOperationException(); + } + + return mi; + } + + protected void AddMember(MemberAccessor member) + { + if (member == null) throw new ArgumentNullException("member"); + + _members.Add(member); + _memberNames.Add(member.MemberInfo.Name, member); + } + + #endregion + + #region CreateInstance + + [DebuggerStepThrough] + public virtual object CreateInstance() + { + throw new TypeBuilderException(string.Format( + "The '{0}' type must have public default or init constructor.", + OriginalType.Name)); + } + + [DebuggerStepThrough] + public virtual object CreateInstance(InitContext context) + { + return CreateInstance(); + } + + [DebuggerStepThrough] + public object CreateInstanceEx() + { + return _objectFactory != null? + _objectFactory.CreateInstance(this, null): CreateInstance((InitContext)null); + } + + [DebuggerStepThrough] + public object CreateInstanceEx(InitContext context) + { + return _objectFactory != null? _objectFactory.CreateInstance(this, context): CreateInstance(context); + } + + #endregion + + #region ObjectFactory + + private IObjectFactory _objectFactory; + public IObjectFactory ObjectFactory + { + get { return _objectFactory; } + set { _objectFactory = value; } + } + + #endregion + + #region Copy & AreEqual + + internal static object CopyInternal(object source, object dest, TypeAccessor ta) + { +#if !SILVERLIGHT && !DATA + var isDirty = false; + var sourceEditable = source as IMemberwiseEditable; + var destEditable = dest as IMemberwiseEditable; + + if (sourceEditable != null && destEditable != null) + { + foreach (MemberAccessor ma in ta) + { + ma.CloneValue(source, dest); + if (sourceEditable.IsDirtyMember(null, ma.MemberInfo.Name, ref isDirty) && !isDirty) + destEditable.AcceptMemberChanges(null, ma.MemberInfo.Name); + } + } + else +#endif + { + foreach (MemberAccessor ma in ta) + ma.CloneValue(source, dest); + } + + return dest; + } + + public static object Copy(object source, object dest) + { + if (source == null) throw new ArgumentNullException("source"); + if (dest == null) throw new ArgumentNullException("dest"); + + TypeAccessor ta; + + var sType = source.GetType(); + var dType = dest. GetType(); + + if (TypeHelper.IsSameOrParent(sType, dType)) ta = GetAccessor(sType); + else if (TypeHelper.IsSameOrParent(dType, sType)) ta = GetAccessor(dType); + else + throw new ArgumentException(); + + return CopyInternal(source, dest, ta); + } + + public static object Copy(object source) + { + if (source == null) throw new ArgumentNullException("source"); + + var ta = GetAccessor(source.GetType()); + + return CopyInternal(source, ta.CreateInstanceEx(), ta); + } + + public static bool AreEqual(object obj1, object obj2) + { + if (ReferenceEquals(obj1, obj2)) + return true; + + if (obj1 == null || obj2 == null) + return false; + + TypeAccessor ta; + + var sType = obj1.GetType(); + var dType = obj2.GetType(); + + if (TypeHelper.IsSameOrParent(sType, dType)) ta = GetAccessor(sType); + else if (TypeHelper.IsSameOrParent(dType, sType)) ta = GetAccessor(dType); + else + return false; + + foreach (MemberAccessor ma in ta) + if ((!Equals(ma.GetValue(obj1), ma.GetValue(obj2)))) + return false; + + return true; + } + + public static int GetHashCode(object obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + + var hash = 0; + object value; + + foreach (MemberAccessor ma in GetAccessor(obj.GetType())) + { + value = ma.GetValue(obj); + hash = ((hash << 5) + hash) ^ (value == null ? 0 : value.GetHashCode()); + } + + return hash; + } + + #endregion + + #region Abstract Members + + public abstract Type Type { get; } + public abstract Type OriginalType { get; } + + #endregion + + #region Items + + private readonly List<MemberAccessor> _members = new List<MemberAccessor>(); + private readonly Dictionary<string,MemberAccessor> _memberNames = new Dictionary<string,MemberAccessor>(); + + public MemberAccessor this[string memberName] + { + get + { + MemberAccessor ma; + return _memberNames.TryGetValue(memberName, out ma) ? ma : null; + } + } + + public MemberAccessor this[int index] + { + get { return _members[index]; } + } + + public MemberAccessor this[NameOrIndexParameter nameOrIndex] + { + get + { + return nameOrIndex.ByName ? _memberNames[nameOrIndex.Name] : _members[nameOrIndex.Index]; + } + } + + #endregion + + #region Static Members + + [Obsolete("Use TypeFactory.LoadTypes instead")] + public static bool LoadTypes + { + get { return TypeFactory.LoadTypes; } + set { TypeFactory.LoadTypes = value; } + } + + private static readonly Dictionary<Type,TypeAccessor> _accessors = new Dictionary<Type,TypeAccessor>(10); + + public static TypeAccessor GetAccessor(Type originalType) + { + if (originalType == null) throw new ArgumentNullException("originalType"); + + lock (_accessors) + { + TypeAccessor accessor; + + if (_accessors.TryGetValue(originalType, out accessor)) + return accessor; + + if (IsAssociatedType(originalType)) + return _accessors[originalType]; + + var instanceType = (IsClassBulderNeeded(originalType) ? null : originalType) ?? TypeFactory.GetType(originalType); + + var accessorType = TypeFactory.GetType(originalType, originalType, new TypeAccessorBuilder(instanceType, originalType)); + + accessor = (TypeAccessor)Activator.CreateInstance(accessorType); + + _accessors.Add(originalType, accessor); + + if (originalType != instanceType) + _accessors.Add(instanceType, accessor); + + return accessor; + } + } + + public static TypeAccessor GetAccessor([JNotNull] object obj) + { + if (obj == null) throw new ArgumentNullException("obj"); + return GetAccessor(obj.GetType()); + } + + public static TypeAccessor GetAccessor<T>() + { + return TypeAccessor<T>.Instance; + } + + private static bool IsClassBulderNeeded(Type type) + { + if (type.IsAbstract && !type.IsSealed) + { + if (!type.IsInterface) + { + if (TypeHelper.GetDefaultConstructor(type) != null) + return true; + + if (TypeHelper.GetConstructor(type, typeof(InitContext)) != null) + return true; + } + else + { + var attrs = TypeHelper.GetAttributes(type, typeof(AutoImplementInterfaceAttribute)); + + if (attrs != null && attrs.Length > 0) + return true; + } + } + + return false; + } + + internal static bool IsInstanceBuildable(Type type) + { + if (!type.IsInterface) + return true; + + lock (_accessors) + { + if (_accessors.ContainsKey(type)) + return true; + + if (IsAssociatedType(type)) + return true; + } + + var attrs = TypeHelper.GetAttributes(type, typeof(AutoImplementInterfaceAttribute)); + + return attrs != null && attrs.Length > 0; + } + + private static bool IsAssociatedType(Type type) + { + if (AssociatedTypeHandler != null) + { + var child = AssociatedTypeHandler(type); + + if (child != null) + { + AssociateType(type, child); + return true; + } + } + + return false; + } + + public static object CreateInstance(Type type) + { + return GetAccessor(type).CreateInstance(); + } + + public static object CreateInstance(Type type, InitContext context) + { + return GetAccessor(type).CreateInstance(context); + } + + public static object CreateInstanceEx(Type type) + { + return GetAccessor(type).CreateInstanceEx(); + } + + public static object CreateInstanceEx(Type type, InitContext context) + { + return GetAccessor(type).CreateInstance(context); + } + + public static T CreateInstance<T>() + { + return TypeAccessor<T>.CreateInstance(); + } + + public static T CreateInstance<T>(InitContext context) + { + return TypeAccessor<T>.CreateInstance(context); + } + + public static T CreateInstanceEx<T>() + { + return TypeAccessor<T>.CreateInstanceEx(); + } + + public static T CreateInstanceEx<T>(InitContext context) + { + return TypeAccessor<T>.CreateInstance(context); + } + + public static TypeAccessor AssociateType(Type parent, Type child) + { + if (!TypeHelper.IsSameOrParent(parent, child)) + throw new ArgumentException( + string.Format("'{0}' must be a base type of '{1}'", parent, child), + "child"); + + var accessor = GetAccessor(child); + + accessor = (TypeAccessor)Activator.CreateInstance(accessor.GetType()); + + lock (_accessors) + _accessors.Add(parent, accessor); + + return accessor; + } + + public delegate Type GetAssociatedType(Type parent); + public static event GetAssociatedType AssociatedTypeHandler; + + #endregion + + #region GetNullValue + + private static NullValueProvider _getNullValue = GetNullInternal; + public static NullValueProvider GetNullValue + { + get { return _getNullValue ?? (_getNullValue = GetNullInternal);} + set { _getNullValue = value; } + } + + private static object GetNullInternal(Type type) + { + if (type == null) throw new ArgumentNullException("type"); + + if (type.IsValueType) + { + if (type.IsEnum) + return GetEnumNullValue(type); + + if (type.IsPrimitive) + { + if (type == typeof(Int32)) return Common.Configuration.NullableValues.Int32; + if (type == typeof(Double)) return Common.Configuration.NullableValues.Double; + if (type == typeof(Int16)) return Common.Configuration.NullableValues.Int16; + if (type == typeof(Boolean)) return Common.Configuration.NullableValues.Boolean; + if (type == typeof(SByte)) return Common.Configuration.NullableValues.SByte; + if (type == typeof(Int64)) return Common.Configuration.NullableValues.Int64; + if (type == typeof(Byte)) return Common.Configuration.NullableValues.Byte; + if (type == typeof(UInt16)) return Common.Configuration.NullableValues.UInt16; + if (type == typeof(UInt32)) return Common.Configuration.NullableValues.UInt32; + if (type == typeof(UInt64)) return Common.Configuration.NullableValues.UInt64; + if (type == typeof(Single)) return Common.Configuration.NullableValues.Single; + if (type == typeof(Char)) return Common.Configuration.NullableValues.Char; + } + else + { + if (type == typeof(DateTime)) return Common.Configuration.NullableValues.DateTime; + if (type == typeof(DateTimeOffset)) return Common.Configuration.NullableValues.DateTimeOffset; + if (type == typeof(Decimal)) return Common.Configuration.NullableValues.Decimal; + if (type == typeof(Guid)) return Common.Configuration.NullableValues.Guid; + +#if !SILVERLIGHT + + if (type == typeof(SqlInt32)) return SqlInt32. Null; + if (type == typeof(SqlString)) return SqlString. Null; + if (type == typeof(SqlBoolean)) return SqlBoolean. Null; + if (type == typeof(SqlByte)) return SqlByte. Null; + if (type == typeof(SqlDateTime)) return SqlDateTime.Null; + if (type == typeof(SqlDecimal)) return SqlDecimal. Null; + if (type == typeof(SqlDouble)) return SqlDouble. Null; + if (type == typeof(SqlGuid)) return SqlGuid. Null; + if (type == typeof(SqlInt16)) return SqlInt16. Null; + if (type == typeof(SqlInt64)) return SqlInt64. Null; + if (type == typeof(SqlMoney)) return SqlMoney. Null; + if (type == typeof(SqlSingle)) return SqlSingle. Null; + if (type == typeof(SqlBinary)) return SqlBinary. Null; + +#endif + } + } + else + { + if (type == typeof(String)) return Common.Configuration.NullableValues.String; + if (type == typeof(DBNull)) return DBNull.Value; + if (type == typeof(Stream)) return Stream.Null; +#if !SILVERLIGHT + if (type == typeof(SqlXml)) return SqlXml.Null; +#endif + } + + return null; + } + + const FieldAttributes EnumField = FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal; + + static readonly Dictionary<Type,object> _nullValues = new Dictionary<Type,object>(); + + static object GetEnumNullValue(Type type) + { + object nullValue; + + lock (_nullValues) + if (_nullValues.TryGetValue(type, out nullValue)) + return nullValue; + + var fields = type.GetFields(); + + foreach (var fi in fields) + { + if ((fi.Attributes & EnumField) == EnumField) + { + var attrs = Attribute.GetCustomAttributes(fi, typeof(NullValueAttribute)); + + if (attrs.Length > 0) + { + nullValue = Enum.Parse(type, fi.Name, false); + break; + } + } + } + + lock (_nullValues) + if (!_nullValues.ContainsKey(type)) + _nullValues.Add(type, nullValue); + + return nullValue; + } + + private static IsNullHandler _isNull = IsNullInternal; + public static IsNullHandler IsNull + { + get { return _isNull ?? (_isNull = IsNullInternal); } + set { _isNull = value; } + } + + private static bool IsNullInternal(object value) + { + if (value == null) + return true; + + var nullValue = GetNullValue(value.GetType()); + + return nullValue != null && value.Equals(nullValue); + } + + #endregion + + #region ICollection Members + + void ICollection<MemberAccessor>.Add(MemberAccessor item) + { + _members.Add(item); + } + + void ICollection<MemberAccessor>.Clear() + { + _members.Clear(); + } + + bool ICollection<MemberAccessor>.Contains(MemberAccessor item) + { + return _members.Contains(item); + } + + void ICollection<MemberAccessor>.CopyTo(MemberAccessor[] array, int arrayIndex) + { + _members.CopyTo(array, arrayIndex); + } + + bool ICollection<MemberAccessor>.Remove(MemberAccessor item) + { + return _members.Remove(item); + } + + public int Count + { + get { return _members.Count; } + } + + bool ICollection<MemberAccessor>.IsReadOnly + { + get { return ((ICollection<MemberAccessor>)_members).IsReadOnly; } + } + + public int IndexOf(MemberAccessor ma) + { + return _members.IndexOf(ma); + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() + { + return _members.GetEnumerator(); + } + + #endregion + + #region IEnumerable<MemberAccessor> Members + + IEnumerator<MemberAccessor> IEnumerable<MemberAccessor>.GetEnumerator() + { + foreach (MemberAccessor member in _members) + yield return member; + } + + #endregion + + #region Write Object Info + + public static void WriteDebug(object o) + { +#if DEBUG + Write(o, DebugWriteLine); +#endif + } + + private static void DebugWriteLine(string text) + { + Debug.WriteLine(text); + } + + public static void WriteConsole(object o) + { + Write(o, Console.WriteLine); + } + + [SuppressMessage("Microsoft.Performance", "CA1818:DoNotConcatenateStringsInsideLoops")] + private static string MapTypeName(Type type) + { + if (type.IsGenericType) + { + if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) + return string.Format("{0}?", MapTypeName(Nullable.GetUnderlyingType(type))); + + var name = type.Name; + var idx = name.IndexOf('`'); + + if (idx >= 0) + name = name.Substring(0, idx); + + name += "<"; + + foreach (var t in type.GetGenericArguments()) + name += MapTypeName(t) + ','; + + if (name[name.Length - 1] == ',') + name = name.Substring(0, name.Length - 1); + + name += ">"; + + return name; + } + + if (type.IsPrimitive || + type == typeof(string) || + type == typeof(object) || + type == typeof(decimal)) + { + if (type == typeof(int)) return "int"; + if (type == typeof(bool)) return "bool"; + if (type == typeof(short)) return "short"; + if (type == typeof(long)) return "long"; + if (type == typeof(ushort)) return "ushort"; + if (type == typeof(uint)) return "uint"; + if (type == typeof(ulong)) return "ulong"; + if (type == typeof(float)) return "float"; + + return type.Name.ToLower(); + } + + return type.Name; + } + + public delegate void WriteLine(string text); + + [SuppressMessage("Microsoft.Usage", "CA2241:ProvideCorrectArgumentsToFormattingMethods")] + public static void Write(object o, WriteLine writeLine) + { + if (o == null) + { + writeLine("*** (null) ***"); + return; + } + + MemberAccessor ma; + + var ta = GetAccessor(o.GetType()); + var nameLen = 0; + var typeLen = 0; + + foreach (var de in ta._memberNames) + { + if (nameLen < de.Key.Length) + nameLen = de.Key.Length; + + ma = de.Value; + + if (typeLen < MapTypeName(ma.Type).Length) + typeLen = MapTypeName(ma.Type).Length; + } + + var text = "*** " + o.GetType().FullName + ": ***"; + + writeLine(text); + + var format = string.Format("{{0,-{0}}} {{1,-{1}}} : {{2}}", typeLen, nameLen); + + foreach (var de in ta._memberNames) + { + ma = de.Value; + + var value = ma.GetValue(o); + + if (value == null) + value = "(null)"; + else if (value is ICollection) + value = string.Format("(Count = {0})", ((ICollection)value).Count); + + text = string.Format(format, MapTypeName(ma.Type), de.Key, value); + + writeLine(text); + } + + writeLine("***"); + } + + #endregion + + #region TypeDescriptor + +#if !SILVERLIGHT && !DATA + + #region CustomTypeDescriptor + + private static readonly Hashtable _descriptors = new Hashtable(); + + public static ICustomTypeDescriptor GetCustomTypeDescriptor(Type type) + { + var descriptor = (ICustomTypeDescriptor)_descriptors[type]; + + if (descriptor == null) + { + lock (_descriptors.SyncRoot) + { + descriptor = (ICustomTypeDescriptor)_descriptors[type]; + + if (descriptor == null) + { + descriptor = new CustomTypeDescriptorImpl(type); + + _descriptors.Add(type, descriptor); + } + } + } + return descriptor; + } + + private ICustomTypeDescriptor _customTypeDescriptor; + public ICustomTypeDescriptor CustomTypeDescriptor + { + get { return _customTypeDescriptor ?? (_customTypeDescriptor = GetCustomTypeDescriptor(OriginalType)); } + } + + #endregion + + #region Property Descriptors + + private PropertyDescriptorCollection _propertyDescriptors; + public PropertyDescriptorCollection PropertyDescriptors + { + get + { + if (_propertyDescriptors == null) + { + if (TypeHelper.IsSameOrParent(typeof(ICustomTypeDescriptor), OriginalType)) + { + var descriptor = CreateInstance() as ICustomTypeDescriptor; + + if (descriptor != null) + _propertyDescriptors = descriptor.GetProperties(); + } + + if (_propertyDescriptors == null) + _propertyDescriptors = CreatePropertyDescriptors(); + } + + return _propertyDescriptors; + } + } + + public PropertyDescriptorCollection CreatePropertyDescriptors() + { + if (Data.DbManager.TraceSwitch.TraceInfo) + Data.DbManager.WriteTraceLine(OriginalType.FullName, "CreatePropertyDescriptors"); + + var pd = new PropertyDescriptor[Count]; + + var i = 0; + foreach (MemberAccessor ma in _members) + pd[i++] = ma.PropertyDescriptor; + + return new PropertyDescriptorCollection(pd); + } + + public PropertyDescriptorCollection CreateExtendedPropertyDescriptors( + Type objectViewType, + IsNullHandler isNull) + { + // This is definitely wrong. + // + //if (isNull == null) + // isNull = _isNull; + + var pdc = CreatePropertyDescriptors(); + + if (objectViewType != null) + { + var viewAccessor = GetAccessor(objectViewType); + var objectView = (IObjectView)viewAccessor.CreateInstanceEx(); + var list = new List<PropertyDescriptor>(); + + var viewpdc = viewAccessor.PropertyDescriptors; + + foreach (PropertyDescriptor pd in viewpdc) + list.Add(new ObjectViewPropertyDescriptor(pd, objectView)); + + foreach (PropertyDescriptor pd in pdc) + if (viewpdc.Find(pd.Name, false) == null) + list.Add(pd); + + pdc = new PropertyDescriptorCollection(list.ToArray()); + } + + pdc = pdc.Sort(new PropertyDescriptorComparer()); + + pdc = GetExtendedProperties(pdc, OriginalType, String.Empty, Type.EmptyTypes, new PropertyDescriptor[0], isNull); + + return pdc; + } + + private static PropertyDescriptorCollection GetExtendedProperties( + PropertyDescriptorCollection pdc, + Type itemType, + string propertyPrefix, + Type[] parentTypes, + PropertyDescriptor[] parentAccessors, + IsNullHandler isNull) + { + var list = new ArrayList(pdc.Count); + var objects = new ArrayList(); + var isDataRow = itemType.IsSubclassOf(typeof(DataRow)); + + foreach (PropertyDescriptor p in pdc) + { + var propertyType = p.PropertyType; + + if (p.Attributes.Matches(BindableAttribute.No) || + //propertyType == typeof(Type) || + isDataRow && p.Name == "ItemArray") + continue; + + var isList = false; + var explicitlyBound = p.Attributes.Contains(BindableAttribute.Yes); + var pd = p; + + if (propertyType.GetInterface("IList") != null) + { + //if (!explicitlyBound) + // continue; + + isList = true; + pd = new ListPropertyDescriptor(pd); + } + + if (!isList && + !propertyType.IsValueType && + !propertyType.IsArray && + (!propertyType.FullName.StartsWith("System.") || explicitlyBound + || propertyType.IsGenericType) && + propertyType != typeof(Type) && + propertyType != typeof(string) && + propertyType != typeof(object) && + Array.IndexOf(parentTypes, propertyType) == -1) + { + var childParentTypes = new Type[parentTypes.Length + 1]; + + parentTypes.CopyTo(childParentTypes, 0); + childParentTypes[parentTypes.Length] = itemType; + + var childParentAccessors = new PropertyDescriptor[parentAccessors.Length + 1]; + + parentAccessors.CopyTo(childParentAccessors, 0); + childParentAccessors[parentAccessors.Length] = pd; + + var pdch = GetAccessor(propertyType).PropertyDescriptors; + + pdch = pdch.Sort(new PropertyDescriptorComparer()); + pdch = GetExtendedProperties( + pdch, + propertyType, + propertyPrefix + pd.Name + "+", + childParentTypes, + childParentAccessors, + isNull); + + objects.AddRange(pdch); + } + else + { + if (propertyPrefix.Length != 0 || isNull != null) + pd = new StandardPropertyDescriptor(pd, propertyPrefix, parentAccessors, isNull); + + list.Add(pd); + } + } + + list.AddRange(objects); + + return new PropertyDescriptorCollection( + (PropertyDescriptor[])list.ToArray(typeof(PropertyDescriptor))); + } + + #region PropertyDescriptorComparer + + class PropertyDescriptorComparer : IComparer + { + public int Compare(object x, object y) + { + return String.Compare(((PropertyDescriptor)x).Name, ((PropertyDescriptor)y).Name); + } + } + + #endregion + + #region ListPropertyDescriptor + + class ListPropertyDescriptor : PropertyDescriptorWrapper + { + public ListPropertyDescriptor(PropertyDescriptor descriptor) + : base(descriptor) + { + } + + public override object GetValue(object component) + { + var value = base.GetValue(component); + + if (value == null) + return value; + + if (value is IBindingList && value is ITypedList) + return value; + + return EditableArrayList.Adapter((IList)value); + } + } + + #endregion + + #region StandardPropertyDescriptor + + class StandardPropertyDescriptor : PropertyDescriptorWrapper + { + protected readonly PropertyDescriptor _descriptor; + protected readonly IsNullHandler _isNull; + + protected readonly string _prefixedName; + protected readonly PropertyDescriptor[] _chainAccessors; + + public StandardPropertyDescriptor( + PropertyDescriptor pd, + string namePrefix, + PropertyDescriptor[] chainAccessors, + IsNullHandler isNull) + : base(pd) + { + _descriptor = pd; + _isNull = isNull; + _prefixedName = namePrefix + pd.Name; + _chainAccessors = chainAccessors; + } + + protected object GetNestedComponent(object component) + { + for (var i = 0; + i < _chainAccessors.Length && component != null && !(component is DBNull); + i++) + { + component = _chainAccessors[i].GetValue(component); + } + + return component; + } + + public override void SetValue(object component, object value) + { + component = GetNestedComponent(component); + + if (component != null && !(component is DBNull)) + _descriptor.SetValue(component, value); + } + + public override object GetValue(object component) + { + component = GetNestedComponent(component); + + return CheckNull( + component != null && !(component is DBNull)? _descriptor.GetValue(component): null); + } + + public override string Name + { + get { return _prefixedName; } + } + + protected object CheckNull(object value) + { + if (_isNull != null && _isNull(value)) + { + switch (Common.Configuration.CheckNullReturnIfNull) + { + case Common.Configuration.NullEquivalent.DBNull: + return DBNull.Value; + case Common.Configuration.NullEquivalent.Null: + return null; + case Common.Configuration.NullEquivalent.Value: + return value; + } + + return DBNull.Value; + } + + return value; + } + } + + #endregion + + #region objectViewPropertyDescriptor + + class ObjectViewPropertyDescriptor : PropertyDescriptorWrapper + { + public ObjectViewPropertyDescriptor(PropertyDescriptor pd, IObjectView objectView) + : base(pd) + { + _objectView = objectView; + } + + private readonly IObjectView _objectView; + + public override object GetValue(object component) + { + _objectView.Object = component; + + return base.GetValue(_objectView); + } + + public override void SetValue(object component, object value) + { + _objectView.Object = component; + + base.SetValue(_objectView, value); + } + } + + #endregion + + #endregion + + #region ITypeDescriptionProvider Members + + string ITypeDescriptionProvider.ClassName + { + get { return OriginalType.Name; } + } + + string ITypeDescriptionProvider.ComponentName + { + get { return OriginalType.Name; } + } + + EventDescriptor ITypeDescriptionProvider.GetEvent(string name) + { + return new CustomEventDescriptor(OriginalType.GetEvent(name)); + } + + PropertyDescriptor ITypeDescriptionProvider.GetProperty(string name) + { + var ma = this[name]; + return ma != null ? ma.PropertyDescriptor : null; + } + + AttributeCollection ITypeDescriptionProvider.GetAttributes() + { + var attributesAsObj = new TypeHelper(OriginalType).GetAttributes(); + var attributes = new Attribute[attributesAsObj.Length]; + + for (var i = 0; i < attributesAsObj.Length; i++) + attributes[i] = attributesAsObj[i] as Attribute; + + return new AttributeCollection(attributes); + } + + EventDescriptorCollection ITypeDescriptionProvider.GetEvents() + { + var ei = OriginalType.GetEvents(); + var ed = new EventDescriptor[ei.Length]; + + for (var i = 0; i < ei.Length; i++) + ed[i] = new CustomEventDescriptor(ei[i]); + + return new EventDescriptorCollection(ed); + } + + PropertyDescriptorCollection ITypeDescriptionProvider.GetProperties() + { + return CreatePropertyDescriptors(); + } + + #region CustomEventDescriptor + + class CustomEventDescriptor : EventDescriptor + { + public CustomEventDescriptor(EventInfo eventInfo) + : base(eventInfo.Name, null) + { + _eventInfo = eventInfo; + } + + private readonly EventInfo _eventInfo; + + public override void AddEventHandler(object component, Delegate value) + { + _eventInfo.AddEventHandler(component, value); + } + + public override void RemoveEventHandler(object component, Delegate value) + { + _eventInfo.RemoveEventHandler(component, value); + } + + public override Type ComponentType { get { return _eventInfo.DeclaringType; } } + public override Type EventType { get { return _eventInfo.EventHandlerType; } } + public override bool IsMulticast { get { return _eventInfo.IsMulticast; } } + } + + #endregion + + #endregion + +#endif + + #endregion + } +}