Example #1
0
        /// <summary>
        /// Convert excel range parameter to arrays and null objects to NA
        /// </summary>
        /// <returns></returns>
        private ParameterConversionConfiguration GetParameterConversionConfig()
        {
            var objectRepository = Container.GetInstance <IObjectRepository>();

            var paramConversionConfig = new ParameterConversionConfiguration()

                                        // Register the Standard Parameter Conversions (with the optional switch on how to treat references to empty cells)
                                        .AddParameterConversion(ParameterConversions.GetNullableConversion(treatEmptyAsMissing: false))
                                        .AddParameterConversion(ParameterConversions.GetOptionalConversion(treatEmptyAsMissing: false))

                                        // This parameter conversion adds support for string[] parameters (by accepting object[] instead).
                                        // It uses the TypeConversion utility class defined in ExcelDna.Registration to get an object->string
                                        // conversion that is consist with Excel (in this case, Excel is called to do the conversion).
                                        .AddParameterConversion((object[] inputs) => inputs.Select(TypeConversion.ConvertToInt32).ToArray())
                                        .AddParameterConversion((object[] inputs) => inputs.Select(TypeConversion.ConvertToString).ToArray())
                                        // #ParameterConversion Convert handle to public object
                                        .AddParameterConversion((object obj) => objectRepository.Get((string)obj))
                                        //.AddParameterConversion((string handle) => handle.Contains("::") ? Container.GetInstance<ICreator>().Create(handle) : handle )
                                        //.AddParameterConversion((Type type, ExcelParameterRegistration paramReg) =>
                                        //    (Expression<Func<object, IPublicObject>>)(obj => creator.Create((string)obj)), typeof(IPublicObject))
                                        //paramReg.ArgumentAttribute.Name == "oTransaction" ? (Expression<Func<object, IPublicObject>>)(obj => creator.Create((string)obj)) : null, typeof(IPublicObject))
                                        //(Expression<Func<object,IPublicObject>>) (obj => paramReg.ArgumentAttribute.Name == "hTransaction" ? creator.Create((string) obj) : null),typeof(IPublicObject))

                                        .AddReturnConversion((object obj) => obj.IsDefault() ? ExcelError.ExcelErrorNA : obj)
                                        // #ReturnConversion Convert public objects to its handle for excel display
                                        .AddReturnConversion((IPublicObject obj) => obj.Handle);

            return(paramConversionConfig);
        }
        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;
        }
        public static void TestGlobalReturnConversion()
        {
            // Conversions can be 'global' so they can be applied to any function.
            // The conversion returns 'null' for cases (type + attribute combinations) where it should not be applied.

            // This test simulates the return type conversion we would do to change async #N/A to something else
            // (though using strings instead of the real ExcelError.ExcelErrorNA result).

            var testLambda = (Expression<Func<string, object>>)(name => name);
            var registration = new ExcelFunctionRegistration(testLambda);

            var conversionConfig = new ParameterConversionConfiguration()
                .AddReturnConversion((type, customAttributes) => type != typeof(object) ? null : ((Expression<Func<object, object>>)
                                                ((object returnValue) => returnValue.Equals("NA") ? (object)"### WAIT ###" : returnValue)), null);

            var convertedRegistrations = ParameterConversionRegistration.ProcessParameterConversions(new[] { registration }, conversionConfig);
            var converted = convertedRegistrations.First();
            var compiled = (Func<string, object>)converted.FunctionLambda.Compile();

            var resultNormal = compiled("XYZ");
            Assert.AreEqual("XYZ", resultNormal);

            var resultNA = compiled("NA");
            Assert.AreEqual("### WAIT ###", resultNA);
        }
