#region License
/*
* Copyright 2002-2007 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.Reflection.Emit;
using Spring.Reflection.Dynamic;
using Spring.Util;
#endregion
namespace Spring.Reflection.Dynamic{
#region IDynamicIndexer interface
/// <summary>
/// Defines methods that dynamic indexer class has to implement.
/// </summary>
public interface IDynamicIndexer
{
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get the indexer value from.
/// </param>
/// <param name="index">
/// Indexer argument.
/// </param>
/// <returns>
/// A indexer value.
/// </returns>
object GetValue( object target, int index );
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get the indexer value from.
/// </param>
/// <param name="index">
/// Indexer argument.
/// </param>
/// <returns>
/// A indexer value.
/// </returns>
object GetValue( object target, object index );
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get the indexer value from.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <returns>
/// A indexer value.
/// </returns>
object GetValue( object target, object[] index );
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set the indexer value on.
/// </param>
/// <param name="index">
/// Indexer argument.
/// </param>
/// <param name="value">
/// A new indexer value.
/// </param>
void SetValue( object target, int index, object value );
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set the indexer value on.
/// </param>
/// <param name="index">
/// Indexer argument.
/// </param>
/// <param name="value">
/// A new indexer value.
/// </param>
void SetValue( object target, object index, object value );
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set the indexer value on.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <param name="value">
/// A new indexer value.
/// </param>
void SetValue( object target, object[] index, object value );
}
#endregion
#region Safe wrapper
#if NET_2_0
/// <summary>
/// Safe wrapper for the dynamic indexer.
/// </summary>
/// <remarks>
/// <see cref="SafeIndexer"/> will attempt to use dynamic
/// indexer if possible, but it will fall back to standard
/// reflection if necessary.
/// </remarks>
[Obsolete("Use SafeProperty instead", false)]
public class SafeIndexer : IDynamicIndexer
{
private PropertyInfo indexerProperty;
/// <summary>
/// Internal PropertyInfo accessor.
/// </summary>
internal PropertyInfo IndexerProperty
{
get { return indexerProperty; }
}
private SafeProperty property;
/// <summary>
/// Creates a new instance of the safe indexer wrapper.
/// </summary>
/// <param name="indexerInfo">Indexer to wrap.</param>
public SafeIndexer( PropertyInfo indexerInfo )
{
AssertUtils.ArgumentNotNull( indexerInfo, "You cannot create a dynamic indexer for a null value." );
this.indexerProperty = indexerInfo;
this.property = new SafeProperty( indexerInfo );
}
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get indexer value from.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <returns>
/// A indexer value.
/// </returns>
public object GetValue( object target, int index )
{
return property.GetValue( target, index );
}
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get the indexer value from.
/// </param>
/// <param name="index">
/// Indexer argument.
/// </param>
/// <returns>
/// A indexer value.
/// </returns>
public object GetValue( object target, object index )
{
return property.GetValue( target, index );
}
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get indexer value from.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <returns>
/// A indexer value.
/// </returns>
public object GetValue( object target, object[] index )
{
return property.GetValue( target, index );
}
/// <summary>
/// Sets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set indexer value on.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <param name="value">
/// A new indexer value.
/// </param>
public void SetValue( object target, int index, object value )
{
property.SetValue( target, value, index );
}
/// <summary>
/// Sets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set indexer value on.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <param name="value">
/// A new indexer value.
/// </param>
public void SetValue( object target, object index, object value )
{
property.SetValue( target, value, index );
}
/// <summary>
/// Sets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set indexer value on.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <param name="value">
/// A new indexer value.
/// </param>
public void SetValue( object target, object[] index, object value )
{
property.SetValue( target, value, index );
}
}
#else
/// <summary>
/// Safe wrapper for the dynamic indexer.
/// </summary>
/// <remarks>
/// <see cref="SafeIndexer"/> will attempt to use dynamic
/// indexer if possible, but it will fall back to standard
/// reflection if necessary.
/// </remarks>
public class SafeIndexer : IDynamicIndexer
{
private PropertyInfo indexerProperty;
/// <summary>
/// Internal PropertyInfo accessor.
/// </summary>
internal PropertyInfo IndexerProperty
{
get { return indexerProperty; }
}
private IDynamicIndexer dynamicIndexer;
private bool isOptimizedGet = false;
private bool isOptimizedSet = false;
/// <summary>
/// Creates a new instance of the safe indexer wrapper.
/// </summary>
/// <param name="indexer">Indexer to wrap.</param>
public SafeIndexer(PropertyInfo indexer)
{
this.indexerProperty = indexer;
if (indexer.CanRead
&& indexer.GetGetMethod() != null
&& ReflectionUtils.IsTypeVisible(indexer.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME))
{
dynamicIndexer = DynamicIndexer.Create(indexer);
isOptimizedGet = true;
}
if (indexer.CanWrite
&& indexer.GetSetMethod() != null
&& ReflectionUtils.IsTypeVisible(indexer.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME))
{
if (dynamicIndexer == null)
{
dynamicIndexer = DynamicIndexer.Create(indexer);
}
isOptimizedSet = true;
}
}
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get indexer value from.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <returns>
/// A indexer value.
/// </returns>
public object GetValue(object target, int index)
{
if (isOptimizedGet)
{
return dynamicIndexer.GetValue(target, index);
}
else
{
return indexerProperty.GetValue(target, new object[] { index });
}
}
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get the indexer value from.
/// </param>
/// <param name="index">
/// Indexer argument.
/// </param>
/// <returns>
/// A indexer value.
/// </returns>
public object GetValue(object target, object index)
{
if (isOptimizedGet)
{
return dynamicIndexer.GetValue(target, index);
}
else
{
return indexerProperty.GetValue(target, new object[] { index });
}
}
/// <summary>
/// Gets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get indexer value from.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <returns>
/// A indexer value.
/// </returns>
public object GetValue(object target, object[] index)
{
if (isOptimizedGet)
{
return dynamicIndexer.GetValue(target, index);
}
else
{
return indexerProperty.GetValue(target, index);
}
}
/// <summary>
/// Sets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set indexer value on.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <param name="value">
/// A new indexer value.
/// </param>
public void SetValue(object target, int index, object value)
{
if (isOptimizedSet)
{
try
{
dynamicIndexer.SetValue(target, index, value);
}
catch (InvalidCastException)
{
isOptimizedSet = false;
indexerProperty.SetValue(target, value, new object[] { index });
}
}
else
{
indexerProperty.SetValue(target, value, new object[] { index });
}
}
/// <summary>
/// Sets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set indexer value on.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <param name="value">
/// A new indexer value.
/// </param>
public void SetValue(object target, object index, object value)
{
if (isOptimizedSet)
{
try
{
dynamicIndexer.SetValue(target, index, value);
}
catch (InvalidCastException)
{
isOptimizedSet = false;
indexerProperty.SetValue(target, value, new object[] { index });
}
}
else
{
indexerProperty.SetValue(target, value, new object[] { index });
}
}
/// <summary>
/// Sets the value of the dynamic indexer for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set indexer value on.
/// </param>
/// <param name="index">
/// Indexer arguments.
/// </param>
/// <param name="value">
/// A new indexer value.
/// </param>
public void SetValue(object target, object[] index, object value)
{
if (isOptimizedSet)
{
try
{
dynamicIndexer.SetValue(target, index, value);
}
catch (InvalidCastException)
{
isOptimizedSet = false;
indexerProperty.SetValue(target, value, index);
}
}
else
{
indexerProperty.SetValue(target, value, index);
}
}
}
#endif
#endregion
#if NET_2_0
/// <summary>
/// Factory class for dynamic indexers.
/// </summary>
/// <author>Aleksandar Seovic</author>
[Obsolete( "Use DynamicProperty instead", false )]
public sealed class DynamicIndexer : BaseDynamicMember
{
/// <summary>
/// Prevent instantiation
/// </summary>
private DynamicIndexer() { }
/// <summary>
/// Creates dynamic indexer instance for the specified <see cref="PropertyInfo"/>.
/// </summary>
/// <param name="indexer">Indexer info to create dynamic indexer for.</param>
/// <returns>Dynamic indexer for the specified <see cref="PropertyInfo"/>.</returns>
public static IDynamicIndexer Create( PropertyInfo indexer )
{
AssertUtils.ArgumentNotNull( indexer, "You cannot create a dynamic indexer for a null value." );
IDynamicIndexer dynamicIndexer = new SafeIndexer( indexer );
return dynamicIndexer;
}
}
#else
/// <summary>
/// Factory class for dynamic indexers.
/// </summary>
/// <author>Aleksandar Seovic</author>
public class DynamicIndexer : BaseDynamicMember
{
private static readonly CreateIndexerCallback s_createCallback = new CreateIndexerCallback(CreateInternal);
#region Create Method
/// <summary>
/// Creates dynamic indexer instance for the specified <see cref="PropertyInfo"/>.
/// </summary>
/// <param name="indexer">Indexer info to create dynamic indexer for.</param>
/// <returns>Dynamic indexer for the specified <see cref="PropertyInfo"/>.</returns>
public static IDynamicIndexer Create(PropertyInfo indexer)
{
AssertUtils.ArgumentNotNull(indexer, "You cannot create a dynamic indexer for a null value.");
IDynamicIndexer dynamicIndexer = DynamicReflectionManager.GetDynamicIndexer(indexer, s_createCallback);
return dynamicIndexer;
}
private static IDynamicIndexer CreateInternal(PropertyInfo indexer)
{
IDynamicIndexer dynamicIndexer = null;
TypeBuilder tb = DynamicReflectionManager.CreateTypeBuilder("Indexer_" + indexer.DeclaringType.Name);
tb.AddInterfaceImplementation(typeof(IDynamicIndexer));
GenerateGetValue(tb, indexer, typeof(int));
GenerateGetValue(tb, indexer, typeof(object));
GenerateGetValue(tb, indexer, typeof(object[]));
GenerateSetValue(tb, indexer, typeof(int));
GenerateSetValue(tb, indexer, typeof(object));
GenerateSetValue(tb, indexer, typeof(object[]));
Type dynamicIndexerType = tb.CreateType();
ConstructorInfo ctor = dynamicIndexerType.GetConstructor(Type.EmptyTypes);
dynamicIndexer = (IDynamicIndexer) ctor.Invoke(ObjectUtils.EmptyObjects);
return dynamicIndexer;
}
private static void GenerateGetValue(TypeBuilder tb, PropertyInfo indexer, Type indexType)
{
MethodBuilder getValueMethod =
tb.DefineMethod("GetValue", METHOD_ATTRIBUTES, typeof(object), new Type[] { typeof(object), indexType });
getValueMethod.DefineParameter(1, ParameterAttributes.None, "target");
getValueMethod.DefineParameter(2, ParameterAttributes.None, "index");
ILGenerator il = getValueMethod.GetILGenerator();
if (indexer.CanRead)
{
MethodInfo getMethod = indexer.GetGetMethod();
bool isValueType = indexer.DeclaringType.IsValueType;
bool isStatic = getMethod.IsStatic;
if (!isStatic)
{
SetupTargetInstance(il, indexer.DeclaringType);
}
if (indexType != typeof(object) && indexType != typeof(object[]))
{
il.Emit(OpCodes.Ldarg_2);
}
else if (indexType == typeof(object))
{
Type argumentType = ReflectionUtils.GetParameterTypes(getMethod)[0];
SetupIndexerArgument(il, argumentType, 0, false);
}
else
{
Type[] argTypes = ReflectionUtils.GetParameterTypes(getMethod);
for (int i = 0; i < argTypes.Length; i++)
{
SetupIndexerArgument(il, argTypes[i], i, true);
}
}
InvokeMethod(il, isStatic, isValueType, getMethod);
ProcessReturnValue(il, indexer.PropertyType);
il.Emit(OpCodes.Ret);
}
else
{
ThrowInvalidOperationException(il, "Cannot get value of a non-readable indexer");
}
}
private static void GenerateSetValue(TypeBuilder tb, PropertyInfo indexer, Type indexType)
{
MethodBuilder setValueMethod =
tb.DefineMethod("SetValue", METHOD_ATTRIBUTES, typeof(void),
new Type[] { typeof(object), indexType, typeof(object) });
setValueMethod.DefineParameter(1, ParameterAttributes.None, "target");
setValueMethod.DefineParameter(2, ParameterAttributes.None, "value");
ILGenerator il = setValueMethod.GetILGenerator();
if (indexer.CanWrite)
{
bool isValueType = indexer.DeclaringType.IsValueType;
if (isValueType)
{
ThrowInvalidOperationException(il, "Cannot set indexer value on a value type due to boxing.");
}
else
{
MethodInfo setMethod = indexer.GetSetMethod();
bool isStatic = setMethod.IsStatic;
if (!isStatic)
{
SetupTargetInstance(il, indexer.DeclaringType);
}
if (indexType != typeof(object) && indexType != typeof(object[]))
{
il.Emit(OpCodes.Ldarg_2);
}
else if (indexType == typeof(object))
{
Type argumentType = ReflectionUtils.GetParameterTypes(setMethod)[0];
SetupIndexerArgument(il, argumentType, 0, false);
}
else
{
Type[] argTypes = ReflectionUtils.GetParameterTypes(setMethod);
for (int i = 0; i < argTypes.Length - 1; i++)
{
SetupIndexerArgument(il, argTypes[i], i, true);
}
}
SetupArgument(il, indexer.PropertyType, 3);
InvokeMethod(il, isStatic, isValueType, setMethod);
il.Emit(OpCodes.Ret);
}
}
else
{
ThrowInvalidOperationException(il, "Cannot set value of a read-only indexer");
}
}
private static void SetupIndexerArgument(ILGenerator il, Type argumentType, int argumentPosition, bool isObjectArray)
{
il.Emit(OpCodes.Ldarg_2);
if (isObjectArray)
{
il.Emit(OpCodes.Ldc_I4, argumentPosition);
il.Emit(OpCodes.Ldelem_Ref);
}
if (argumentType.IsValueType)
{
il.Emit(OpCodes.Unbox, argumentType);
il.Emit(OpCodes.Ldobj, argumentType);
}
else
{
il.Emit(OpCodes.Castclass, argumentType);
}
}
#endregion
}
#endif
} // namespace
|