You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
9.1 KiB
255 lines
9.1 KiB
using NPOI.SS.Formula;
|
|
using NPOI.SS.Formula.Eval;
|
|
using NPOI.Util;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace NPOI.SS.Formula.Functions
|
|
{
|
|
public interface IArrayFunction
|
|
{
|
|
/// <summary>
|
|
/// - Excel uses the error code #NUM! instead of IEEE NaN, so when numeric functions evaluate to Double#NaN be sure to translate the result to ErrorEval#NUM_ERROR.
|
|
/// </summary>
|
|
/// <param name="args">the evaluated function arguments. Empty values are represented with BlankEval or MissingArgEval, never <code>null</code></param>
|
|
/// <param name="srcRowIndex">row index of the cell containing the formula under evaluation</param>
|
|
/// <param name="srcColumnIndex">column index of the cell containing the formula under evaluation</param>
|
|
/// <returns> The evaluated result, possibly an ErrorEval, never <code>null</code></returns>
|
|
ValueEval EvaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex);
|
|
}
|
|
|
|
public class ArrayFunction
|
|
{
|
|
/// <summary>
|
|
/// Evaluate an array function with two arguments.
|
|
/// </summary>
|
|
/// <param name="arg0">the first function argument. Empty values are represented with <see cref="BlankEval"/> or <see cref="MissingArgEval"/>, never <c>null</c></param>
|
|
/// <param name="arg1">the second function argument. Empty values are represented with <see cref="BlankEval"/> or <see cref="MissingArgEval"/>, never <c>null</c></param>
|
|
/// <param name="srcRowIndex">row index of the cell containing the formula under evaluation</param>
|
|
/// <param name="srcColumnIndex">column index of the cell containing the formula under evaluation</param>
|
|
/// <param name="evalFunc"></param>
|
|
/// <returns>The evaluated result, possibly an <see cref="ErrorEval"/>, never <c>null</c>.
|
|
/// <b>Note</b> - Excel uses the error code <i>#NUM!</i> instead of IEEE <i>NaN</i>, so when
|
|
/// numeric functions evaluate to <see cref="double.NaN"/> be sure to translate the result to
|
|
/// <see cref="ErrorEval.NUM_ERROR"/>. </returns>
|
|
public ValueEval EvaluateTwoArrayArgs(ValueEval arg0, ValueEval arg1, int srcRowIndex, int srcColumnIndex,
|
|
Func<ValueEval, ValueEval, ValueEval> evalFunc)
|
|
{
|
|
return _evaluateTwoArrayArgs(arg0, arg1, srcRowIndex, srcColumnIndex, evalFunc);
|
|
}
|
|
|
|
public ValueEval EvaluateOneArrayArg(ValueEval arg0, int srcRowIndex, int srcColumnIndex,
|
|
Func<ValueEval, ValueEval> evalFunc)
|
|
{
|
|
return _evaluateOneArrayArg(arg0, srcRowIndex, srcColumnIndex, evalFunc);
|
|
}
|
|
|
|
internal static ValueEval _evaluateTwoArrayArgs(ValueEval arg0, ValueEval arg1, int srcRowIndex, int srcColumnIndex,
|
|
Func<ValueEval, ValueEval, ValueEval> evalFunc)
|
|
{
|
|
int w1, w2, h1, h2;
|
|
int a1FirstCol = 0, a1FirstRow = 0;
|
|
if(arg0 is AreaEval)
|
|
{
|
|
AreaEval ae = (AreaEval)arg0;
|
|
w1 = ae.Width;
|
|
h1 = ae.Height;
|
|
a1FirstCol = ae.FirstColumn;
|
|
a1FirstRow = ae.FirstRow;
|
|
}
|
|
else if(arg0 is RefEval)
|
|
{
|
|
RefEval ref1 = (RefEval)arg0;
|
|
w1 = 1;
|
|
h1 = 1;
|
|
a1FirstCol = ref1.Column;
|
|
a1FirstRow = ref1.Row;
|
|
}
|
|
else
|
|
{
|
|
w1 = 1;
|
|
h1 = 1;
|
|
}
|
|
int a2FirstCol = 0, a2FirstRow = 0;
|
|
if(arg1 is AreaEval)
|
|
{
|
|
AreaEval ae = (AreaEval)arg1;
|
|
w2 = ae.Width;
|
|
h2 = ae.Height;
|
|
a2FirstCol = ae.FirstColumn;
|
|
a2FirstRow = ae.FirstRow;
|
|
}
|
|
else if(arg1 is RefEval)
|
|
{
|
|
RefEval ref1 = (RefEval)arg1;
|
|
w2 = 1;
|
|
h2 = 1;
|
|
a2FirstCol = ref1.Column;
|
|
a2FirstRow = ref1.Row;
|
|
}
|
|
else
|
|
{
|
|
w2 = 1;
|
|
h2 = 1;
|
|
}
|
|
|
|
int width = Math.Max(w1, w2);
|
|
int height = Math.Max(h1, h2);
|
|
|
|
ValueEval[] vals = new ValueEval[height * width];
|
|
|
|
int idx = 0;
|
|
for(int i = 0; i < height; i++)
|
|
{
|
|
for(int j = 0; j < width; j++)
|
|
{
|
|
ValueEval vA;
|
|
try
|
|
{
|
|
vA = OperandResolver.GetSingleValue(arg0, a1FirstRow + i, a1FirstCol + j);
|
|
}
|
|
catch(FormulaParseException e)
|
|
{
|
|
vA = ErrorEval.NAME_INVALID;
|
|
}
|
|
catch(EvaluationException e)
|
|
{
|
|
vA = e.GetErrorEval();
|
|
}
|
|
catch(RuntimeException e)
|
|
{
|
|
if(e.Message.StartsWith("Don't know how to evaluate name"))
|
|
{
|
|
vA = ErrorEval.NAME_INVALID;
|
|
}
|
|
else
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
ValueEval vB;
|
|
try
|
|
{
|
|
vB = OperandResolver.GetSingleValue(arg1, a2FirstRow + i, a2FirstCol + j);
|
|
}
|
|
catch(FormulaParseException e)
|
|
{
|
|
vB = ErrorEval.NAME_INVALID;
|
|
}
|
|
catch(EvaluationException e)
|
|
{
|
|
vB = e.GetErrorEval();
|
|
}
|
|
catch(RuntimeException e)
|
|
{
|
|
if(e.Message.StartsWith("Don't know how to evaluate name"))
|
|
{
|
|
vB = ErrorEval.NAME_INVALID;
|
|
}
|
|
else
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
if(vA is ErrorEval)
|
|
{
|
|
vals[idx++] = vA;
|
|
}
|
|
else if(vB is ErrorEval)
|
|
{
|
|
vals[idx++] = vB;
|
|
}
|
|
else
|
|
{
|
|
vals[idx++] = evalFunc.Invoke(vA, vB);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if(vals.Length == 1)
|
|
{
|
|
return vals[0];
|
|
}
|
|
|
|
return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals);
|
|
}
|
|
|
|
|
|
internal static ValueEval _evaluateOneArrayArg(ValueEval arg0, int srcRowIndex, int srcColumnIndex,
|
|
Func<ValueEval, ValueEval> evalFunc)
|
|
{
|
|
int w1, w2, h1, h2;
|
|
int a1FirstCol = 0, a1FirstRow = 0;
|
|
if(arg0 is AreaEval)
|
|
{
|
|
AreaEval ae = (AreaEval)arg0;
|
|
w1 = ae.Width;
|
|
h1 = ae.Height;
|
|
a1FirstCol = ae.FirstColumn;
|
|
a1FirstRow = ae.FirstRow;
|
|
}
|
|
else if(arg0 is RefEval)
|
|
{
|
|
RefEval ref1 = (RefEval)arg0;
|
|
w1 = 1;
|
|
h1 = 1;
|
|
a1FirstCol = ref1.Column;
|
|
a1FirstRow = ref1.Row;
|
|
}
|
|
else
|
|
{
|
|
w1 = 1;
|
|
h1 = 1;
|
|
}
|
|
w2 = 1;
|
|
h2 = 1;
|
|
|
|
int width = Math.Max(w1, w2);
|
|
int height = Math.Max(h1, h2);
|
|
|
|
ValueEval[] vals = new ValueEval[height * width];
|
|
|
|
int idx = 0;
|
|
for(int i = 0; i < height; i++)
|
|
{
|
|
for(int j = 0; j < width; j++)
|
|
{
|
|
ValueEval vA;
|
|
try
|
|
{
|
|
vA = OperandResolver.GetSingleValue(arg0, a1FirstRow + i, a1FirstCol + j);
|
|
}
|
|
catch(FormulaParseException e)
|
|
{
|
|
vA = ErrorEval.NAME_INVALID;
|
|
}
|
|
catch(EvaluationException e)
|
|
{
|
|
vA = e.GetErrorEval();
|
|
}
|
|
catch(RuntimeException e)
|
|
{
|
|
if(e.Message.StartsWith("Don't know how to evaluate name"))
|
|
{
|
|
vA = ErrorEval.NAME_INVALID;
|
|
}
|
|
else
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
vals[idx++] = evalFunc.Invoke(vA);
|
|
}
|
|
}
|
|
|
|
if(vals.Length == 1)
|
|
{
|
|
return vals[0];
|
|
}
|
|
|
|
return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + height - 1, srcColumnIndex + width - 1, vals);
|
|
}
|
|
}
|
|
}
|