Example #4
0
        public static void TestGlobalReturnConversion()
        {
            // Conversions can be 'global' so they can be applied to any function.
            // The conversion returns 'null' for cases (type + attribute combinations) where it should not be applied.

            // This test simulates the return type conversion we would do to change async #N/A to something else
            // (though using strings instead of the real ExcelError.ExcelErrorNA result).

            var testLambda   = (Expression <Func <string, object> >)(name => name);
            var registration = new ExcelFunctionRegistration(testLambda);

            var conversionConfig = new ParameterConversionConfiguration()
                                   .AddReturnConversion((type, customAttributes) => type != typeof(object) ? null : ((Expression <Func <object, object> >)
                                                                                                                         ((object returnValue) => returnValue.Equals("NA") ? (object)"### WAIT ###" : returnValue)), null);

            var convertedRegistrations = ParameterConversionRegistration.ProcessParameterConversions(new[] { registration }, conversionConfig);
            var converted = convertedRegistrations.First();
            var compiled  = (Func <string, object>)converted.FunctionLambda.Compile();

            var resultNormal = compiled("XYZ");

            Assert.AreEqual("XYZ", resultNormal);

            var resultNA = compiled("NA");

            Assert.AreEqual("### WAIT ###", resultNA);
        }
        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 List<LambdaExpression> GetReturnConversions(ParameterConversionConfiguration conversionConfig, Type initialReturnType, ExcelReturnRegistration returnRegistration)
        {
            var appliedConversions = new List<LambdaExpression>();

            // paramReg might be modified internally by the conversions, but won't become a different object
            var returnType = initialReturnType; // Might become a different type as we convert

            foreach (var returnConversion in conversionConfig.ReturnConversions)
            {
                var lambda = returnConversion.Convert(returnType, returnRegistration);
                if (lambda == null)
                    continue;

                // We got one to apply...
                // Some sanity checks
                Debug.Assert(lambda.Parameters.Count == 1);
                Debug.Assert(lambda.Parameters[0].Type == returnType);

                appliedConversions.Add(lambda);

                // Change the Return Type to be whatever the conversion function returns
                // for the next round of processing
                returnType = lambda.ReturnType;
            }

            if (appliedConversions.Count == 0)
                return null;

            return appliedConversions;
        }
Example #7
0
        static ParameterConversionConfiguration GetParameterConversionConfig()
        {
            // NOTE: The parameter conversion list is processed once per parameter.
            //       Parameter conversions will apply from most inside, to most outside.
            //       So to apply a conversion chain like
            //           string -> Type1 -> Type2
            //       we need to register in the (reverse) order
            //           Type1 -> Type2
            //           string -> Type1
            //
            //       (If the registration were in the order
            //           string -> Type1
            //           Type1 -> Type2
            //       the parameter (starting as Type2) would not match the first conversion,
            //       then the second conversion (Type1 -> Type2) would be applied, and no more,
            //       leaving the parameter having Type1 (and probably not eligible for Excel registration.)
            //
            //
            //       Return conversions are also applied from most inside to most outside.
            //       So to apply a return conversion chain like
            //           Type1 -> Type2 -> string
            //       we need to register the ReturnConversions as
            //           Type1 -> Type2
            //           Type2 -> string
            //

            var paramConversionConfig = new ParameterConversionConfiguration()

                                        // Register the Standard Parameter Conversions (with the optional switch on how to treat references to empty cells)
                                        .AddParameterConversion(ParameterConversions.GetOptionalConversion(treatEmptyAsMissing: true))

                                        // Register some type conversions (not the ordering discussed above)
                                        .AddParameterConversion((string value) => new TestType1(value))
                                        .AddParameterConversion((TestType1 value) => new TestType2(value))

                                        // This is a conversion applied to the return value of the function
                                        .AddReturnConversion((TestType1 value) => value.ToString())
                                        .AddReturnConversion((Complex value) => new double[2] {
                value.Real, value.Imaginary
            })

                                        //  .AddParameterConversion((string value) => convert2(convert1(value)));

                                        // This parameter conversion adds support for string[] parameters (by accepting object[] instead).
                                        // It uses the TypeConversion utility class defined in ExcelDna.Registration to get an object->string
                                        // conversion that is consist with Excel (in this case, Excel is called to do the conversion).
                                        .AddParameterConversion((object[] inputs) => inputs.Select(TypeConversion.ConvertToString).ToArray())

                                        // This is a pair of very generic conversions for Enum types
                                        .AddReturnConversion((Enum value) => value.ToString(), handleSubTypes: true)
                                        .AddParameterConversion(ParameterConversions.GetEnumStringConversion())

                                        .AddParameterConversion((object[] input) => new Complex(TypeConversion.ConvertToDouble(input[0]), TypeConversion.ConvertToDouble(input[1])))
                                        .AddNullableConversion(treatEmptyAsMissing: true, treatNAErrorAsMissing: true);

            return(paramConversionConfig);
        }
