#region License
/*
* Copyright 2002-2006 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 System.Web.UI;
using Spring.Context;
using Spring.Proxy;
using Spring.Util;
using Spring.Web.Support;
#if NET_2_0
using System.Collections.Generic;
#else
using Spring.Reflection.Dynamic;
#endif
#endregion
namespace Spring.Web.Support{
/// <summary>
/// This strategy enhances a ControlCollection's type by
/// dynamically implementing ISupportsWebDependencyInjection on this type
/// </summary>
/// <author>Erich Eichinger</author>
internal class InterceptControlCollectionStrategy : IInterceptionStrategy
{
/// <summary>
/// Holds a reference to the static(!) callback-method to be used during generation of intercepted ControlCollection-Types
/// </summary>
private delegate Control InjectDependenciesCallbackHandler(IApplicationContext defaultApplicationContext, Control control);
/// <summary>
/// The list of methods to be intercepted for a ControlCollection
/// </summary>
private static readonly MethodInfo[] s_collectionMethods = new MethodInfo[]
{
typeof(ControlCollection).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public)
, typeof (ControlCollection).GetMethod("AddAt", BindingFlags.Instance | BindingFlags.Public)
};
/// <summary>
/// Holds a table of already known intercepted ControlCollection types
/// </summary>
private static readonly Hashtable s_interceptedCollectionTypeCache = new Hashtable();
/// <summary>
/// Intercepts the given <see cref="ControlCollection"/> by dynamically deriving
/// the original type and let it implement <see cref="ISupportsWebDependencyInjection"/>.
/// </summary>
/// <param name="defaultApplicationContext">the ApplicationContext to be set on the collection instance.</param>
/// <param name="ctlAccessor">a wrapper around the owner control instance.</param>
/// <param name="ctlColAccessor">a wrapper around the collection instance.</param>
/// <returns><value>true</value>, if interception was successful. <value>false</value> otherwise</returns>
public bool Intercept(IApplicationContext defaultApplicationContext,
ControlAccessor ctlAccessor, ControlCollectionAccessor ctlColAccessor)
{
Type collectionType = ctlColAccessor.GetTargetType();
if (collectionType.IsSealed ||
!ReflectionUtils.IsTypeVisible(collectionType, DynamicProxyManager.ASSEMBLY_NAME))
// || (null == collectionType.GetConstructor(new Type[] {typeof (Control)}))
{
return false;
}
// this will enhance the collection's type and create a new instance of this type with fields copied from original collection
try
{
ControlCollection childControls = InterceptCollection(ctlAccessor.GetTarget(), ctlColAccessor.GetTarget() );
((ISupportsWebDependencyInjection)childControls).DefaultApplicationContext = defaultApplicationContext;
ctlAccessor.Controls = childControls;
}
catch
{
// this may happen, if the ControlCollection doesn't contain a standard-ctor ControlCollection( Control owner)
return false;
}
return true;
}
private static ControlCollection InterceptCollection(Control owner, ControlCollection originalCollection)
{
CreateControlCollectionDelegate factoryMethod = GetInterceptedCollectionFactory(owner.GetType(), originalCollection.GetType());
ControlCollection interceptedCollection = factoryMethod(owner);
ReflectionUtils.MemberwiseCopy(originalCollection, interceptedCollection);
return interceptedCollection;
}
internal static ControlCollection TryCreateCollection(Control owner)
{
CreateControlCollectionDelegate factoryMethod = (CreateControlCollectionDelegate)s_collectionFactoryCache[owner.GetType()];
if (factoryMethod != null)
{
return factoryMethod(owner);
}
return null;
}
private delegate ControlCollection CreateControlCollectionDelegate(Control owner);
private static readonly Hashtable s_collectionFactoryCache = new Hashtable();
#if NET_2_0
private static CreateControlCollectionDelegate GetInterceptedCollectionFactory(Type ownerType, Type collectionType)
{
AssertUtils.State( typeof(Control).IsAssignableFrom(ownerType), "ownerType must be of type Control" );
AssertUtils.State( typeof(ControlCollection).IsAssignableFrom(collectionType), "collectionType must be of type ControlCollection" );
CreateControlCollectionDelegate factoryMethod = (CreateControlCollectionDelegate)s_collectionFactoryCache[ownerType];
if (factoryMethod == null)
{
lock (s_collectionFactoryCache)
{
factoryMethod = (CreateControlCollectionDelegate)s_collectionFactoryCache[ownerType];
if (factoryMethod == null)
{
Type interceptedCollectionType = GetInterceptedCollectionType(
collectionType
, WebDependencyInjectionUtils.InjectDependenciesRecursive
);
ConstructorInfo ctor = interceptedCollectionType.GetConstructor(new Type[] { typeof(Control) });
DynamicMethod dm = new DynamicMethod(string.Empty, typeof(ControlCollection), new Type[] { typeof(Control) });
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Ret);
factoryMethod = (CreateControlCollectionDelegate)dm.CreateDelegate(typeof(CreateControlCollectionDelegate));
s_collectionFactoryCache[ownerType] = factoryMethod;
}
}
}
return factoryMethod;
}
#else
private class CreateControlCollectionWrapper
{
private IDynamicConstructor _ctor;
public CreateControlCollectionWrapper(IDynamicConstructor ctor)
{
_ctor = ctor;
}
public ControlCollection Create(Control owner)
{
return (ControlCollection) _ctor.Invoke(new object[] {owner});
}
}
private static CreateControlCollectionDelegate GetInterceptedCollectionFactory(Type controlType, Type controlCollectionType)
{
AssertUtils.State( typeof(Control).IsAssignableFrom(controlType), "controlType must be of type Control" );
AssertUtils.State( typeof(ControlCollection).IsAssignableFrom(controlCollectionType), "controlCollectionType must be of type ControlCollection" );
CreateControlCollectionDelegate factoryMethod = (CreateControlCollectionDelegate)s_collectionFactoryCache[controlType];
if (factoryMethod == null)
{
lock (s_collectionFactoryCache)
{
factoryMethod = (CreateControlCollectionDelegate)s_collectionFactoryCache[controlType];
if (factoryMethod == null)
{
Type interceptedCollectionType = GetInterceptedCollectionType(controlCollectionType, new InjectDependenciesCallbackHandler(WebDependencyInjectionUtils.InjectDependenciesRecursive));
ConstructorInfo ctor = interceptedCollectionType.GetConstructor(new Type[] {typeof (Control)});
IDynamicConstructor dynCtor = new SafeConstructor(ctor);
s_collectionFactoryCache[controlType] = new CreateControlCollectionDelegate(new CreateControlCollectionWrapper(dynCtor).Create);
}
}
}
return factoryMethod;
}
#endif
private static Type GetInterceptedCollectionType(Type controlCollectionType, InjectDependenciesCallbackHandler staticCallback)
{
AssertUtils.State( typeof(ControlCollection).IsAssignableFrom(controlCollectionType), "controlCollectionType must be of type ControlCollection" );
Type interceptedCollectionType = (Type)s_interceptedCollectionTypeCache[controlCollectionType];
if (interceptedCollectionType == null)
{
lock (s_interceptedCollectionTypeCache)
{
MethodInfo callbackMethod = staticCallback.Method;
AssertUtils.State(callbackMethod.IsStatic && callbackMethod.IsPublic, "staticCallback must be a public static method");
interceptedCollectionType = (Type)s_interceptedCollectionTypeCache[controlCollectionType];
if (interceptedCollectionType == null)
{
SupportsWebDependencyInjectionTypeBuilder builder = new SupportsWebDependencyInjectionTypeBuilder(controlCollectionType, s_collectionMethods, callbackMethod);
interceptedCollectionType = builder.BuildProxyType();
s_interceptedCollectionTypeCache[controlCollectionType] = interceptedCollectionType;
}
}
}
return interceptedCollectionType;
}
}
}
|