using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ZeroLevel.DependencyInjection
{
///
/// Constructor metadata
///
internal class ConstructorMetadata
{
public ConstructorInfo Constructor { get; }
private IReadOnlyList Parameters { get; }
private readonly IContainer _parent;
public ConstructorMetadata(Container parent, ConstructorInfo info)
{
_parent = parent;
Constructor = info;
Parameters = info.
GetParameters().
Select(p =>
{
var parameterAttribute = p.GetCustomAttribute();
var resolveAttribute = p.GetCustomAttribute();
var kind = (parameterAttribute != null!) ? ConstructorParameterKind.Parameter :
(resolveAttribute != null!) ? ConstructorParameterKind.Dependency : ConstructorParameterKind.None;
return new ConstructorParameter
{
Type = p.ParameterType,
ParameterKind = kind,
ParameterResolveName = ((kind == ConstructorParameterKind.Parameter) ? parameterAttribute?.Name ?? p.Name :
(kind == ConstructorParameterKind.Dependency) ? resolveAttribute?.ResolveName : null)!,
ParameterResolveType = (kind == ConstructorParameterKind.Parameter) ? parameterAttribute?.Type ?? p.ParameterType :
(kind == ConstructorParameterKind.Dependency) ? resolveAttribute?.ContractType ?? p.ParameterType : null!,
IsNullable = IsNullable(p.ParameterType)
};
}).ToList();
}
private static bool IsNullable(Type type)
{
if (!type.IsValueType) return true; // ref-type
if (Nullable.GetUnderlyingType(type) != null!) return true; // Nullable
return false; // value-type
}
///
/// Determining whether the constructor is suitable for the specified arguments
///
/// Arguments
/// Prepared arguments for constructor call
/// true - if the constructor can be called with the arguments passed
public bool IsMatch(object[] args, out object[] parameters)
{
parameters = null!;
int arg_index = 0;
if (Parameters.Count > 0)
{
parameters = new object[Parameters.Count];
for (int i = 0; i < parameters.Length; i++)
{
switch (Parameters[i].ParameterKind)
{
case ConstructorParameterKind.Parameter:
parameters[i] = _parent.Get(Parameters[i].ParameterResolveType, Parameters[i].ParameterResolveName);
break;
case ConstructorParameterKind.Dependency:
parameters[i] = _parent.Resolve(Parameters[i].ParameterResolveType, Parameters[i].ParameterResolveName);
break;
default:
if (args == null || arg_index >= args.Length) return false;
if (null == args[arg_index])
{
if (Parameters[i].IsNullable)
{
parameters[i] = args[arg_index];
}
else
{
return false;
}
}
else if (Parameters[i].Type.IsAssignableFrom(args[arg_index].GetType()))
{
parameters[i] = args[arg_index];
}
else
{
try
{
parameters[i] = Convert.ChangeType(args[i], Parameters[i].Type);
}
catch
{
return false;
}
}
arg_index++;
break;
}
}
return true;
}
if (args != null && args.Length > 0)
return false;
return true;
}
}
}