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);
        }
Example #2
0
        // 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);
        }
Example #3
0
 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);
 }
Example #4
0
        /// <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);
            }
        }
Example #5
0
 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());
 }
Example #6
0
 internal static LambdaExpression GetParameterConversion(ParameterConversionConfiguration conversionConfig,
                                                         Type initialParamType, ExcelParameterRegistration paramRegistration)
 {
     return(ComposeLambdas(GetParameterConversions(conversionConfig, initialParamType, paramRegistration)));
 }
Example #7
0
        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);
            }
        }
Example #8
0
 internal static LambdaExpression GetReturnConversion(ParameterConversionConfiguration conversionConfig,
                                                      Type initialReturnType, ExcelReturnRegistration returnRegistration)
 {
     return(ComposeLambdas(GetReturnConversions(conversionConfig, initialReturnType, returnRegistration)));
 }
Example #9
0
 public ShimParameter(Type type, ExcelReturnRegistration reg, ParameterConversionConfiguration config)
     : this(type, reg.CustomAttributes)
 {
     ReturnRegistration = reg;
     PreparePropertyConverters(config, reg, ParameterConversionRegistration.GetReturnConversion);
 }