Skip to content

Commit d128f4d

Browse files
Sergey-Vlasovgovert
authored andcommitted
Added [ExcelReturnConversion] for custom return types.
1 parent 6111ecc commit d128f4d

File tree

10 files changed

+110
-8
lines changed

10 files changed

+110
-8
lines changed

Source/ExcelDna.Integration/AssemblyLoader.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public static void ProcessAssemblies(
2828
List<ExportedAssembly> assemblies,
2929
List<MethodInfo> methods,
3030
List<ExtendedRegistration.ExcelParameterConversion> excelParameterConversions,
31+
List<ExtendedRegistration.ExcelReturnConversion> excelReturnConversions,
3132
List<ExtendedRegistration.ExcelFunctionProcessor> excelFunctionProcessors,
3233
List<Registration.ExcelFunctionRegistration> excelFunctionsExtendedRegistration,
3334
List<Registration.FunctionExecutionHandlerSelector> excelFunctionExecutionHandlerSelectors,
@@ -41,6 +42,7 @@ public static void ProcessAssemblies(
4142
{
4243
int initialObjectsCount = methods.Count +
4344
excelParameterConversions.Count +
45+
excelReturnConversions.Count +
4446
excelFunctionProcessors.Count +
4547
excelFunctionsExtendedRegistration.Count +
4648
excelFunctionExecutionHandlerSelectors.Count +
@@ -85,6 +87,7 @@ public static void ProcessAssemblies(
8587
if (!explicitRegistration)
8688
{
8789
GetExcelParameterConversions(type, excelParameterConversions);
90+
GetExcelReturnConversions(type, excelReturnConversions);
8891
GetExcelFunctionProcessors(type, excelFunctionProcessors);
8992
GetExcelMethods(type, explicitExports, methods, excelFunctionsExtendedRegistration);
9093
GetExcelFunctionExecutionHandlerSelectors(type, excelFunctionExecutionHandlerSelectors);
@@ -101,6 +104,7 @@ public static void ProcessAssemblies(
101104

102105
if (methods.Count +
103106
excelParameterConversions.Count +
107+
excelReturnConversions.Count +
104108
excelFunctionProcessors.Count +
105109
excelFunctionsExtendedRegistration.Count +
106110
excelFunctionExecutionHandlerSelectors.Count +
@@ -127,6 +131,18 @@ static void GetExcelParameterConversions(Type t, List<ExtendedRegistration.Excel
127131
}
128132
}
129133

134+
static void GetExcelReturnConversions(Type t, List<ExtendedRegistration.ExcelReturnConversion> excelReturnConversions)
135+
{
136+
MethodInfo[] mis = t.GetMethods(BindingFlags.Public | BindingFlags.Static);
137+
foreach (MethodInfo mi in mis)
138+
{
139+
if (IsReturnConversion(mi))
140+
{
141+
excelReturnConversions.Add(new ExtendedRegistration.ExcelReturnConversion(mi));
142+
}
143+
}
144+
}
145+
130146
static void GetExcelFunctionProcessors(Type t, List<ExtendedRegistration.ExcelFunctionProcessor> excelFunctionProcessors)
131147
{
132148
MethodInfo[] mis = t.GetMethods(BindingFlags.Public | BindingFlags.Static);
@@ -434,6 +450,11 @@ private static bool IsParameterConversion(MethodInfo methodInfo)
434450
return HasCustomAttribute(methodInfo, "ExcelDna.Integration.ExcelParameterConversionAttribute");
435451
}
436452

453+
private static bool IsReturnConversion(MethodInfo methodInfo)
454+
{
455+
return HasCustomAttribute(methodInfo, "ExcelDna.Integration.ExcelReturnConversionAttribute");
456+
}
457+
437458
private static bool IsFunctionProcessor(MethodInfo methodInfo)
438459
{
439460
return HasCustomAttribute(methodInfo, "ExcelDna.Integration.ExcelFunctionProcessorAttribute");

Source/ExcelDna.Integration/DnaLibrary.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ internal List<ExportedAssembly> GetAssemblies(string pathResolveRoot)
259259
[XmlIgnore]
260260
List<ExtendedRegistration.ExcelParameterConversion> _excelParameterConversions = new List<ExtendedRegistration.ExcelParameterConversion>();
261261
[XmlIgnore]
262+
List<ExtendedRegistration.ExcelReturnConversion> _excelReturnConversions = new List<ExtendedRegistration.ExcelReturnConversion>();
263+
[XmlIgnore]
262264
private List<Registration.ExcelFunctionRegistration> _excelFunctionsExtendedRegistration = new List<Registration.ExcelFunctionRegistration>();
263265
[XmlIgnore]
264266
private List<Registration.FunctionExecutionHandlerSelector> _excelFunctionExecutionHandlerSelectors = new List<Registration.FunctionExecutionHandlerSelector>();
@@ -281,7 +283,7 @@ internal void Initialize()
281283

282284
// Recursively get assemblies down .dna tree.
283285
_exportedAssemblies = GetAssemblies(dnaResolveRoot);
284-
AssemblyLoader.ProcessAssemblies(_exportedAssemblies, _methods, _excelParameterConversions, _excelFunctionProcessors, _excelFunctionsExtendedRegistration, _excelFunctionExecutionHandlerSelectors, _addIns, rtdServerTypes, comClassTypes);
286+
AssemblyLoader.ProcessAssemblies(_exportedAssemblies, _methods, _excelParameterConversions, _excelReturnConversions, _excelFunctionProcessors, _excelFunctionsExtendedRegistration, _excelFunctionExecutionHandlerSelectors, _addIns, rtdServerTypes, comClassTypes);
285287

286288
// Register RTD Server Types (i.e. remember that these types are available as RTD servers, with relevant ProgId etc.)
287289
RtdRegistration.RegisterRtdServerTypes(rtdServerTypes);
@@ -328,7 +330,7 @@ internal void AutoOpen()
328330
else
329331
ExtendedRegistration.Registration.RegisterStandard(_methods.Select(i => new ExcelDna.Registration.ExcelFunctionRegistration(i)), _excelFunctionExecutionHandlerSelectors);
330332

331-
ExtendedRegistration.Registration.RegisterExtended(_excelFunctionsExtendedRegistration, _excelParameterConversions, _excelFunctionProcessors, _excelFunctionExecutionHandlerSelectors);
333+
ExtendedRegistration.Registration.RegisterExtended(_excelFunctionsExtendedRegistration, _excelParameterConversions, _excelReturnConversions, _excelFunctionProcessors, _excelFunctionExecutionHandlerSelectors);
332334

333335
// Invoke AutoOpen in all assemblies
334336
foreach (AssemblyLoader.ExcelAddInInfo addIn in _addIns)

Source/ExcelDna.Integration/ExcelAttributes.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,15 @@ public class ExcelParameterConversionAttribute : Attribute
128128
{
129129
}
130130

131+
/// <summary>
132+
/// For user-defined return conversions.
133+
/// </summary>
134+
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
135+
[MeansImplicitUse]
136+
public class ExcelReturnConversionAttribute : Attribute
137+
{
138+
}
139+
131140
/// <summary>
132141
/// For user-defined function execution handlers.
133142
/// </summary>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Linq;
3+
using System.Linq.Expressions;
4+
using System.Reflection;
5+
6+
namespace ExcelDna.Integration.ExtendedRegistration
7+
{
8+
internal class ExcelReturnConversion
9+
{
10+
public MethodInfo MethodInfo { get; private set; }
11+
12+
public ExcelReturnConversion(MethodInfo methodInfo)
13+
{
14+
this.MethodInfo = methodInfo;
15+
}
16+
17+
public Func<Type, IExcelFunctionReturn, LambdaExpression> GetConversion()
18+
{
19+
return (type, returnReg) => CreateConversion(type, returnReg);
20+
}
21+
22+
private LambdaExpression CreateConversion(Type type, IExcelFunctionReturn returnReg)
23+
{
24+
ParameterInfo[] parameters = MethodInfo.GetParameters();
25+
26+
if (parameters.Length != 1 || type != parameters[0].ParameterType)
27+
return null;
28+
29+
var paramExprs = parameters
30+
.Select(pi => Expression.Parameter(pi.ParameterType, pi.Name))
31+
.ToList();
32+
return Expression.Lambda(Expression.Call(MethodInfo, paramExprs), MethodInfo.Name, paramExprs);
33+
}
34+
}
35+
}

Source/ExcelDna.Integration/ExtendedRegistration/Registration.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ namespace ExcelDna.Integration.ExtendedRegistration
1010
{
1111
internal class Registration
1212
{
13-
public static void RegisterExtended(IEnumerable<ExcelDna.Registration.ExcelFunctionRegistration> functions, IEnumerable<ExcelParameterConversion> parameterConversions, IEnumerable<ExcelFunctionProcessor> excelFunctionProcessors, IEnumerable<FunctionExecutionHandlerSelector> excelFunctionExecutionHandlerSelectors)
13+
public static void RegisterExtended(IEnumerable<ExcelDna.Registration.ExcelFunctionRegistration> functions, IEnumerable<ExcelParameterConversion> parameterConversions, IEnumerable<ExcelReturnConversion> returnConversions, IEnumerable<ExcelFunctionProcessor> excelFunctionProcessors, IEnumerable<FunctionExecutionHandlerSelector> excelFunctionExecutionHandlerSelectors)
1414
{
1515
// Set the Parameter Conversions before they are applied by the ProcessParameterConversions call below.
1616
// CONSIDER: We might change the registration to be an object...?
17-
var conversionConfig = GetParameterConversionConfig(parameterConversions);
17+
var conversionConfig = GetParameterConversionConfig(parameterConversions, returnConversions);
1818

1919
var functionHandlerConfig = GetFunctionExecutionHandlerConfig(excelFunctionExecutionHandlerSelectors);
2020

@@ -47,7 +47,7 @@ internal static void Register(IEnumerable<ExcelDna.Registration.ExcelFunctionReg
4747
ExcelIntegration.RegisterLambdaExpressions(lambdas, attribs, argAttribs);
4848
}
4949

50-
static ParameterConversionConfiguration GetParameterConversionConfig(IEnumerable<ExcelParameterConversion> parameterConversions)
50+
static ParameterConversionConfiguration GetParameterConversionConfig(IEnumerable<ExcelParameterConversion> parameterConversions, IEnumerable<ExcelReturnConversion> returnConversions)
5151
{
5252
// NOTE: The parameter conversion list is processed once per parameter.
5353
// Parameter conversions will apply from most inside, to most outside.
@@ -79,7 +79,8 @@ static ParameterConversionConfiguration GetParameterConversionConfig(IEnumerable
7979
.AddParameterConversion(ParameterConversions.GetOptionalConversion(treatEmptyAsMissing: true))
8080
.AddParameterConversion(RangeConversion.GetRangeParameterConversion, null)
8181

82-
.AddParameterConversions(ParameterConversions.GetUserConversions(parameterConversions))
82+
.AddParameterConversions(ParameterConversions.GetUserParameterConversions(parameterConversions))
83+
.AddReturnConversions(ParameterConversions.GetUserReturnConversions(returnConversions))
8384

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

Source/ExcelDna.Integration/Registration/ParameterConversionConfiguration.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,14 @@ public ParameterConversionConfiguration AddReturnConversion<TFrom, TTo>(Expressi
155155
AddReturnConversion<TFrom>((unusedReturnType, unusedAttributes) => convert, null, handleSubTypes);
156156
return this;
157157
}
158+
159+
public ParameterConversionConfiguration AddReturnConversions(IEnumerable<Func<Type, IExcelFunctionReturn, LambdaExpression>> returnConversions)
160+
{
161+
foreach (var i in returnConversions)
162+
AddReturnConversion(i);
163+
164+
return this;
165+
}
158166
#endregion
159167

160168
Func<Type, ExcelParameterRegistration, LambdaExpression> GetNullableConversion(bool treatEmptyAsMissing, bool treatNAErrorAsMissing)

Source/ExcelDna.Integration/Registration/ParameterConversions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,16 @@ public static Func<Type, ExcelParameterRegistration, LambdaExpression> GetEnumSt
3939
return (type, paramReg) => EnumStringConversion(type, paramReg);
4040
}
4141

42-
internal static IEnumerable<Func<Type, ExcelParameterRegistration, LambdaExpression>> GetUserConversions(IEnumerable<Integration.ExtendedRegistration.ExcelParameterConversion> parameterConversions)
42+
internal static IEnumerable<Func<Type, ExcelParameterRegistration, LambdaExpression>> GetUserParameterConversions(IEnumerable<Integration.ExtendedRegistration.ExcelParameterConversion> parameterConversions)
4343
{
4444
return parameterConversions.OrderBy(i => i.MethodInfo.Name).Select(i => i.GetConversion());
4545
}
4646

47+
internal static IEnumerable<Func<Type, IExcelFunctionReturn, LambdaExpression>> GetUserReturnConversions(IEnumerable<Integration.ExtendedRegistration.ExcelReturnConversion> returnConversions)
48+
{
49+
return returnConversions.OrderBy(i => i.MethodInfo.Name).Select(i => i.GetConversion());
50+
}
51+
4752
internal static LambdaExpression NullableConversion(
4853
ParameterConversionConfiguration config, Type type,
4954
ExcelParameterRegistration paramReg, bool treatEmptyAsMissing,

Source/Tests/ExcelDna.AddIn.RuntimeTests/Conversions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,11 @@ public static Version ToVersion(string s)
2727
{
2828
return new Version(s);
2929
}
30+
31+
[ExcelReturnConversion]
32+
public static string Order1FromTestType1(TestType1 value)
33+
{
34+
return value.Value;
35+
}
3036
}
3137
}

Source/Tests/ExcelDna.AddIn.RuntimeTests/MyFunctions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ public static string MyVersion2(Version v)
141141
return "The Version value with field count 2 is " + v.ToString(2);
142142
}
143143

144+
[ExcelFunction]
145+
public static TestType1 MyReturnTestType1(string s)
146+
{
147+
return new TestType1("The TestType1 return value is " + s);
148+
}
149+
144150
[ExcelFunction]
145151
public static string MyFunctionExecutionLog()
146152
{

Source/Tests/ExcelDna.RuntimeTests/Registration.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,16 @@ public void UserDefinedParameterConversions()
223223
Assert.Equal("The TestType2 value is From TestType1 world2", functionRange.Value.ToString());
224224
}
225225

226-
[ExcelFact(Workbook = "", AddIn = @"..\..\..\..\ExcelDna.AddIn.RuntimeTests\bin\Debug\net6.0-windows\ExcelDna.AddIn.RuntimeTests-AddIn")]
226+
[ExcelFact(Workbook = "", AddIn = AddInPath.RuntimeTests)]
227+
public void UserDefinedReturnConversions()
228+
{
229+
Range functionRange = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["B1:B1"];
230+
231+
functionRange.Formula = "=MyReturnTestType1(\"world\")";
232+
Assert.Equal("The TestType1 return value is world", functionRange.Value.ToString());
233+
}
234+
235+
[ExcelFact(Workbook = "", AddIn = AddInPath.RuntimeTests)]
227236
public void FunctionExecutionHandlerExtended()
228237
{
229238
Range functionRange = ((Worksheet)ExcelDna.Testing.Util.Workbook.Sheets[1]).Range["B1"];

0 commit comments

Comments
 (0)