Example #8
0
 public TestCase(MethodInfo methodInfo, object[] inputData, object expectedOutputData, string name = "",
                 ParameterConversionConfiguration config = null)
 {
     MethodInfo         = methodInfo;
     InputData          = inputData;
     ExpectedOutputData = expectedOutputData;
     Name =
         (String.IsNullOrEmpty(name) ? MethodInfo.ToString() : name);
     Config = config;
 }
        public static void TestInstanceRegistration()
        {
            var instanceMethod = typeof(TestClass).GetMethod("GetContent");
            var lambda = WrapInstanceMethod(instanceMethod);

            // Create a new object every time the instance method is called... (typically we'd look up using a handle)
            var paramConversionConfig = new ParameterConversionConfiguration()
                .AddParameterConversion((string content) => new TestClass(content));

            // TODO: Clean up how we suggest this code gets called
            var reg = new ExcelFunctionRegistration(lambda);
            var processed = ParameterConversionRegistration.ProcessParameterConversions(new[] { reg }, paramConversionConfig);
            processed.RegisterFunctions();
        }
Example #10
0
        public static void TestInstanceRegistration()
        {
            var instanceMethod = typeof(TestClass).GetMethod("GetContent");
            var lambda         = WrapInstanceMethod(instanceMethod);

            // Create a new object every time the instance method is called... (typically we'd look up using a handle)
            var paramConversionConfig = new ParameterConversionConfiguration()
                                        .AddParameterConversion((string content) => new TestClass(content));

            // TODO: Clean up how we suggest this code gets called
            var reg       = new ExcelFunctionRegistration(lambda);
            var processed = ParameterConversionRegistration.ProcessParameterConversions(new[] { reg }, paramConversionConfig);

            processed.RegisterFunctions();
        }
        public static void TestIgnoredGlobalReturnConversion()
        {
            var testLambda = (Expression<Func<string, object>>)(name => name);
            var registration = new ExcelFunctionRegistration(testLambda);

            var conversionConfig = new ParameterConversionConfiguration()
                // Add a return conversion that is never applied
                .AddReturnConversion((type, customAttributes) => null, null);

            var convertedRegistrations = ParameterConversionRegistration.ProcessParameterConversions(new[] { registration }, conversionConfig);
            var converted = convertedRegistrations.First();
            var compiled = (Func<string, object>)converted.FunctionLambda.Compile();

            var resultNormal = compiled("XYZ");
            Assert.AreEqual("XYZ", resultNormal);
        }
Example #12
0
        public static void TestIgnoredGlobalReturnConversion()
        {
            var testLambda   = (Expression <Func <string, object> >)(name => name);
            var registration = new ExcelFunctionRegistration(testLambda);

            var conversionConfig = new ParameterConversionConfiguration()
                                   // Add a return conversion that is never applied
                                   .AddReturnConversion((type, customAttributes) => null, null);

            var convertedRegistrations = ParameterConversionRegistration.ProcessParameterConversions(new[] { registration }, conversionConfig);
            var converted = convertedRegistrations.First();
            var compiled  = (Func <string, object>)converted.FunctionLambda.Compile();

            var resultNormal = compiled("XYZ");

            Assert.AreEqual("XYZ", resultNormal);
        }
