using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
namespace Core.Common.Gui.PropertyBag
{
///
/// This class represents a single property.
///
public class PropertySpec
{
private readonly System.Reflection.PropertyInfo propertyInfo;
///
/// Initializes a new instance of the class for a given
/// property meta-data object.
///
/// The property information.
/// When is
/// an index property.
public PropertySpec(System.Reflection.PropertyInfo propertyInfo)
{
if (propertyInfo.GetIndexParameters().Length > 0)
{
throw new ArgumentException("Index properties are not allowed.", "propertyInfo");
}
this.propertyInfo = propertyInfo;
Name = propertyInfo.Name;
TypeName = propertyInfo.PropertyType.AssemblyQualifiedName;
var attributeList = propertyInfo.GetCustomAttributes(true).OfType().Select(attrib => attrib).ToList();
if (propertyInfo.GetSetMethod() == null)
{
attributeList.Add(new ReadOnlyAttribute(true));
}
Attributes = attributeList.ToArray();
}
///
/// Gets or sets a collection of additional s for this property.
/// This can be used to specify attributes beyond those supported intrinsically by the
/// class, such as
/// and .
///
public Attribute[] Attributes { get; set; }
///
/// Gets the name of the property.
///
public string Name { get; private set; }
///
/// Gets the fully qualified name of the type of this property.
///
public string TypeName { get; private set; }
///
/// Sets the property represented by this instance of some object instance.
///
/// The instance to be updated.
/// The new value for the property.
/// When
///
/// - Represented property is an index-property.
/// - Property setter is not available.
/// - does not match the target type.
/// - Property is an instance property but is null.
/// - is of incorrect type.
/// - An error occurred while setting the property value. The
/// property indicates the reason for the error.
///
/// Calling this method while property
/// has no setter.
public void SetValue(object instance, object newValue)
{
var setMethodInfo = propertyInfo.GetSetMethod();
if (setMethodInfo == null)
{
throw new InvalidOperationException("Property lacks public setter!");
}
try
{
setMethodInfo.Invoke(instance, new[]
{
newValue
});
}
catch (TargetException e)
{
object type = instance == null ? null : instance.GetType();
var message = string.Format("Are you calling SetValue on the correct instance? Expected '{0}', but was '{1}'",
propertyInfo.DeclaringType, type);
throw new ArgumentException(message, "instance", e);
}
catch (TargetInvocationException e)
{
var message = string.Format("Something went wrong while setting property with value '{0}'; Check InnerException for more information.",
newValue);
throw new ArgumentException(message, "newValue", e);
}
}
///
/// Gets the property value represented by this instance of some object instance.
///
/// The instance that holds the property to be retrieved.
/// When
///
/// - Represented property is an index-property.
/// - Property getter is not available.
/// - does not match the target type.
/// - Property is an instance property but is null.
/// - An error occurred while setting the property value. The
/// property indicates the reason for the error.
///
/// Calling this method while property
/// has no getter.
public object GetValue(object instance)
{
var getMethodInfo = propertyInfo.GetGetMethod();
if (getMethodInfo == null)
{
throw new InvalidOperationException("Property lacks public getter!");
}
try
{
return getMethodInfo.Invoke(instance, new object[0]);
}
catch (TargetException e)
{
object type = instance == null ? null : instance.GetType();
var message = string.Format("Are you calling GetValue on the correct instance? Expected '{0}', but was '{1}'",
propertyInfo.DeclaringType, type);
throw new ArgumentException(message, "instance", e);
}
catch (TargetInvocationException e)
{
throw new ArgumentException("Something went wrong while getting property; Check InnerException for more information.", "instance", e);
}
}
///
/// Determines whether the captured property is decorated with [is non custom expandable object property].
/// that is configured to use .
///
/// Returns true if a is declared using
/// , false if no match has been found or when
/// the type converter inherits from .
/// Custom implementations of is
/// likely to have behavior that Core.Common.Gui namespace cannot account for. As
/// such those properties will be considered not having the expandable object type converter.
public bool IsNonCustomExpandableObjectProperty()
{
var typeConverterClassName = propertyInfo.GetCustomAttributes(typeof(TypeConverterAttribute), false)
.OfType()
.Select(tca => tca.ConverterTypeName)
.Where(n => !string.IsNullOrEmpty(n));
foreach (string typeName in typeConverterClassName)
{
try
{
var type = Type.GetType(typeName);
if (type != null)
{
if (typeof(ExpandableObjectConverter) == type)
{
return true;
}
}
}
catch (Exception)
{
// gulp
}
}
return false;
}
}
}