// Copyright (C) Stichting Deltares 2016. All rights reserved.
//
// This file is part of Ringtoets.
//
// Ringtoets is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see .
//
// All names, logos, and references to "Deltares" are registered trademarks of
// Stichting Deltares and remain full property of Stichting Deltares at all times.
// All rights reserved.
using System;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using log4net;
namespace Core.Common.Gui.PropertyBag
{
///
/// This class represents a single property.
///
public class PropertySpec
{
private static readonly ILog log = LogManager.GetLogger(typeof(PropertySpec));
private readonly PropertyInfo propertyInfo;
///
/// Initializes a new instance of the class for a given
/// property meta-data object.
///
/// The property information.
/// Thrown when is
/// an index property.
/// When is null.
public PropertySpec(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
{
throw new ArgumentNullException("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 = Attribute.GetCustomAttributes(propertyInfo, true).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; private 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.
/// Thrown when
///
/// - Represented property is an index-property.
/// - 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.
/// Calling the method resulted in an exception.
/// Thrown when is null
/// or the method is not defined on .
public void SetValue(object instance, object newValue)
{
var setMethodInfo = propertyInfo.GetSetMethod();
if (setMethodInfo == null)
{
throw new InvalidOperationException("Property lacks public setter!");
}
setMethodInfo.Invoke(instance, new[]
{
newValue
});
}
///
/// Gets the property value represented by this instance of some object instance.
///
/// The instance that holds the property to be retrieved.
/// The property value on .
/// Thrown when
///
/// - Represented property is an index-property.
/// - 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.
///
/// Thrown when 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(CultureInfo.CurrentCulture,
"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
/// 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 typeConverterAttribute = (TypeConverterAttribute) Attribute.GetCustomAttribute(propertyInfo, typeof(TypeConverterAttribute), true);
if (typeConverterAttribute != null)
{
try
{
var type = Type.GetType(typeConverterAttribute.ConverterTypeName);
if (type != null && typeof(ExpandableObjectConverter) == type)
{
return true;
}
}
catch (Exception e)
{
if (e is TargetInvocationException || e is ArgumentException ||
e is TypeLoadException || e is FileLoadException || e is BadImageFormatException)
{
log.DebugFormat("Unable to find TypeConverter of type '{0}", typeConverterAttribute.ConverterTypeName);
}
else
{
throw; // Not expected exception -> Fail fast
}
}
}
return false;
}
}
}