Example #13
0
        public static void TestReturnConversion()
        {
            //
            var testLambda   = (Expression <Func <string, int, string> >)((name, value) => name + value.ToString());
            var registration = new ExcelFunctionRegistration(testLambda);

            // This conversion takes the return string and duplicates it
            var conversionConfig = new ParameterConversionConfiguration()
                                   .AddReturnConversion((string value) => value + value);

            var convertedRegistrations = ParameterConversionRegistration.ProcessParameterConversions(new[] { registration }, conversionConfig);
            var converted = convertedRegistrations.First();
            var compiled  = (Func <string, int, string>)converted.FunctionLambda.Compile();
            var result    = compiled("asd", 42);

            Assert.AreEqual("asd42asd42", result);
        }
        static ParameterConversionConfiguration GetParameterConversionConfig()
        {
            var paramConversionConfig = new ParameterConversionConfiguration()

                                        // Register the Standard Parameter Conversions (with the optional switch on how to treat references to empty cells)
                                        .AddParameterConversion(ParameterConversions.GetNullableConversion(treatEmptyAsMissing: false))
                                        .AddParameterConversion(ParameterConversions.GetOptionalConversion(treatEmptyAsMissing: false))

                                        // From string to Type
                                        .AddParameterConversion((string input) => ToType(input))

                                        // This parameter conversion adds support for string[] parameters (by accepting object[] instead).
                                        // It uses the TypeConversion utility class defined in ExcelDna.Registration to get an object->string
                                        // conversion that is consist with Excel (in this case, Excel is called to do the conversion).
                                        .AddParameterConversion((object[] inputs) => inputs.Select(TypeConversion.ConvertToString).ToArray())
                                        .AddParameterConversion(RangeParameterConversion.ParameterConversion);

            return(paramConversionConfig);
        }
        public static void TestNestedLambdaConversion()
        {
            // The function duplicates the input string (inside a lambda that captures that input parameter)
            var registration = new ExcelFunctionRegistration(typeof(ParameterConversionTests).GetMethod("TestMethodWithLambda", BindingFlags.NonPublic | BindingFlags.Static));

            var conversionConfig = new ParameterConversionConfiguration()
                .AddReturnConversion((type, customAttributes) => type != typeof(object) ? null : ((Expression<Func<object, object>>)
                                                ((object returnValue) => returnValue.Equals("NANA") ? (object)"### WAIT ###" : returnValue)), null);

            var convertedRegistrations = ParameterConversionRegistration.ProcessParameterConversions(new[] { registration }, conversionConfig);
            var converted = convertedRegistrations.First();
            var compiled = (Func<string, object>)converted.FunctionLambda.Compile();

            var resultNormal = compiled("XYZ");
            Assert.AreEqual("XYZXYZ", resultNormal);

            var resultNA = compiled("NA");
            Assert.AreEqual("### WAIT ###", resultNA);
        }
Example #16
0
        public static void TestParameterConversions()
        {
            //
            var testLambda   = (Expression <Func <string, int, string> >)((name, value) => name + value.ToString());
            var registration = new ExcelFunctionRegistration(testLambda);

            // This conversion applied a mix of parameter and string conversions
            var conversionConfig = new ParameterConversionConfiguration()
                                   .AddParameterConversion((double value) => (int)(value * 10))
                                   .AddParameterConversion((string value) => value.Substring(0, 2))
                                   .AddReturnConversion((string value) => value + value)
                                   .AddReturnConversion((string value) => value.Length);

            var convertedRegistrations = ParameterConversionRegistration.ProcessParameterConversions(new[] { registration }, conversionConfig);
            var converted = convertedRegistrations.First();
            var compiled  = (Func <string, double, int>)converted.FunctionLambda.Compile();
            var result    = compiled("qwXXX", 4.2);

            Assert.AreEqual("qw42qw42".Length, result);
        }
Example #17
0
        public static void TestNestedLambdaConversion()
        {
            // The function duplicates the input string (inside a lambda that captures that input parameter)
            var registration = new ExcelFunctionRegistration(typeof(ParameterConversionTests).GetMethod("TestMethodWithLambda", BindingFlags.NonPublic | BindingFlags.Static));

            var conversionConfig = new ParameterConversionConfiguration()
                                   .AddReturnConversion((type, customAttributes) => type != typeof(object) ? null : ((Expression <Func <object, object> >)
                                                                                                                         ((object returnValue) => returnValue.Equals("NANA") ? (object)"### WAIT ###" : returnValue)), null);

            var convertedRegistrations = ParameterConversionRegistration.ProcessParameterConversions(new[] { registration }, conversionConfig);
            var converted = convertedRegistrations.First();
            var compiled  = (Func <string, object>)converted.FunctionLambda.Compile();

            var resultNormal = compiled("XYZ");

            Assert.AreEqual("XYZXYZ", resultNormal);

            var resultNA = compiled("NA");

            Assert.AreEqual("### WAIT ###", resultNA);
        }
