#region License
/**
* Ingenious MVC : An MVC framework for .NET 2.0
* Copyright (C) 2006, JDP Group
*
* This library 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 2.1 of the License, or (at your option) any later version.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: - Kent Boogaart (kentcb@internode.on.net)
*/
#endregion
using System;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using Ingenious.Mvc.Configuration;
using Ingenious.Mvc.Util;
namespace Ingenious.Mvc.Factories{
/// <include file='DefaultFactoryBase.doc.xml' path='/doc/member[@name="T:DefaultFactoryBase"]/*'/>
[Serializable]
public abstract class DefaultFactoryBase
{
private const BindingFlags _propertyBindingFlags = BindingFlags.Instance | BindingFlags.Public;
private const BindingFlags _constructorBindingFlags = BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public;
/// <include file='DefaultFactoryBase.doc.xml' path='/doc/member[@name="P:Name"]/*'/>
public abstract string Name
{
get;
}
/// <include file='DefaultFactoryBase.doc.xml' path='/doc/member[@name="M:CreateInstance(System.Type,System.Object,System.Type[],System.Object[],System.String[],System.Type[])"]/*'/>
protected object CreateInstance(Type type, object customConfiguration, Type[] argTypes, object[] args, string[] argNames, Type[] requiredInterfaces)
{
ArgumentHelper.AssertNotNull(type, "type");
if (argTypes == null)
{
//default to zero arguments
argTypes = Type.EmptyTypes;
}
else
{
//all supplied argument types must be non-null
foreach (object argType in argTypes)
{
ExceptionHelper.ThrowIf(argType == null, "CreateInstance.nullArgumentType", "argTypes");
}
}
if (argNames == null)
{
//default to zero argument names
argNames = new string[0];
}
else
{
//all supplied argument names must be non-null
foreach (object argName in argNames)
{
ExceptionHelper.ThrowIf(argName == null, "CreateInstance.nullArgumentName", "argNames");
}
}
if (args == null)
{
//default to zero arguments
args = new object[0];
}
//make sure the same number of argument types, argument values and argument names were supplied
ExceptionHelper.ThrowIf(argTypes.Length != args.Length, "CreateInstance.argumentTypesAndValuesMismatch", argTypes.Length, args.Length);
ExceptionHelper.ThrowIf(args.Length != argNames.Length, "CreateInstance.argumentAndNamesMismatch", args.Length, argNames.Length);
//make sure all interfaces are implemented by the type
if (requiredInterfaces != null)
{
foreach (Type requiredInterface in requiredInterfaces)
{
//all required interfaces must be non-null
ExceptionHelper.ThrowIf(requiredInterface == null, "CreateInstance.nullInterface", "requiredInterfaces");
ExceptionHelper.ThrowIf(!requiredInterface.IsAssignableFrom(type), "CreateInstance.interfaceNotImplemented", type.FullName, requiredInterface.FullName, Name);
}
}
//make sure the type implements ICustomConfigurable if we need to pass in custom configuration
if ((customConfiguration != null) && (!typeof(ICustomConfigurable).IsAssignableFrom(type)))
{
ExceptionHelper.Throw("CreateInstance.notConfigurable", type.FullName, typeof(ICustomConfigurable).FullName, Name);
}
object retVal = null;
Binder binder = Type.DefaultBinder;
object state;
//first we'll look for a constructor that takes the arguments in question
ConstructorInfo constructor = null;
//this is set to true if all properties are successfully passed into an appropriate constructor
bool propertiesPassedInConstructor = false;
if (type.GetConstructors(_constructorBindingFlags).Length > 0)
{
try
{
constructor = (ConstructorInfo) binder.BindToMethod(_constructorBindingFlags, type.GetConstructors(_constructorBindingFlags), ref args, null, null, null, out state);
propertiesPassedInConstructor = true;
}
catch (MissingMethodException)
{
//swallow
}
}
//no constructor was found that takes the necessary arguments - look for a default constructor
if (constructor == null)
{
constructor = type.GetConstructor(new Type[0]);
}
ExceptionHelper.ThrowIf(constructor == null, "CreateInstance.noAppropriateConstructor", type.FullName, Name, GetArgList(argTypes));
//now try and create an instance of the type via the constructor we found
try
{
retVal = constructor.Invoke(propertiesPassedInConstructor ? args : new object[0]);
}
catch (Exception e)
{
ExceptionHelper.Throw("CreateInstance.constructorInvokeFailed", e, type.FullName, Name);
}
//set properties if we couldn't find a constructor to do it for us
if (!propertiesPassedInConstructor)
{
for (int i = 0; i < args.Length; ++i)
{
object arg = args[i];
string argName = argNames[i];
//try the obvious match first
PropertyInfo property = type.GetProperty(argName, argTypes[i]);
if (property == null)
{
try
{
property = binder.SelectProperty(_propertyBindingFlags, type.GetProperties(_propertyBindingFlags), arg.GetType(), Type.EmptyTypes, null);
}
catch (Exception e)
{
ExceptionHelper.Throw("CreateInstance.problemFindingProperty", e, arg.GetType().FullName, type.FullName);
}
}
ExceptionHelper.ThrowIf(property == null, "CreateInstance.noAppropriateProperty", type.FullName, GetArgList(argTypes), GetPropertySignature(argTypes[i]), Name);
ExceptionHelper.ThrowIf(!property.CanWrite, "CreateInstance.propertyNotWritable", property.Name, type.FullName);
//set the property
property.SetValue(retVal, arg, null);
}
}
//now pass on any custom configuration information
if (customConfiguration != null)
{
((ICustomConfigurable) retVal).SetCustomConfiguration(customConfiguration);
}
return retVal;
}
private static string GetArgList(Type[] argTypes)
{
StringBuilder retVal = new StringBuilder();
retVal.Append("(");
foreach (Type argType in argTypes)
{
retVal.Append(argType).Append(", ");
}
//remove last comma and space after it
if (argTypes.Length > 0)
{
retVal.Remove(retVal.Length - 2, 2);
}
retVal.Append(")");
return retVal.ToString();
}
private static string GetPropertySignature(Type type)
{
return string.Concat("public ", type.Name, " PropertyName { get; set; }");
}
}
}
|