From 4dc026d2e688527e5337b4c18afb78f4c37149ca Mon Sep 17 00:00:00 2001 From: bbaia Date: Thu, 8 Apr 2010 09:45:26 +0000 Subject: [PATCH] Fix TypeNameAutoProxyCreator to use element for the TypeNames property [SPRNET-1184] --- .../AutoProxy/TypeNameAutoProxyCreator.cs | 13 +- .../Expressions/PropertyOrFieldNode.cs | 1444 ++++++++--------- 2 files changed, 732 insertions(+), 725 deletions(-) diff --git a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/TypeNameAutoProxyCreator.cs b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/TypeNameAutoProxyCreator.cs index 9e768e0e..410700e7 100644 --- a/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/TypeNameAutoProxyCreator.cs +++ b/src/Spring/Spring.Aop/Aop/Framework/AutoProxy/TypeNameAutoProxyCreator.cs @@ -41,7 +41,14 @@ namespace Spring.Aop.Framework.AutoProxy /// public string[] TypeNames { - get { return _typeNameFilter.TypeNamePatterns; } + get + { + if (_typeNameFilter != null) + { + return _typeNameFilter.TypeNamePatterns; + } + return null; + } set { AssertUtils.ArgumentNotNull(value, "TypeNames"); @@ -55,8 +62,8 @@ namespace Spring.Aop.Framework.AutoProxy /// /// Override this method to allow or reject proxying for the given object. /// - /// the object's type - /// the name of the object + /// the object's type + /// the name of the object /// /// whether the given object shall be proxied. protected override bool IsEligibleForProxying(Type targetType, string targetName) diff --git a/src/Spring/Spring.Core/Expressions/PropertyOrFieldNode.cs b/src/Spring/Spring.Core/Expressions/PropertyOrFieldNode.cs index eef97bdc..c2da3a68 100644 --- a/src/Spring/Spring.Core/Expressions/PropertyOrFieldNode.cs +++ b/src/Spring/Spring.Core/Expressions/PropertyOrFieldNode.cs @@ -1,724 +1,724 @@ -#region License - -/* - * Copyright © 2002-2005 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#endregion - -#region Imports - -using System; -using System.Collections; -using System.Reflection; -using System.Runtime.Remoting; -using System.Runtime.Serialization; - -using Spring.Collections; -using Spring.Core; -using Spring.Core.TypeConversion; -using Spring.Core.TypeResolution; -using Spring.Util; -using Spring.Reflection.Dynamic; - -#endregion - -namespace Spring.Expressions -{ - /// - /// Represents node that navigates to object's property or public field. - /// - /// Aleksandar Seovic - [Serializable] - public class PropertyOrFieldNode : BaseNode - { - //private static readonly Common.Logging.ILog Log = Common.Logging.LogManager.GetLogger(typeof(PropertyOrFieldNode)); - - private const BindingFlags BINDING_FLAGS = - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | - BindingFlags.IgnoreCase; - - private string memberName; - private IValueAccessor accessor; - - /// - /// Create a new instance - /// - public PropertyOrFieldNode() - { - } - - /// - /// Create a new instance from SerializationInfo - /// - protected PropertyOrFieldNode(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - - /// - /// Initializes the node. - /// - /// The parent. - private void InitializeNode(object context) - { - Type contextType = (context == null || context is Type ? context as Type : context.GetType()); - - if (accessor == null || accessor.RequiresRefresh(contextType)) - { - memberName = this.getText(); - - // clear cached member info if context type has changed (for example, when ASP.NET page is recompiled) - if (accessor != null && accessor.RequiresRefresh(contextType)) - { - accessor = null; - } - - // initialize this node if necessary - if (contextType != null && accessor == null) - { - // try to initialize node as enum value first - if (contextType.IsEnum) - { - try - { - accessor = new EnumValueAccessor(Enum.Parse(contextType, memberName, true)); - } - catch (ArgumentException) - { - // ArgumentException will be thrown if specified member name is not a valid - // enum value for the context type. We should just ignore it and continue processing, - // because the specified member could be a property of a Type class (i.e. EnumType.FullName) - } - } - - // then try to initialize node as property or field value - if (accessor == null) - { - // check the context type first - accessor = GetPropertyOrFieldAccessor(contextType, memberName, BINDING_FLAGS); - - // if not found, probe the Type type - if (accessor == null && context is Type) - { - accessor = GetPropertyOrFieldAccessor(typeof(Type), memberName, BINDING_FLAGS); - } - } - } - - // if there is still no match, try to initialize node as type accessor - if (accessor == null) - { - try - { - accessor = new TypeValueAccessor(TypeResolutionUtils.ResolveType(memberName)); - } - catch (TypeLoadException) - { - if (context == null) - { - throw new NullValueInNestedPathException("Cannot initialize property or field node '" + - memberName + - "' because the specified context is null."); - } - else - { - throw new InvalidPropertyException(contextType, memberName, - "'" + memberName + - "' node cannot be resolved for the specified context [" + - context + "]."); - } - } - } - } - } - - /// - /// Attempts to resolve property or field. - /// - /// - /// Type to search for a property or a field. - /// - /// - /// Property or field name. - /// - /// - /// Binding flags to use. - /// - /// - /// Resolved property or field accessor, or null - /// if specified cannot be resolved. - /// - private static IValueAccessor GetPropertyOrFieldAccessor(Type contextType, string memberName, BindingFlags bindingFlags) - { - try - { - PropertyInfo pi = contextType.GetProperty(memberName, bindingFlags); - if (pi == null) - { - FieldInfo fi = contextType.GetField(memberName, bindingFlags); - if (fi != null) - { - return new FieldValueAccessor(fi); - } - } - else - { - return new PropertyValueAccessor(pi); - } - } - catch (AmbiguousMatchException) - { - PropertyInfo pi = null; - - // search type hierarchy - while (contextType != typeof(object)) - { - pi = contextType.GetProperty(memberName, bindingFlags | BindingFlags.DeclaredOnly); - if (pi == null) - { - FieldInfo fi = contextType.GetField(memberName, bindingFlags | BindingFlags.DeclaredOnly); - if (fi != null) - { - return new FieldValueAccessor(fi); - } - } - else - { - return new PropertyValueAccessor(pi); - } - contextType = contextType.BaseType; - } - } - - return null; - } - - /// - /// Returns node's value for the given context. - /// - /// Context to evaluate expressions against. - /// Current expression evaluation context. - /// Node's value. - protected override object Get(object context, EvaluationContext evalContext) - { - lock (this) - { - InitializeNode(context); - - if (context == null && accessor.RequiresContext) - { - throw new NullValueInNestedPathException( - "Cannot retrieve the value of a field or property '" + this.memberName - + "', because context for its resolution is null."); - } - if (IsProperty || IsField) - { - return GetPropertyOrFieldValue(context, evalContext); - } - else - { - return accessor.Get(context); - } - } - } - - /// - /// Sets node's value for the given context. - /// - /// Context to evaluate expressions against. - /// Current expression evaluation context. - /// New value for this node. - protected override void Set(object context, EvaluationContext evalContext, object newValue) - { - lock (this) - { - InitializeNode(context); - - if (context == null && accessor.RequiresContext) - { - throw new NullValueInNestedPathException( - "Cannot set the value of a field or property '" + this.memberName - + "', because context for its resolution is null."); - } - if (IsProperty || IsField) - { - SetPropertyOrFieldValue(context, evalContext, newValue); - } - else - { - accessor.Set(context, newValue); - } - } - } - - /// - /// Gets a value indicating whether this node represents a property. - /// - /// - /// true if this node is a property; otherwise, false. - /// - private bool IsProperty - { - get { return accessor is PropertyValueAccessor; } - } - - /// - /// Gets a value indicating whether this node represents a field. - /// - /// - /// true if this node is a field; otherwise, false. - /// - private bool IsField - { - get { return accessor is FieldValueAccessor; } - } - - /// - /// Retrieves property or field value. - /// - /// Context to evaluate expressions against. - /// Current expression evaluation context. - /// Property or field value. - private object GetPropertyOrFieldValue(object context, EvaluationContext evalContext) - { - try - { - return accessor.Get(context); - } - catch (InvalidOperationException) - { - throw new NotReadablePropertyException(evalContext.RootContextType, this.memberName); - } - catch (TargetInvocationException e) - { - throw new InvalidPropertyException(evalContext.RootContextType, this.memberName, - "Getter for property '" + this.memberName + "' threw an exception.", - e); - } - catch (UnauthorizedAccessException e) - { - throw new InvalidPropertyException(evalContext.RootContextType, this.memberName, - "Illegal attempt to get value for the property '" + this.memberName + - "'.", e); - } - } - - /// - /// Sets property value, doing any type conversions that are necessary along the way. - /// - /// Context to evaluate expressions against. - /// Current expression evaluation context. - /// New value for this node. - private void SetPropertyOrFieldValue(object context, EvaluationContext evalContext, object newValue) - { - bool isWriteable = accessor.IsWriteable; - Type targetType = accessor.TargetType; - - try - { - if (!isWriteable) - { - if (!AddToCollections(context, evalContext, newValue)) - { - throw new NotWritablePropertyException( - "Can't change the value of the read-only property or field '" + this.memberName + "'."); - } - } - else if (targetType.IsPrimitive && (newValue == null || String.Empty.Equals(newValue))) - { - throw new ArgumentException("Invalid value [" + newValue + "] for property or field '" + - this.memberName + "' of primitive type [" - + targetType + "]"); +#region License + +/* + * Copyright © 2002-2005 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Runtime.Remoting; +using System.Runtime.Serialization; + +using Spring.Collections; +using Spring.Core; +using Spring.Core.TypeConversion; +using Spring.Core.TypeResolution; +using Spring.Util; +using Spring.Reflection.Dynamic; + +#endregion + +namespace Spring.Expressions +{ + /// + /// Represents node that navigates to object's property or public field. + /// + /// Aleksandar Seovic + [Serializable] + public class PropertyOrFieldNode : BaseNode + { + //private static readonly Common.Logging.ILog Log = Common.Logging.LogManager.GetLogger(typeof(PropertyOrFieldNode)); + + private const BindingFlags BINDING_FLAGS = + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | + BindingFlags.IgnoreCase; + + private string memberName; + private IValueAccessor accessor; + + /// + /// Create a new instance + /// + public PropertyOrFieldNode() + { + } + + /// + /// Create a new instance from SerializationInfo + /// + protected PropertyOrFieldNode(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + /// + /// Initializes the node. + /// + /// The parent. + private void InitializeNode(object context) + { + Type contextType = (context == null || context is Type ? context as Type : context.GetType()); + + if (accessor == null || accessor.RequiresRefresh(contextType)) + { + memberName = this.getText(); + + // clear cached member info if context type has changed (for example, when ASP.NET page is recompiled) + if (accessor != null && accessor.RequiresRefresh(contextType)) + { + accessor = null; } - else if (newValue == null || ObjectUtils.IsAssignable(targetType, newValue)) // targetType.IsAssignableFrom(newValue.GetType()) - { - SetPropertyOrFieldValueInternal(context, newValue); - } - else if (!RemotingServices.IsTransparentProxy(newValue) && - (newValue is IList || newValue is IDictionary || newValue is ISet)) - { - if (!AddToCollections(context, evalContext, newValue)) - { - object tmpValue = - TypeConversionUtils.ConvertValueIfNecessary(targetType, newValue, this.memberName); - SetPropertyOrFieldValueInternal(context, tmpValue); - } - } - else - { - object tmpValue = TypeConversionUtils.ConvertValueIfNecessary(targetType, newValue, this.memberName); - SetPropertyOrFieldValueInternal(context, tmpValue); - } - } - catch (TargetInvocationException ex) - { - PropertyChangeEventArgs propertyChangeEvent = - new PropertyChangeEventArgs(this.memberName, null, newValue); - if (ex.GetBaseException() is InvalidCastException) - { - throw new TypeMismatchException(propertyChangeEvent, targetType, ex.GetBaseException()); - } - else - { - throw new MethodInvocationException(ex.GetBaseException(), propertyChangeEvent); - } - } - catch (UnauthorizedAccessException ex) - { - throw new FatalReflectionException("Illegal attempt to set property '" + this.memberName + "'", ex); - } - catch (NotWritablePropertyException) - { - throw; - } - catch (NotReadablePropertyException) - { - throw; - } - catch (ArgumentException ex) - { - PropertyChangeEventArgs propertyChangeEvent = - new PropertyChangeEventArgs(this.memberName, null, newValue); - throw new TypeMismatchException(propertyChangeEvent, targetType, ex); - } - } - - /// - /// Sets property or field value using either dynamic or standard reflection. - /// - /// Object to evaluate node against. - /// New value for this node, converted to appropriate type. - private void SetPropertyOrFieldValueInternal(object context, object newValue) - { - accessor.Set(context, newValue); - } - - /// - /// In the case of read only collections or custom collections that are not assignable from - /// IList, try to add to the collection. - /// - /// Context to evaluate expressions against. - /// Current expression evaluation context. - /// New value for this node. - /// true if was able add to IList, IDictionary, or ISet - private bool AddToCollections(object context, EvaluationContext evalContext, object newValue) - { - // short-circuit if accessor is not readable - if (!this.accessor.IsReadable) - { - return false; - } - - bool added = false; - - // try adding values if property is a list... - if (newValue is IList && !RemotingServices.IsTransparentProxy(newValue)) - { - IList currentValue = (IList)Get(context, evalContext); - if (currentValue != null && !currentValue.IsFixedSize && !currentValue.IsReadOnly) - { - foreach (object el in (IList)newValue) - { - currentValue.Add(el); - } - added = true; - } - } - // try adding values if property is a dictionary... - else if (newValue is IDictionary && !RemotingServices.IsTransparentProxy(newValue)) - { - IDictionary currentValue = (IDictionary)Get(context, evalContext); - if (currentValue != null && !currentValue.IsFixedSize && !currentValue.IsReadOnly) - { - foreach (DictionaryEntry entry in (IDictionary)newValue) - { - currentValue[entry.Key] = entry.Value; - } - added = true; - } - } - // try adding values if property is a set... - else if (newValue is ISet && !RemotingServices.IsTransparentProxy(newValue)) - { - ISet currentValue = (ISet)Get(context, evalContext); - if (currentValue != null) - { - currentValue.AddAll((ICollection)newValue); - added = true; - } - } - return added; - } - - /// - /// Utility method that is needed by ObjectWrapper and AbstractAutowireCapableObjectFactory. - /// We try as hard as we can, but there are instances when we won't be able to obtain PropertyInfo... - /// - /// Context to resolve property against. - /// PropertyInfo for this node. - internal MemberInfo GetMemberInfo(object context) - { - lock (this) - { - InitializeNode(context); - } - return accessor.MemberInfo; - - //if (IsProperty) - //{ - // return (((PropertyValueAccessor) accessor).MemberInfo); - //} - //else - //{ - // throw new FatalObjectException( - // "Cannot obtain PropertyInfo from an expression that does not resolve to a property."); - //} - } - - #region IValueAccessor interface - - private interface IValueAccessor - { - object Get(object context); - void Set(object context, object value); - - bool IsReadable { get; } - bool IsWriteable { get; } - bool RequiresContext { get; } - Type TargetType { get; } - MemberInfo MemberInfo { get; } - bool RequiresRefresh(Type contextType); - } - - #endregion - - #region BaseValueAccessor implementation - - private abstract class BaseValueAccessor : IValueAccessor - { - public abstract object Get(object context); - - public abstract void Set(object context, object value); - - public virtual bool IsReadable - { - get { return true; } - } - - public virtual bool IsWriteable - { - get { return false; } - } - - public virtual bool RequiresContext - { - get { return false; } - } - - public virtual Type TargetType - { - get { throw new NotSupportedException(); } - } - - public virtual MemberInfo MemberInfo - { - get { throw new NotSupportedException(); } - } - - public virtual bool RequiresRefresh(Type contextType) - { - return false; - } - } - - #endregion - - #region PropertyValueAccessor implementation - - private class PropertyValueAccessor : BaseValueAccessor - { - private SafeProperty property; - private string name; - private bool isReadable; - private bool isWriteable; - private Type targetType; - private Type contextType; - - public PropertyValueAccessor(PropertyInfo propertyInfo) - { - this.name = propertyInfo.Name; - this.isReadable = propertyInfo.CanRead; - this.isWriteable = propertyInfo.CanWrite; - this.targetType = propertyInfo.PropertyType; - this.contextType = propertyInfo.DeclaringType; - this.property = new SafeProperty(propertyInfo); - } - - public override object Get(object context) - { - if (!isReadable) - { - throw new NotReadablePropertyException("Cannot get a non-readable property [" + name + "]"); - } - return property.GetValue(context); - } - - public override void Set(object context, object value) - { - if (!isWriteable) - { - throw new NotWritablePropertyException("Cannot set a read-only property [" + name + "]"); - } - property.SetValue(context, value); - } - - public override bool IsReadable - { - get { return isReadable; } - } - - public override bool IsWriteable - { - get { return isWriteable; } - } - - public override bool RequiresContext - { - get { return true; } - } - - public override Type TargetType - { - get { return targetType; } - } - - public override MemberInfo MemberInfo - { - get { return property.PropertyInfo; } - } - - public override bool RequiresRefresh(Type contextType) - { - return this.contextType != contextType; - } - } - - #endregion - - #region FieldValueAccessor implementation - - private class FieldValueAccessor : BaseValueAccessor - { - private SafeField field; - private bool isWriteable; - private Type targetType; - private Type contextType; - - public FieldValueAccessor(FieldInfo fieldInfo) - { - this.field = new SafeField(fieldInfo); - this.isWriteable = !(fieldInfo.IsInitOnly || fieldInfo.IsLiteral); - this.targetType = fieldInfo.FieldType; - this.contextType = fieldInfo.DeclaringType; - } - - public override object Get(object context) - { - return field.GetValue(context); - } - - public override void Set(object context, object value) - { - field.SetValue(context, value); - } - - public override bool IsWriteable - { - get { return isWriteable; } - } - - public override bool RequiresContext - { - get { return true; } - } - - public override Type TargetType - { - get { return targetType; } - } - - public override MemberInfo MemberInfo - { - get { return field.FieldInfo; } - } - - public override bool RequiresRefresh(Type contextType) - { - return this.contextType != contextType; - } - } - - #endregion - - #region EnumValueAccessor implementation - - private class EnumValueAccessor : BaseValueAccessor - { - private object enumValue; - - public EnumValueAccessor(object enumValue) - { - this.enumValue = enumValue; - } - - public override object Get(object context) - { - return enumValue; - } - - public override void Set(object context, object value) - { - throw new NotSupportedException("Cannot set the value of an enum."); - } - } - - #endregion - - #region TypeValueAccessor implementation - - private class TypeValueAccessor : BaseValueAccessor - { - private Type type; - - public TypeValueAccessor(Type type) - { - this.type = type; - } - - public override object Get(object context) - { - return type; - } - - public override void Set(object context, object value) - { - throw new NotSupportedException("Cannot set the value of a type."); - } - } - - #endregion - } + + // initialize this node if necessary + if (contextType != null && accessor == null) + { + // try to initialize node as enum value first + if (contextType.IsEnum) + { + try + { + accessor = new EnumValueAccessor(Enum.Parse(contextType, memberName, true)); + } + catch (ArgumentException) + { + // ArgumentException will be thrown if specified member name is not a valid + // enum value for the context type. We should just ignore it and continue processing, + // because the specified member could be a property of a Type class (i.e. EnumType.FullName) + } + } + + // then try to initialize node as property or field value + if (accessor == null) + { + // check the context type first + accessor = GetPropertyOrFieldAccessor(contextType, memberName, BINDING_FLAGS); + + // if not found, probe the Type type + if (accessor == null && context is Type) + { + accessor = GetPropertyOrFieldAccessor(typeof(Type), memberName, BINDING_FLAGS); + } + } + } + + // if there is still no match, try to initialize node as type accessor + if (accessor == null) + { + try + { + accessor = new TypeValueAccessor(TypeResolutionUtils.ResolveType(memberName)); + } + catch (TypeLoadException) + { + if (context == null) + { + throw new NullValueInNestedPathException("Cannot initialize property or field node '" + + memberName + + "' because the specified context is null."); + } + else + { + throw new InvalidPropertyException(contextType, memberName, + "'" + memberName + + "' node cannot be resolved for the specified context [" + + context + "]."); + } + } + } + } + } + + /// + /// Attempts to resolve property or field. + /// + /// + /// Type to search for a property or a field. + /// + /// + /// Property or field name. + /// + /// + /// Binding flags to use. + /// + /// + /// Resolved property or field accessor, or null + /// if specified cannot be resolved. + /// + private static IValueAccessor GetPropertyOrFieldAccessor(Type contextType, string memberName, BindingFlags bindingFlags) + { + try + { + PropertyInfo pi = contextType.GetProperty(memberName, bindingFlags); + if (pi == null) + { + FieldInfo fi = contextType.GetField(memberName, bindingFlags); + if (fi != null) + { + return new FieldValueAccessor(fi); + } + } + else + { + return new PropertyValueAccessor(pi); + } + } + catch (AmbiguousMatchException) + { + PropertyInfo pi = null; + + // search type hierarchy + while (contextType != typeof(object)) + { + pi = contextType.GetProperty(memberName, bindingFlags | BindingFlags.DeclaredOnly); + if (pi == null) + { + FieldInfo fi = contextType.GetField(memberName, bindingFlags | BindingFlags.DeclaredOnly); + if (fi != null) + { + return new FieldValueAccessor(fi); + } + } + else + { + return new PropertyValueAccessor(pi); + } + contextType = contextType.BaseType; + } + } + + return null; + } + + /// + /// Returns node's value for the given context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Node's value. + protected override object Get(object context, EvaluationContext evalContext) + { + lock (this) + { + InitializeNode(context); + + if (context == null && accessor.RequiresContext) + { + throw new NullValueInNestedPathException( + "Cannot retrieve the value of a field or property '" + this.memberName + + "', because context for its resolution is null."); + } + if (IsProperty || IsField) + { + return GetPropertyOrFieldValue(context, evalContext); + } + else + { + return accessor.Get(context); + } + } + } + + /// + /// Sets node's value for the given context. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// New value for this node. + protected override void Set(object context, EvaluationContext evalContext, object newValue) + { + lock (this) + { + InitializeNode(context); + + if (context == null && accessor.RequiresContext) + { + throw new NullValueInNestedPathException( + "Cannot set the value of a field or property '" + this.memberName + + "', because context for its resolution is null."); + } + if (IsProperty || IsField) + { + SetPropertyOrFieldValue(context, evalContext, newValue); + } + else + { + accessor.Set(context, newValue); + } + } + } + + /// + /// Gets a value indicating whether this node represents a property. + /// + /// + /// true if this node is a property; otherwise, false. + /// + private bool IsProperty + { + get { return accessor is PropertyValueAccessor; } + } + + /// + /// Gets a value indicating whether this node represents a field. + /// + /// + /// true if this node is a field; otherwise, false. + /// + private bool IsField + { + get { return accessor is FieldValueAccessor; } + } + + /// + /// Retrieves property or field value. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// Property or field value. + private object GetPropertyOrFieldValue(object context, EvaluationContext evalContext) + { + try + { + return accessor.Get(context); + } + catch (InvalidOperationException) + { + throw new NotReadablePropertyException(evalContext.RootContextType, this.memberName); + } + catch (TargetInvocationException e) + { + throw new InvalidPropertyException(evalContext.RootContextType, this.memberName, + "Getter for property '" + this.memberName + "' threw an exception.", + e); + } + catch (UnauthorizedAccessException e) + { + throw new InvalidPropertyException(evalContext.RootContextType, this.memberName, + "Illegal attempt to get value for the property '" + this.memberName + + "'.", e); + } + } + + /// + /// Sets property value, doing any type conversions that are necessary along the way. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// New value for this node. + private void SetPropertyOrFieldValue(object context, EvaluationContext evalContext, object newValue) + { + bool isWriteable = accessor.IsWriteable; + Type targetType = accessor.TargetType; + + try + { + if (!isWriteable) + { + if (!AddToCollections(context, evalContext, newValue)) + { + throw new NotWritablePropertyException( + "Can't change the value of the read-only property or field '" + this.memberName + "'."); + } + } + else if (targetType.IsPrimitive && (newValue == null || String.Empty.Equals(newValue))) + { + throw new ArgumentException("Invalid value [" + newValue + "] for property or field '" + + this.memberName + "' of primitive type [" + + targetType + "]"); + } + else if (newValue == null || ObjectUtils.IsAssignable(targetType, newValue)) // targetType.IsAssignableFrom(newValue.GetType()) + { + SetPropertyOrFieldValueInternal(context, newValue); + } + else if (!RemotingServices.IsTransparentProxy(newValue) && + (newValue is IList || newValue is IDictionary || newValue is ISet)) + { + if (!AddToCollections(context, evalContext, newValue)) + { + object tmpValue = + TypeConversionUtils.ConvertValueIfNecessary(targetType, newValue, this.memberName); + SetPropertyOrFieldValueInternal(context, tmpValue); + } + } + else + { + object tmpValue = TypeConversionUtils.ConvertValueIfNecessary(targetType, newValue, this.memberName); + SetPropertyOrFieldValueInternal(context, tmpValue); + } + } + catch (TargetInvocationException ex) + { + PropertyChangeEventArgs propertyChangeEvent = + new PropertyChangeEventArgs(this.memberName, null, newValue); + if (ex.GetBaseException() is InvalidCastException) + { + throw new TypeMismatchException(propertyChangeEvent, targetType, ex.GetBaseException()); + } + else + { + throw new MethodInvocationException(ex.GetBaseException(), propertyChangeEvent); + } + } + catch (UnauthorizedAccessException ex) + { + throw new FatalReflectionException("Illegal attempt to set property '" + this.memberName + "'", ex); + } + catch (NotWritablePropertyException) + { + throw; + } + catch (NotReadablePropertyException) + { + throw; + } + catch (ArgumentException ex) + { + PropertyChangeEventArgs propertyChangeEvent = + new PropertyChangeEventArgs(this.memberName, null, newValue); + throw new TypeMismatchException(propertyChangeEvent, targetType, ex); + } + } + + /// + /// Sets property or field value using either dynamic or standard reflection. + /// + /// Object to evaluate node against. + /// New value for this node, converted to appropriate type. + private void SetPropertyOrFieldValueInternal(object context, object newValue) + { + accessor.Set(context, newValue); + } + + /// + /// In the case of read only collections or custom collections that are not assignable from + /// IList, try to add to the collection. + /// + /// Context to evaluate expressions against. + /// Current expression evaluation context. + /// New value for this node. + /// true if was able add to IList, IDictionary, or ISet + private bool AddToCollections(object context, EvaluationContext evalContext, object newValue) + { + // short-circuit if accessor is not readable or if we have an array + if (!this.accessor.IsReadable || this.accessor.TargetType.IsArray) + { + return false; + } + + bool added = false; + + // try adding values if property is a list... + if (newValue is IList && !RemotingServices.IsTransparentProxy(newValue)) + { + IList currentValue = (IList)Get(context, evalContext); + if (currentValue != null && !currentValue.IsFixedSize && !currentValue.IsReadOnly) + { + foreach (object el in (IList)newValue) + { + currentValue.Add(el); + } + added = true; + } + } + // try adding values if property is a dictionary... + else if (newValue is IDictionary && !RemotingServices.IsTransparentProxy(newValue)) + { + IDictionary currentValue = (IDictionary)Get(context, evalContext); + if (currentValue != null && !currentValue.IsFixedSize && !currentValue.IsReadOnly) + { + foreach (DictionaryEntry entry in (IDictionary)newValue) + { + currentValue[entry.Key] = entry.Value; + } + added = true; + } + } + // try adding values if property is a set... + else if (newValue is ISet && !RemotingServices.IsTransparentProxy(newValue)) + { + ISet currentValue = (ISet)Get(context, evalContext); + if (currentValue != null) + { + currentValue.AddAll((ICollection)newValue); + added = true; + } + } + return added; + } + + /// + /// Utility method that is needed by ObjectWrapper and AbstractAutowireCapableObjectFactory. + /// We try as hard as we can, but there are instances when we won't be able to obtain PropertyInfo... + /// + /// Context to resolve property against. + /// PropertyInfo for this node. + internal MemberInfo GetMemberInfo(object context) + { + lock (this) + { + InitializeNode(context); + } + return accessor.MemberInfo; + + //if (IsProperty) + //{ + // return (((PropertyValueAccessor) accessor).MemberInfo); + //} + //else + //{ + // throw new FatalObjectException( + // "Cannot obtain PropertyInfo from an expression that does not resolve to a property."); + //} + } + + #region IValueAccessor interface + + private interface IValueAccessor + { + object Get(object context); + void Set(object context, object value); + + bool IsReadable { get; } + bool IsWriteable { get; } + bool RequiresContext { get; } + Type TargetType { get; } + MemberInfo MemberInfo { get; } + bool RequiresRefresh(Type contextType); + } + + #endregion + + #region BaseValueAccessor implementation + + private abstract class BaseValueAccessor : IValueAccessor + { + public abstract object Get(object context); + + public abstract void Set(object context, object value); + + public virtual bool IsReadable + { + get { return true; } + } + + public virtual bool IsWriteable + { + get { return false; } + } + + public virtual bool RequiresContext + { + get { return false; } + } + + public virtual Type TargetType + { + get { throw new NotSupportedException(); } + } + + public virtual MemberInfo MemberInfo + { + get { throw new NotSupportedException(); } + } + + public virtual bool RequiresRefresh(Type contextType) + { + return false; + } + } + + #endregion + + #region PropertyValueAccessor implementation + + private class PropertyValueAccessor : BaseValueAccessor + { + private SafeProperty property; + private string name; + private bool isReadable; + private bool isWriteable; + private Type targetType; + private Type contextType; + + public PropertyValueAccessor(PropertyInfo propertyInfo) + { + this.name = propertyInfo.Name; + this.isReadable = propertyInfo.CanRead; + this.isWriteable = propertyInfo.CanWrite; + this.targetType = propertyInfo.PropertyType; + this.contextType = propertyInfo.DeclaringType; + this.property = new SafeProperty(propertyInfo); + } + + public override object Get(object context) + { + if (!isReadable) + { + throw new NotReadablePropertyException("Cannot get a non-readable property [" + name + "]"); + } + return property.GetValue(context); + } + + public override void Set(object context, object value) + { + if (!isWriteable) + { + throw new NotWritablePropertyException("Cannot set a read-only property [" + name + "]"); + } + property.SetValue(context, value); + } + + public override bool IsReadable + { + get { return isReadable; } + } + + public override bool IsWriteable + { + get { return isWriteable; } + } + + public override bool RequiresContext + { + get { return true; } + } + + public override Type TargetType + { + get { return targetType; } + } + + public override MemberInfo MemberInfo + { + get { return property.PropertyInfo; } + } + + public override bool RequiresRefresh(Type contextType) + { + return this.contextType != contextType; + } + } + + #endregion + + #region FieldValueAccessor implementation + + private class FieldValueAccessor : BaseValueAccessor + { + private SafeField field; + private bool isWriteable; + private Type targetType; + private Type contextType; + + public FieldValueAccessor(FieldInfo fieldInfo) + { + this.field = new SafeField(fieldInfo); + this.isWriteable = !(fieldInfo.IsInitOnly || fieldInfo.IsLiteral); + this.targetType = fieldInfo.FieldType; + this.contextType = fieldInfo.DeclaringType; + } + + public override object Get(object context) + { + return field.GetValue(context); + } + + public override void Set(object context, object value) + { + field.SetValue(context, value); + } + + public override bool IsWriteable + { + get { return isWriteable; } + } + + public override bool RequiresContext + { + get { return true; } + } + + public override Type TargetType + { + get { return targetType; } + } + + public override MemberInfo MemberInfo + { + get { return field.FieldInfo; } + } + + public override bool RequiresRefresh(Type contextType) + { + return this.contextType != contextType; + } + } + + #endregion + + #region EnumValueAccessor implementation + + private class EnumValueAccessor : BaseValueAccessor + { + private object enumValue; + + public EnumValueAccessor(object enumValue) + { + this.enumValue = enumValue; + } + + public override object Get(object context) + { + return enumValue; + } + + public override void Set(object context, object value) + { + throw new NotSupportedException("Cannot set the value of an enum."); + } + } + + #endregion + + #region TypeValueAccessor implementation + + private class TypeValueAccessor : BaseValueAccessor + { + private Type type; + + public TypeValueAccessor(Type type) + { + this.type = type; + } + + public override object Get(object context) + { + return type; + } + + public override void Set(object context, object value) + { + throw new NotSupportedException("Cannot set the value of a type."); + } + } + + #endregion + } } \ No newline at end of file