internal static LambdaExpression NullableConversion( ParameterConversionConfiguration config, Type type, ExcelParameterRegistration paramReg, bool treatEmptyAsMissing, bool treatNAErrorAsMissing) { // Decide whether to return a conversion function for this parameter if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(Nullable <>)) { return(null); } var innerType = type.GetGenericArguments()[0]; // E.g. innerType is Complex LambdaExpression innerTypeConversion = ParameterConversionRegistration.GetParameterConversion(config, innerType, paramReg) ?? TypeConversion.GetConversion(typeof(object), innerType); ParameterExpression input = innerTypeConversion.Parameters[0]; // Here's the actual conversion function var result = Expression.Lambda( Expression.Condition( // if the value is missing (or possibly empty) MissingTest(input, treatEmptyAsMissing, treatNAErrorAsMissing), // cast null to int? Expression.Constant(null, type), // else convert to int, and cast that to int? Expression.Convert(Expression.Invoke(innerTypeConversion, input), type)), input); return(result); }
// Should return null if there are no conversions to apply internal static List <LambdaExpression> GetParameterConversions(ParameterConversionConfiguration conversionConfig, Type initialParamType, ExcelParameterRegistration paramRegistration) { var appliedConversions = new List <LambdaExpression>(); // paramReg might be modified internally by the conversions, but won't become a different object var paramType = initialParamType; // Might become a different type as we convert foreach (var paramConversion in conversionConfig.ParameterConversions) { var lambda = paramConversion.Convert(paramType, paramRegistration); if (lambda == null) { continue; } // We got one to apply... // Some sanity checks Debug.Assert(lambda.Parameters.Count == 1); Debug.Assert(lambda.ReturnType == paramType || lambda.ReturnType.IsEquivalentTo(paramType)); appliedConversions.Add(lambda); // Change the Parameter Type to be whatever the conversion function takes us to // for the next round of processing paramType = lambda.Parameters[0].Type; } if (appliedConversions.Count == 0) { return(null); } return(appliedConversions); }
public ShimParameter(Type type, ExcelParameterRegistration reg, ParameterConversionConfiguration config) : this(type, reg.CustomAttributes) { // Try to find a converter for EnumeratedType ParameterRegistration = reg; PreparePropertyConverters(config, reg, ParameterConversionRegistration.GetParameterConversion); }
/// <summary> /// Modifies RegistrationEntries which have [ExcelMapArrayFunction], /// converting IEnumerable parameters to and from Excel Ranges (i.e. object[,]). /// This allows idiomatic .NET functions (which use sequences and lists) to be used as UDFs. /// /// Supports the use of Excel Array formulae where a UDF returns an enumerable. /// /// 1-dimensional Excel arrays are mapped automatically to/from IEnumerable. /// 2-dimensional Excel arrays can be mapped to a single function parameter with /// [ExcelMapPropertiesToColumnHeaders]. /// </summary> public static IEnumerable <ExcelFunctionRegistration> ProcessMapArrayFunctions( this IEnumerable <ExcelFunctionRegistration> registrations, ParameterConversionConfiguration config = null) { foreach (var reg in registrations) { if (!(reg.FunctionAttribute is ExcelMapArrayFunctionAttribute)) { // Not considered at all yield return(reg); continue; } try { var inputShimParameters = reg.FunctionLambda.Parameters.ZipSameLengths(reg.ParameterRegistrations, (p, r) => new ShimParameter(p.Type, r, config)).ToList(); var resultShimParameter = new ShimParameter(reg.FunctionLambda.ReturnType, reg.ReturnRegistration, config); // create the shim function as a lambda, using reflection LambdaExpression shim = MakeObjectArrayShim( reg.FunctionLambda, inputShimParameters, resultShimParameter); // create a description of the function, with a list of the output fields string functionDescription = "Returns " + resultShimParameter.HelpString; // create a description of each parameter, with a list of the input fields var parameterDescriptions = inputShimParameters.Select(shimParameter => "Input " + shimParameter.HelpString).ToArray(); // all ok so far - modify the registration reg.FunctionLambda = shim; if (String.IsNullOrEmpty(reg.FunctionAttribute.Description)) { reg.FunctionAttribute.Description = functionDescription; } for (int param = 0; param != reg.ParameterRegistrations.Count; ++param) { if (String.IsNullOrEmpty(reg.ParameterRegistrations[param].ArgumentAttribute.Description)) { reg.ParameterRegistrations[param].ArgumentAttribute.Description = parameterDescriptions[param]; } } } catch { // failed to shim, just pass on the original } yield return(reg); } }
void PreparePropertyConverters <Registration>(ParameterConversionConfiguration config, Registration reg, Func <ParameterConversionConfiguration, Type, Registration, LambdaExpression> getConversion) { Type[] propTypes = (MappedProperties == null) ? new[] { EnumeratedType ?? Type } : Array.ConvertAll(MappedProperties, p => p.PropertyType); LambdaExpression[] lambdas = Array.ConvertAll(propTypes, pt => (config == null) ? null : getConversion(config, EnumeratedType ?? Type, reg)); lambdas = Array.ConvertAll(lambdas, l => CastParamAndResult(l, typeof(object))); PropertyConverters = Array.ConvertAll(lambdas, l => (l == null) ? null : (Func <object, object>)l.Compile()); }
internal static LambdaExpression GetParameterConversion(ParameterConversionConfiguration conversionConfig, Type initialParamType, ExcelParameterRegistration paramRegistration) { return(ComposeLambdas(GetParameterConversions(conversionConfig, initialParamType, paramRegistration))); }
public static IEnumerable <ExcelFunctionRegistration> ProcessParameterConversions(this IEnumerable <ExcelFunctionRegistration> registrations, ParameterConversionConfiguration conversionConfig) { foreach (var reg in registrations) { // Keep a list of conversions for each parameter // TODO: Prevent having a cycle, but allow arbitrary ordering...? var paramsConversions = new List <List <LambdaExpression> >(); for (int i = 0; i < reg.FunctionLambda.Parameters.Count; i++) { var initialParamType = reg.FunctionLambda.Parameters[i].Type; var paramReg = reg.ParameterRegistrations[i]; var paramConversions = GetParameterConversions(conversionConfig, initialParamType, paramReg); paramsConversions.Add(paramConversions); } // for each parameter ! // Process return conversions var returnConversions = GetReturnConversions(conversionConfig, reg.FunctionLambda.ReturnType, reg.ReturnRegistration); // Now we apply all the conversions ApplyConversions(reg, paramsConversions, returnConversions); yield return(reg); } }
internal static LambdaExpression GetReturnConversion(ParameterConversionConfiguration conversionConfig, Type initialReturnType, ExcelReturnRegistration returnRegistration) { return(ComposeLambdas(GetReturnConversions(conversionConfig, initialReturnType, returnRegistration))); }
public ShimParameter(Type type, ExcelReturnRegistration reg, ParameterConversionConfiguration config) : this(type, reg.CustomAttributes) { ReturnRegistration = reg; PreparePropertyConverters(config, reg, ParameterConversionRegistration.GetReturnConversion); }