diff --git a/Mvc/DataAnnotationsModelValidator.cs b/Mvc/DataAnnotationsModelValidator.cs index f03130c..309c4fb 100644 --- a/Mvc/DataAnnotationsModelValidator.cs +++ b/Mvc/DataAnnotationsModelValidator.cs @@ -30,7 +30,9 @@ public DataAnnotationsModelValidator(ModelMetadata metadata, ControllerContext c protected internal string ErrorMessage { get { - return Attribute.FormatErrorMessage(Metadata.GetDisplayName()); + // FormatErrorMessage is not implemented in mono + return "Validation error (" + Attribute.GetType().ToString() + "): " + Metadata.GetDisplayName(); + //return Attribute.FormatErrorMessage(Metadata.GetDisplayName()); } } @@ -45,6 +47,21 @@ public DataAnnotationsModelValidator(ModelMetadata metadata, ControllerContext c } public override IEnumerable Validate(object container) { + // RequiredAttribute is not implemented in mono + // This is a small workaround that might work for some and might break for a lot of objects + if (IsRequired) { + if (ReferenceEquals(Metadata.Model,null)) { + yield return new ModelValidationResult + { + Message = ErrorMessage + " is null" + }; + } else if (Metadata.Model.ToString() == "") { + yield return new ModelValidationResult + { + Message = ErrorMessage + " is empty" + }; + } + } else if (!Attribute.IsValid(Metadata.Model)) { yield return new ModelValidationResult { Message = ErrorMessage @@ -53,3 +70,4 @@ public DataAnnotationsModelValidator(ModelMetadata metadata, ControllerContext c } } } + diff --git a/Mvc/Html/DefaultDisplayTemplates.cs b/Mvc/Html/DefaultDisplayTemplates.cs index 324e188..89ce828 100644 --- a/Mvc/Html/DefaultDisplayTemplates.cs +++ b/Mvc/Html/DefaultDisplayTemplates.cs @@ -185,7 +185,8 @@ internal static class DefaultDisplayTemplates { private static bool ShouldShow(ModelMetadata metadata, TemplateInfo templateInfo) { return metadata.ShowForDisplay - && metadata.ModelType != typeof(EntityState) + // MONO: System.Data.Entity is unavailable + // && metadata.ModelType != typeof(EntityState) && !metadata.IsComplexType && !templateInfo.Visited(metadata); } @@ -202,3 +203,4 @@ internal static class DefaultDisplayTemplates { } } } + diff --git a/Mvc/Html/DefaultEditorTemplates.cs b/Mvc/Html/DefaultEditorTemplates.cs index 750fa6b..238d79d 100644 --- a/Mvc/Html/DefaultEditorTemplates.cs +++ b/Mvc/Html/DefaultEditorTemplates.cs @@ -119,7 +119,7 @@ internal static class DefaultEditorTemplates { object model = html.ViewContext.ViewData.Model; Binary modelAsBinary = model as Binary; - if (modelAsBinary != null) { + if (((System.Object)modelAsBinary) != null) { model = Convert.ToBase64String(modelAsBinary.ToArray()); } else { @@ -191,7 +191,8 @@ internal static class DefaultEditorTemplates { private static bool ShouldShow(ModelMetadata metadata, TemplateInfo templateInfo) { return metadata.ShowForEdit - && metadata.ModelType != typeof(EntityState) + // EntityState is unavailable on mono + //&& metadata.ModelType != typeof(EntityState) && !metadata.IsComplexType && !templateInfo.Visited(metadata); } @@ -211,3 +212,4 @@ internal static class DefaultEditorTemplates { } } } + diff --git a/Mvc/Html/InputExtensions.cs b/Mvc/Html/InputExtensions.cs index 1f8d8c2..a61f212 100644 --- a/Mvc/Html/InputExtensions.cs +++ b/Mvc/Html/InputExtensions.cs @@ -132,7 +132,7 @@ public static class InputExtensions { private static MvcHtmlString HiddenHelper(HtmlHelper htmlHelper, object value, bool useViewData, string expression, IDictionary htmlAttributes) { Binary binaryValue = value as Binary; - if (binaryValue != null) { + if (((System.Object)binaryValue) != null) { value = binaryValue.ToArray(); } @@ -396,3 +396,4 @@ public static class InputExtensions { } } } + diff --git a/Mvc/ParameterInfoUtil.cs b/Mvc/ParameterInfoUtil.cs index 194d804..283f899 100644 --- a/Mvc/ParameterInfoUtil.cs +++ b/Mvc/ParameterInfoUtil.cs @@ -20,7 +20,13 @@ internal static class ParameterInfoUtil { public static bool TryGetDefaultValue(ParameterInfo parameterInfo, out object value) { // this will get the default value as seen by the VB / C# compilers // if no value was baked in, RawDefaultValue returns DBNull.Value - object rawDefaultValue = parameterInfo.RawDefaultValue; + object rawDefaultValue = null; + try { + rawDefaultValue = parameterInfo.RawDefaultValue; + } + catch (NotImplementedException) { + rawDefaultValue = null; + } if (rawDefaultValue != DBNull.Value) { value = rawDefaultValue; return true; @@ -40,3 +46,4 @@ internal static class ParameterInfoUtil { } } + diff --git a/Mvc/TypeDescriptorHelper.cs b/Mvc/TypeDescriptorHelper.cs index 4ba729f..45869fc 100644 --- a/Mvc/TypeDescriptorHelper.cs +++ b/Mvc/TypeDescriptorHelper.cs @@ -10,273 +10,657 @@ * * ***************************************************************************/ -namespace System.Web.Mvc { - using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.ComponentModel.DataAnnotations; - using System.Globalization; - using System.Linq; - using System.Reflection; - using System.Web.Mvc.Resources; - - // TODO: Remove this class in MVC 3 - // - // We brought in a private copy of the AssociatedMetadataTypeTypeDescriptionProvider - // from .NET 4, because it provides several bug fixes and perf improvements. If we're - // running on .NET < 4, we'll use our private copy. - - internal static class TypeDescriptorHelper { - - private static Func _typeDescriptorFactory = GetTypeDescriptorFactory(); - - private static Func GetTypeDescriptorFactory() { - if (Environment.Version.Major < 4) { - return type => new _AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type); - } - - return type => new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type); +namespace System.Web.Mvc +{ + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.ComponentModel.DataAnnotations; + using System.Globalization; + using System.Linq; + using System.Reflection; + using System.Web.Mvc.Resources; + using System.ComponentModel.Design; + using System.Collections; + + internal static class TypeDescriptorHelper + { + + private static Func _typeDescriptorFactory = GetTypeDescriptorFactory(); + + private static Func GetTypeDescriptorFactory() + { + // MONO 2.4.4 fix: copy mono 2.6 version locally (the same way MS did with .NET 3.5) + // MONO 2.6 fix: do not use MS .NET 4 fix + //if (Environment.Version.Major < 4) { + // return type => new _AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type); + //} + bool use_original = true; + if (Type.GetType("Mono.Runtime") != null) + { + try { + new AssociatedMetadataTypeTypeDescriptionProvider(typeof(System.Object)); + } + catch (NotImplementedException) { + use_original = false; } + } + if (use_original) { + return type => new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type); + } else { + return type => new _AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type); + } + + } - public static ICustomTypeDescriptor Get(Type type) { - return _typeDescriptorFactory(type); - } + public static ICustomTypeDescriptor Get(Type type) + { + return _typeDescriptorFactory(type); + } - // Private copies of the .NET 4 AssociatedMetadataType classes + // Backport classes from MONO 2.6.3 for MONO 2.4.4 + // Using only if AssociatedMetadataTypeTypeDescriptionProvider doesn't work - private class _AssociatedMetadataTypeTypeDescriptionProvider : TypeDescriptionProvider { - public _AssociatedMetadataTypeTypeDescriptionProvider(Type type) - : base(TypeDescriptor.GetProvider(type)) { - } + #region _AssociatedMetadataTypeTypeDescriptionProvider + + public class _AssociatedMetadataTypeTypeDescriptionProvider : TypeDescriptionProvider + { + Type type; + Type associatedMetadataType; + + public _AssociatedMetadataTypeTypeDescriptionProvider(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + this.type = type; + } + + public _AssociatedMetadataTypeTypeDescriptionProvider(Type type, Type associatedMetadataType) + { + if (type == null) + throw new ArgumentNullException("type"); + if (associatedMetadataType == null) + throw new ArgumentNullException("associatedMetadataType"); + + this.type = type; + this.associatedMetadataType = associatedMetadataType; + } + + public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) + { + return new _AssociatedMetadataTypeTypeDescriptor(base.GetTypeDescriptor(objectType, instance), type, associatedMetadataType); + } + } + + #endregion + + #region _AssociatedMetadataTypeTypeDescriptor + + class _AssociatedMetadataTypeTypeDescriptor : CustomTypeDescriptor + { + Type type; + Type associatedMetadataType; + bool associatedMetadataTypeChecked; + PropertyDescriptorCollection properties; - public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) { - ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(objectType, instance); - return new _AssociatedMetadataTypeTypeDescriptor(baseDescriptor, objectType); + Type AssociatedMetadataType + { + get + { + if (!associatedMetadataTypeChecked && associatedMetadataType == null) + associatedMetadataType = FindMetadataType(); + + return associatedMetadataType; + } + } + + public _AssociatedMetadataTypeTypeDescriptor(ICustomTypeDescriptor parent, Type type) + : this(parent, type, null) + { + } + + public _AssociatedMetadataTypeTypeDescriptor(ICustomTypeDescriptor parent, Type type, Type associatedMetadataType) + : base(parent) + { + this.type = type; + this.associatedMetadataType = associatedMetadataType; + } + + void CopyAttributes(object[] from, List to) + { + foreach (object o in from) + { + Attribute a = o as Attribute; + if (a == null) + continue; + + to.Add(a); + } + } + + public override AttributeCollection GetAttributes() + { + var attributes = new List(); + CopyAttributes(type.GetCustomAttributes(true), attributes); + + Type metaType = AssociatedMetadataType; + if (metaType != null) + CopyAttributes(metaType.GetCustomAttributes(true), attributes); + + return new AttributeCollection(attributes.ToArray()); + } + + public override PropertyDescriptorCollection GetProperties() + { + // Code partially copied from TypeDescriptor.TypeInfo.GetProperties + if (properties != null) + return properties; + + Dictionary metaMembers = null; + var propertiesHash = new Dictionary(); // name - null + var propertiesList = new List<_AssociatedMetadataTypePropertyDescriptor>(); + Type currentType = type; + Type metaType = AssociatedMetadataType; + + if (metaType != null) + { + metaMembers = new Dictionary(); + MemberInfo[] members = metaType.GetMembers(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); + + foreach (MemberInfo member in members) + { + switch (member.MemberType) + { + case MemberTypes.Field: + case MemberTypes.Property: + break; + + default: + continue; } + + string name = member.Name; + if (metaMembers.ContainsKey(name)) + continue; + + metaMembers.Add(name, member); + } } - private class _AssociatedMetadataTypeTypeDescriptor : CustomTypeDescriptor { - private Type AssociatedMetadataType { - get; - set; + // Getting properties type by type, because in the case of a property in the child type, where + // the "new" keyword is used and also the return type is changed Type.GetProperties returns + // also the parent property. + // + // Note that we also have to preserve the properties order here. + // + while (currentType != null && currentType != typeof(object)) + { + PropertyInfo[] props = currentType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); + foreach (PropertyInfo property in props) + { + string propName = property.Name; + + if (property.GetIndexParameters().Length == 0 && property.CanRead && !propertiesHash.ContainsKey(propName)) + { + MemberInfo metaMember; + + if (metaMembers != null) + metaMembers.TryGetValue(propName, out metaMember); + else + metaMember = null; + propertiesList.Add(new _AssociatedMetadataTypePropertyDescriptor(property, metaMember)); + propertiesHash.Add(propName, true); } + } + currentType = currentType.BaseType; + } - public _AssociatedMetadataTypeTypeDescriptor(ICustomTypeDescriptor parent, Type type) - : base(parent) { - AssociatedMetadataType = TypeDescriptorCache.GetAssociatedMetadataType(type); - if (AssociatedMetadataType != null) { - TypeDescriptorCache.ValidateMetadataType(type, AssociatedMetadataType); - } - } + properties = new PropertyDescriptorCollection((PropertyDescriptor[])propertiesList.ToArray(), true); + return properties; + } - public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) { - return GetPropertiesWithMetadata(base.GetProperties(attributes)); - } + Type FindMetadataType() + { + associatedMetadataTypeChecked = true; + if (type == null) + return null; - public override PropertyDescriptorCollection GetProperties() { - return GetPropertiesWithMetadata(base.GetProperties()); - } + object[] attrs = type.GetCustomAttributes(typeof(MetadataTypeAttribute), true); + if (attrs == null || attrs.Length == 0) + return null; - private PropertyDescriptorCollection GetPropertiesWithMetadata(PropertyDescriptorCollection originalCollection) { - if (AssociatedMetadataType == null) { - return originalCollection; - } - - bool customDescriptorsCreated = false; - List tempPropertyDescriptors = new List(); - foreach (PropertyDescriptor propDescriptor in originalCollection) { - Attribute[] newMetadata = TypeDescriptorCache.GetAssociatedMetadata(AssociatedMetadataType, propDescriptor.Name); - PropertyDescriptor descriptor = propDescriptor; - if (newMetadata.Length > 0) { - // Create a metadata descriptor that wraps the property descriptor - descriptor = new _MetadataPropertyDescriptorWrapper(propDescriptor, newMetadata); - customDescriptorsCreated = true; - } - - tempPropertyDescriptors.Add(descriptor); - } - - if (customDescriptorsCreated) { - return new PropertyDescriptorCollection(tempPropertyDescriptors.ToArray(), true); - } - return originalCollection; - } + var attr = attrs[0] as MetadataTypeAttribute; + if (attr == null) + return null; - public override AttributeCollection GetAttributes() { - // Since normal TD behavior is to return cached attribute instances on subsequent - // calls to GetAttributes, we must be sure below to use the TD APIs to get both - // the base and associated attributes - AttributeCollection attributes = base.GetAttributes(); - if (AssociatedMetadataType != null) { - // Note that the use of TypeDescriptor.GetAttributes here opens up the possibility of - // infinite recursion, in the corner case of two Types referencing each other as - // metadata types (or a longer cycle) - Attribute[] newAttributes = TypeDescriptor.GetAttributes(AssociatedMetadataType).OfType().ToArray(); - attributes = AttributeCollection.FromExisting(attributes, newAttributes); - } - return attributes; - } + return attr.MetadataClassType; + } + } - private static class TypeDescriptorCache { - private static readonly Attribute[] emptyAttributes = new Attribute[0]; - - // Stores the associated metadata type for a type - private static readonly Dictionary _metadataTypeCache = new Dictionary(); - - // For a type and a property name stores the attributes for that property name. - private static readonly Dictionary, Attribute[]> _typeMemberCache = new Dictionary, Attribute[]>(); - - // Stores whether or not a type and associated metadata type has been checked for validity - private static readonly Dictionary, bool> _validatedMetadataTypeCache = new Dictionary, bool>(); - - public static void ValidateMetadataType(Type type, Type associatedType) { - Tuple typeTuple = new Tuple(type, associatedType); - - lock (_validatedMetadataTypeCache) { - if (!_validatedMetadataTypeCache.ContainsKey(typeTuple)) { - CheckAssociatedMetadataType(type, associatedType); - _validatedMetadataTypeCache.Add(typeTuple, true); - } - } - } - - public static Type GetAssociatedMetadataType(Type type) { - Type associatedMetadataType = null; - lock (_metadataTypeCache) { - if (_metadataTypeCache.TryGetValue(type, out associatedMetadataType)) { - return associatedMetadataType; - } - } - - // Try association attribute - MetadataTypeAttribute attribute = (MetadataTypeAttribute)Attribute.GetCustomAttribute(type, typeof(MetadataTypeAttribute)); - if (attribute != null) { - associatedMetadataType = attribute.MetadataClassType; - } - - lock (_metadataTypeCache) { - _metadataTypeCache[type] = associatedMetadataType; - } - - return associatedMetadataType; - } - - private static void CheckAssociatedMetadataType(Type mainType, Type associatedMetadataType) { - // Only properties from main type - HashSet mainTypeMemberNames = new HashSet(mainType.GetProperties().Select(p => p.Name)); - - // Properties and fields from buddy type - var buddyFields = associatedMetadataType.GetFields().Select(f => f.Name); - var buddyProperties = associatedMetadataType.GetProperties().Select(p => p.Name); - HashSet buddyTypeMembers = new HashSet(buddyFields.Concat(buddyProperties), StringComparer.Ordinal); - - // Buddy members should be a subset of the main type's members - if (!buddyTypeMembers.IsSubsetOf(mainTypeMemberNames)) { - // Reduce the buddy members to the set not contained in the main members - buddyTypeMembers.ExceptWith(mainTypeMemberNames); - - throw new InvalidOperationException(String.Format( - CultureInfo.CurrentCulture, - MvcResources.PrivateAssociatedMetadataTypeTypeDescriptor_MetadataTypeContainsUnknownProperties, - mainType.FullName, - String.Join(", ", buddyTypeMembers.ToArray()))); - } - } - - public static Attribute[] GetAssociatedMetadata(Type type, string memberName) { - var memberTuple = new Tuple(type, memberName); - Attribute[] attributes; - lock (_typeMemberCache) { - if (_typeMemberCache.TryGetValue(memberTuple, out attributes)) { - return attributes; - } - } - - // Allow fields and properties - MemberTypes allowedMemberTypes = MemberTypes.Property | MemberTypes.Field; - // Only public static/instance members - BindingFlags searchFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; - // Try to find a matching member on type - MemberInfo matchingMember = type.GetMember(memberName, allowedMemberTypes, searchFlags).FirstOrDefault(); - if (matchingMember != null) { - attributes = Attribute.GetCustomAttributes(matchingMember, true /* inherit */); - } - else { - attributes = emptyAttributes; - } - - lock (_typeMemberCache) { - _typeMemberCache[memberTuple] = attributes; - } - return attributes; - } - - private class Tuple { - public T1 Item1 { get; set; } - public T2 Item2 { get; set; } - - public Tuple(T1 item1, T2 item2) { - Item1 = item1; - Item2 = item2; - } - - public override int GetHashCode() { - int h1 = Item1.GetHashCode(); - int h2 = Item2.GetHashCode(); - return ((h1 << 5) + h1) ^ h2; - } - - public override bool Equals(object obj) { - var other = obj as Tuple; - if (other != null) { - return other.Item1.Equals(Item1) && other.Item2.Equals(Item2); - } - return false; - } - } - } + #endregion + + #region _AssociatedMetadataTypePropertyDescriptor + class _AssociatedMetadataTypePropertyDescriptor : _ReflectionPropertyDescriptor + { + MemberInfo metaTypeMember; + + public _AssociatedMetadataTypePropertyDescriptor(PropertyInfo typeProperty, MemberInfo metaTypeMember) + : base(typeProperty) + { + this.metaTypeMember = metaTypeMember; + } + + protected override void FillAttributes(IList attributeList) + { + base.FillAttributes(attributeList); + if (metaTypeMember == null) + return; + + object[] attributes = metaTypeMember.GetCustomAttributes(false); + if (attributes == null || attributes.Length == 0) + return; + + foreach (object o in attributes) + { + var attr = o as Attribute; + if (attr == null) + continue; + + attributeList.Add(attr); } + } + } - private class _MetadataPropertyDescriptorWrapper : PropertyDescriptor { - private PropertyDescriptor _descriptor; - private bool _isReadOnly; + #endregion + + #region _ReflectionPropertyDescriptor + + class _ReflectionPropertyDescriptor : PropertyDescriptor + { + PropertyInfo _member; + Type _componentType; + Type _propertyType; + PropertyInfo getter, setter; + bool accessors_inited; + + public _ReflectionPropertyDescriptor(Type componentType, PropertyDescriptor oldPropertyDescriptor, Attribute[] attributes) + : base(oldPropertyDescriptor, attributes) + { + _componentType = componentType; + _propertyType = oldPropertyDescriptor.PropertyType; + } + + public _ReflectionPropertyDescriptor(Type componentType, string name, Type type, Attribute[] attributes) + : base(name, attributes) + { + _componentType = componentType; + _propertyType = type; + } + + public _ReflectionPropertyDescriptor(PropertyInfo info) + : base(info.Name, null) + { + _member = info; + _componentType = _member.DeclaringType; + _propertyType = info.PropertyType; + } + + PropertyInfo GetPropertyInfo() + { + if (_member == null) + { + _member = _componentType.GetProperty(Name, BindingFlags.GetProperty | BindingFlags.NonPublic | + BindingFlags.Public | BindingFlags.Instance, + null, this.PropertyType, + new Type[0], new ParameterModifier[0]); + if (_member == null) + throw new ArgumentException("Accessor methods for the " + Name + " property are missing"); + } + return _member; + } + + public override Type ComponentType + { + get { return _componentType; } + } + + public override bool IsReadOnly + { + get + { + ReadOnlyAttribute attrib = ((ReadOnlyAttribute)Attributes[typeof(ReadOnlyAttribute)]); + return !GetPropertyInfo().CanWrite || attrib.IsReadOnly; + } + } + + public override Type PropertyType + { + get { return _propertyType; } + } + + // The last added to the list attributes have higher precedence + // + protected override void FillAttributes(IList attributeList) + { + base.FillAttributes(attributeList); + + if (!GetPropertyInfo().CanWrite) + attributeList.Add(ReadOnlyAttribute.Yes); + + // PropertyDescriptor merges the attributes of both virtual and also "new" properties + // in the the component type hierarchy. + // + int numberOfBaseTypes = 0; + Type baseType = this.ComponentType; + while (baseType != null && baseType != typeof(object)) + { + numberOfBaseTypes++; + baseType = baseType.BaseType; + } - public _MetadataPropertyDescriptorWrapper(PropertyDescriptor descriptor, Attribute[] newAttributes) - : base(descriptor, newAttributes) { - _descriptor = descriptor; - var readOnlyAttribute = newAttributes.OfType().FirstOrDefault(); - _isReadOnly = (readOnlyAttribute != null ? readOnlyAttribute.IsReadOnly : false); - } + Attribute[][] hierarchyAttributes = new Attribute[numberOfBaseTypes][]; + baseType = this.ComponentType; + while (baseType != null && baseType != typeof(object)) + { + PropertyInfo property = baseType.GetProperty(Name, BindingFlags.NonPublic | + BindingFlags.Public | BindingFlags.Instance | + BindingFlags.DeclaredOnly, + null, this.PropertyType, + new Type[0], new ParameterModifier[0]); + if (property != null) + { + object[] attrObjects = property.GetCustomAttributes(false); + Attribute[] attrsArray = new Attribute[attrObjects.Length]; + attrObjects.CopyTo(attrsArray, 0); + // add in reverse order so that the base types have lower precedence + hierarchyAttributes[--numberOfBaseTypes] = attrsArray; + } + baseType = baseType.BaseType; + } - public override void AddValueChanged(object component, EventHandler handler) { _descriptor.AddValueChanged(component, handler); } + foreach (Attribute[] attrArray in hierarchyAttributes) + { + if (attrArray != null) + { + foreach (Attribute attr in attrArray) + attributeList.Add(attr); + } + } - public override bool CanResetValue(object component) { return _descriptor.CanResetValue(component); } + foreach (Attribute attribute in TypeDescriptor.GetAttributes(PropertyType)) + attributeList.Add(attribute); + } + +#pragma warning disable 0618 + public override object GetValue(object component) + { + component = MemberDescriptor.GetInvokee(_componentType, component); + InitAccessors(); + return getter.GetValue(component, null); + } +#pragma warning restore 0618 + + DesignerTransaction CreateTransaction(object obj, string description) + { + IComponent com = obj as IComponent; + if (com == null || com.Site == null) + return null; + + IDesignerHost dh = (IDesignerHost)com.Site.GetService(typeof(IDesignerHost)); + if (dh == null) + return null; + + DesignerTransaction tran = dh.CreateTransaction(description); + IComponentChangeService ccs = (IComponentChangeService)com.Site.GetService(typeof(IComponentChangeService)); + if (ccs != null) + ccs.OnComponentChanging(com, this); + return tran; + } + + void EndTransaction(object obj, DesignerTransaction tran, object oldValue, object newValue, bool commit) + { + if (tran == null) + { + // FIXME: EventArgs might be differen type. + OnValueChanged(obj, new PropertyChangedEventArgs(Name)); + return; + } - public override Type ComponentType { get { return _descriptor.ComponentType; } } + if (commit) + { + IComponent com = obj as IComponent; + IComponentChangeService ccs = (IComponentChangeService)com.Site.GetService(typeof(IComponentChangeService)); + if (ccs != null) + ccs.OnComponentChanged(com, this, oldValue, newValue); + tran.Commit(); + // FIXME: EventArgs might be differen type. + OnValueChanged(obj, new PropertyChangedEventArgs(Name)); + } + else + tran.Cancel(); + } + + /* + This method exists because reflection is way too low level for what we need. + A given virtual property that is partially overriden by a child won't show the + non-overriden accessor in PropertyInfo. IOW: + class Parent { + public virtual string Prop { get; set; } + } + class Child : Parent { + public override string Prop { + get { return "child"; } + } + } + PropertyInfo pi = typeof (Child).GetProperty ("Prop"); + pi.GetGetMethod (); //returns the MethodInfo for the overridden getter + pi.GetSetMethod (); //returns null as no override exists + */ + void InitAccessors() + { + if (accessors_inited) + return; + PropertyInfo prop = GetPropertyInfo(); + MethodInfo setterMethod, getterMethod; + setterMethod = prop.GetSetMethod(true); + getterMethod = prop.GetGetMethod(true); + + if (getterMethod != null) + getter = prop; + + if (setterMethod != null) + setter = prop; + + + if (setterMethod != null && getterMethod != null) + {//both exist + accessors_inited = true; + return; + } + if (setterMethod == null && getterMethod == null) + {//neither exist, this is a broken property + accessors_inited = true; + return; + } - public override object GetValue(object component) { return _descriptor.GetValue(component); } + //In order to detect that this is a virtual property with override, we check the non null accessor + MethodInfo mi = getterMethod != null ? getterMethod : setterMethod; - public override bool IsReadOnly { - get { - // Dev10 Bug 594083 - // It's not enough to call the wrapped _descriptor because it does not know anything about - // new attributes passed into the constructor of this class. - return _isReadOnly || _descriptor.IsReadOnly; - } - } + if (mi == null || !mi.IsVirtual || (mi.Attributes & MethodAttributes.NewSlot) == MethodAttributes.NewSlot) + { + accessors_inited = true; + return; + } - public override Type PropertyType { get { return _descriptor.PropertyType; } } + Type type = _componentType.BaseType; + while (type != null && type != typeof(object)) + { + prop = type.GetProperty(Name, BindingFlags.GetProperty | BindingFlags.NonPublic | + BindingFlags.Public | BindingFlags.Instance, + null, this.PropertyType, + new Type[0], new ParameterModifier[0]); + if (prop == null) //nothing left to search + break; + if (setterMethod == null) + setterMethod = mi = prop.GetSetMethod(); + else + getterMethod = mi = prop.GetGetMethod(); + + if (getterMethod != null && getter == null) + getter = prop; + + if (setterMethod != null && setter == null) + setter = prop; + + if (mi != null) + break; + type = type.BaseType; + } + accessors_inited = true; + } + +#pragma warning disable 0618 + public override void SetValue(object component, object value) + { + DesignerTransaction tran = CreateTransaction(component, "Set Property '" + Name + "'"); + + object propertyHolder = MemberDescriptor.GetInvokee(_componentType, component); + object old = GetValue(propertyHolder); + + try + { + InitAccessors(); + setter.SetValue(propertyHolder, value, null); + EndTransaction(component, tran, old, value, true); + } + catch + { + EndTransaction(component, tran, old, value, false); + throw; + } + } +#pragma warning restore 0618 + + MethodInfo FindPropertyMethod(object o, string method_name) + { + MethodInfo mi = null; + string name = method_name + Name; + + foreach (MethodInfo m in o.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) + { + // XXX should we really not check the return type of the method? + if (m.Name == name && m.GetParameters().Length == 0) + { + mi = m; + break; + } + } - public override void RemoveValueChanged(object component, EventHandler handler) { _descriptor.RemoveValueChanged(component, handler); } + return mi; + } - public override void ResetValue(object component) { _descriptor.ResetValue(component); } +#pragma warning disable 0618 + public override void ResetValue(object component) + { + object propertyHolder = MemberDescriptor.GetInvokee(_componentType, component); - public override void SetValue(object component, object value) { _descriptor.SetValue(component, value); } + DefaultValueAttribute attrib = ((DefaultValueAttribute)Attributes[typeof(DefaultValueAttribute)]); + if (attrib != null) + SetValue(propertyHolder, attrib.Value); - public override bool ShouldSerializeValue(object component) { return _descriptor.ShouldSerializeValue(component); } + DesignerTransaction tran = CreateTransaction(component, "Reset Property '" + Name + "'"); + object old = GetValue(propertyHolder); - public override bool SupportsChangeEvents { get { return _descriptor.SupportsChangeEvents; } } + try + { + MethodInfo mi = FindPropertyMethod(propertyHolder, "Reset"); + if (mi != null) + mi.Invoke(propertyHolder, null); + EndTransaction(component, tran, old, GetValue(propertyHolder), true); + } + catch + { + EndTransaction(component, tran, old, GetValue(propertyHolder), false); + throw; + } + } +#pragma warning restore 0618 + +#pragma warning disable 0618 + public override bool CanResetValue(object component) + { + component = MemberDescriptor.GetInvokee(_componentType, component); + + DefaultValueAttribute attrib = ((DefaultValueAttribute)Attributes[typeof(DefaultValueAttribute)]); + if (attrib != null) + { + object current = GetValue(component); + if (attrib.Value == null || current == null) + { + if (attrib.Value != current) + return true; + if (attrib.Value == null && current == null) + return false; + } + + return !attrib.Value.Equals(current); } - + else + { +#if NET_2_0 + if (!_member.CanWrite) + return false; +#endif + MethodInfo mi = FindPropertyMethod(component, "ShouldPersist"); + if (mi != null) + return (bool)mi.Invoke(component, null); + + mi = FindPropertyMethod(component, "ShouldSerialize"); + if (mi != null && !((bool)mi.Invoke(component, null))) + return false; + + mi = FindPropertyMethod(component, "Reset"); + return mi != null; + } + } +#pragma warning restore 0618 + +#pragma warning disable 0618 + public override bool ShouldSerializeValue(object component) + { + component = MemberDescriptor.GetInvokee(_componentType, component); + + if (IsReadOnly) + { + MethodInfo mi = FindPropertyMethod(component, "ShouldSerialize"); + if (mi != null) + return (bool)mi.Invoke(component, null); + return Attributes.Contains(DesignerSerializationVisibilityAttribute.Content); + } + + DefaultValueAttribute attrib = ((DefaultValueAttribute)Attributes[typeof(DefaultValueAttribute)]); + if (attrib != null) + { + object current = GetValue(component); + if (attrib.Value == null || current == null) + return attrib.Value != current; + return !attrib.Value.Equals(current); + } + else + { + MethodInfo mi = FindPropertyMethod(component, "ShouldSerialize"); + if (mi != null) + return (bool)mi.Invoke(component, null); + // MSDN: If this method cannot find a DefaultValueAttribute or a ShouldSerializeMyProperty method, + // it cannot create optimizations and it returns true. + return true; + } + } } +#pragma warning restore 0618 + + #endregion + } } +