using QFSW.QC.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using QFSW.QC.Internal;
namespace QFSW.QC
{
///
/// Contains the full data about a command and provides an execution point for invoking the command.
///
public class CommandData
{
public readonly string CommandName;
public readonly string CommandDescription;
public readonly string CommandSignature;
public readonly string ParameterSignature;
public readonly string GenericSignature;
public readonly ParameterInfo[] MethodParamData;
public readonly Type[] ParamTypes;
public readonly Type[] GenericParamTypes;
public readonly MethodInfo MethodData;
public readonly MonoTargetType MonoTarget;
private readonly object[] _defaultParameters;
public bool IsGeneric => GenericParamTypes.Length > 0;
public bool IsStatic => MethodData.IsStatic;
public bool HasDescription => !string.IsNullOrWhiteSpace(CommandDescription);
public int ParamCount => ParamTypes.Length - _defaultParameters.Length;
public Type[] MakeGenericArguments(params Type[] genericTypeArguments)
{
if (genericTypeArguments.Length != GenericParamTypes.Length)
{
throw new ArgumentException("Incorrect number of generic substitution types were supplied.");
}
Dictionary substitutionTable = new Dictionary();
for (int i = 0; i < genericTypeArguments.Length; i++)
{
substitutionTable.Add(GenericParamTypes[i].Name, genericTypeArguments[i]);
}
Type[] types = new Type[ParamTypes.Length];
for (int i = 0; i < types.Length; i++)
{
if (ParamTypes[i].ContainsGenericParameters)
{
Type substitution = ConstructGenericType(ParamTypes[i], substitutionTable);
types[i] = substitution;
}
else
{
types[i] = ParamTypes[i];
}
}
return types;
}
private Type ConstructGenericType(Type genericType, Dictionary substitutionTable)
{
if (!genericType.ContainsGenericParameters) { return genericType; }
if (substitutionTable.ContainsKey(genericType.Name)) { return substitutionTable[genericType.Name]; }
if (genericType.IsArray) { return ConstructGenericType(genericType.GetElementType(), substitutionTable).MakeArrayType(); }
if (genericType.IsGenericType)
{
Type baseType = genericType.GetGenericTypeDefinition();
Type[] typeArguments = genericType.GetGenericArguments();
for (int i = 0; i < typeArguments.Length; i++)
{
typeArguments[i] = ConstructGenericType(typeArguments[i], substitutionTable);
}
return baseType.MakeGenericType(typeArguments);
}
throw new ArgumentException($"Could not construct the generic type {genericType}");
}
public object Invoke(object[] paramData, Type[] genericTypeArguments)
{
// For MonoTargetType.Argument, need to use the first argument as the invocation target
// and then forward the rest as normal
int paramDataStart = 0;
int paramDataLength = paramData.Length;
if (MonoTarget == MonoTargetType.Argument || MonoTarget == MonoTargetType.ArgumentMulti)
{
paramDataStart++;
paramDataLength--;
}
int numArguments = paramDataLength + _defaultParameters.Length;
object[] arguments = new object[numArguments];
// Copy supplied argument data and default arguments to create final argument set to forward to method
Array.Copy(paramData, paramDataStart, arguments, 0, paramDataLength);
Array.Copy(_defaultParameters, 0, arguments, paramDataLength, _defaultParameters.Length);
MethodInfo invokingMethod = GetInvokingMethod(genericTypeArguments);
if (IsStatic)
{
return invokingMethod.Invoke(null, arguments);
}
// For MonoTargetType.Argument, use the first argument as the target
// Otherwise, get invocation targets like normal
IEnumerable