Example #18
0
 public TestCase(MethodInfo methodInfo, object[] inputData, object expectedOutputData,
                 ParameterConversionConfiguration config)
     : this(methodInfo, inputData, expectedOutputData, "", config)
 {
 }
        public static void TestParameterConversions()
        {
            //
            var testLambda = (Expression<Func<string, int, string>>)((name, value) => name + value.ToString());
            var registration = new ExcelFunctionRegistration(testLambda);

            // This conversion applied a mix of parameter and string conversions
            var conversionConfig = new ParameterConversionConfiguration()
                                    .AddParameterConversion((double value) => (int)(value * 10))
                                    .AddParameterConversion((string value) => value.Substring(0, 2))
                                    .AddReturnConversion((string value) => value + value)
                                    .AddReturnConversion((string value) => value.Length);

            var convertedRegistrations = ParameterConversionRegistration.ProcessParameterConversions(new[] { registration }, conversionConfig);
            var converted = convertedRegistrations.First();
            var compiled = (Func<string, double, int>)converted.FunctionLambda.Compile();
            var result = compiled("qwXXX", 4.2);
            Assert.AreEqual("qw42qw42".Length, result);
        }
        public static void TestReturnConversion()
        {
            //
            var testLambda = (Expression<Func<string, int, string>>)((name, value) => name + value.ToString());
            var registration = new ExcelFunctionRegistration(testLambda);

            // This conversion takes the return string and duplicates it
            var conversionConfig = new ParameterConversionConfiguration()
                                    .AddReturnConversion((string value) => value + value);

            var convertedRegistrations = ParameterConversionRegistration.ProcessParameterConversions(new[] { registration }, conversionConfig);
            var converted = convertedRegistrations.First();
            var compiled = (Func<string, int, string>)converted.FunctionLambda.Compile();
            var result = compiled("asd", 42);
            Assert.AreEqual("asd42asd42", result);
        }
Example #21
0
        static ParameterConversionConfiguration GetParameterConversionConfig()
        {
            // NOTE: The parameter conversion list is processed once per parameter.
            //       Parameter conversions will apply from most inside, to most outside.
            //       So to apply a conversion chain like
            //           string -> Type1 -> Type2
            //       we need to register in the (reverse) order
            //           Type1 -> Type2
            //           string -> Type1
            //
            //       (If the registration were in the order
            //           string -> Type1
            //           Type1 -> Type2
            //       the parameter (starting as Type2) would not match the first conversion,
            //       then the second conversion (Type1 -> Type2) would be applied, and no more,
            //       leaving the parameter having Type1 (and probably not eligible for Excel registration.)
            //
            //
            //       Return conversions are also applied from most inside to most outside.
            //       So to apply a return conversion chain like
            //           Type1 -> Type2 -> string
            //       we need to register the ReturnConversions as
            //           Type1 -> Type2
            //           Type2 -> string
            //

            var paramConversionConfig = new ParameterConversionConfiguration()

                // Register the Standard Parameter Conversions (with the optional switch on how to treat references to empty cells)
                .AddParameterConversion(ParameterConversions.GetOptionalConversion(treatEmptyAsMissing: true))

                // Register some type conversions (note the ordering discussed above)
                .AddParameterConversion((TestType1 value) => new TestType2(value))
                .AddParameterConversion((string value) => new TestType1(value))

                // This is a conversion applied to the return value of the function
                .AddReturnConversion((TestType1 value) => value.ToString())
                .AddReturnConversion((Complex value) => new double[2] {value.Real, value.Imaginary})

                //  .AddParameterConversion((string value) => convert2(convert1(value)));

                // This parameter conversion adds support for string[] parameters (by accepting object[] instead).
                // It uses the TypeConversion utility class defined in ExcelDna.Registration to get an object->string
                // conversion that is consist with Excel (in this case, Excel is called to do the conversion).
                .AddParameterConversion((object[] inputs) => inputs.Select(TypeConversion.ConvertToString).ToArray())

                // This is a pair of very generic conversions for Enum types
                .AddReturnConversion((Enum value) => value.ToString(), handleSubTypes: true)
                .AddParameterConversion(ParameterConversions.GetEnumStringConversion())

                .AddParameterConversion((object[] input) => new Complex(TypeConversion.ConvertToDouble(input[0]), TypeConversion.ConvertToDouble(input[1])))
                .AddNullableConversion(treatEmptyAsMissing: true, treatNAErrorAsMissing: true);

            return paramConversionConfig;
        }
 internal static LambdaExpression GetReturnConversion(ParameterConversionConfiguration conversionConfig,
     Type initialReturnType, ExcelReturnRegistration returnRegistration)
 {
     return ComposeLambdas(GetReturnConversions(conversionConfig, initialReturnType, returnRegistration));
 }
 internal static LambdaExpression GetParameterConversion(ParameterConversionConfiguration conversionConfig,
     Type initialParamType, ExcelParameterRegistration paramRegistration)
 {
     return ComposeLambdas(GetParameterConversions(conversionConfig, initialParamType, paramRegistration));
 }