Skip to content

Commit 3923675

Browse files
committed
Merge branch 'develop' of https://github.com/EPPlusSoftware/EPPlus into develop
2 parents 34b94e9 + 0d19193 commit 3923675

File tree

6 files changed

+142
-6
lines changed

6 files changed

+142
-6
lines changed

src/EPPlus/FormulaParsing/Excel/Functions/FunctionArgument.cs

+10
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ public bool IsExcelRange
6060
get { return Value != null && Value is EpplusExcelDataProvider.IRangeInfo; }
6161
}
6262

63+
public bool IsEnumerableOfFuncArgs
64+
{
65+
get { return Value != null && Value is IEnumerable<FunctionArgument>; }
66+
}
67+
68+
public IEnumerable<FunctionArgument> ValueAsEnumerableOfFuncArgs
69+
{
70+
get { return Value as IEnumerable<FunctionArgument>; }
71+
}
72+
6373
public bool ValueIsExcelError
6474
{
6575
get { return ExcelErrorValue.Values.IsErrorValue(Value); }

src/EPPlus/FormulaParsing/Excel/Functions/Math/SumIf.cs

+35-6
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,14 @@ public override CompileResult Execute(IEnumerable<FunctionArgument> arguments, P
4646
{
4747
ValidateArguments(arguments, 2);
4848
var argRange = ArgToRangeInfo(arguments, 0);
49-
var criteria = arguments.ElementAt(1).ValueFirst != null ? ArgToString(arguments, 1) : null;
49+
50+
// Criteria can either be a string or an array of strings
51+
var criteria = GetCriteria(arguments.ElementAt(1));
5052
var retVal = 0d;
5153
if (argRange == null)
5254
{
5355
var val = arguments.ElementAt(0).Value;
54-
if (criteria != default(string) && _evaluator.Evaluate(val, criteria))
56+
if (_evaluator.Evaluate(val, criteria))
5557
{
5658
var sumRange = ArgToRangeInfo(arguments, 2);
5759
retVal = arguments.Count() > 2
@@ -71,12 +73,39 @@ public override CompileResult Execute(IEnumerable<FunctionArgument> arguments, P
7173
return CreateResult(retVal, DataType.Decimal);
7274
}
7375

74-
private double CalculateWithSumRange(ExcelDataProvider.IRangeInfo range, string criteria, ExcelDataProvider.IRangeInfo sumRange, ParsingContext context)
76+
private IEnumerable<string> GetCriteria(FunctionArgument criteriaArg)
77+
{
78+
var criteria = new List<string>();
79+
if (criteriaArg.IsEnumerableOfFuncArgs)
80+
{
81+
foreach (var arg in criteriaArg.ValueAsEnumerableOfFuncArgs)
82+
{
83+
criteria.Add(arg.ValueFirstString);
84+
}
85+
}
86+
else if (criteriaArg.IsExcelRange)
87+
{
88+
foreach (var cell in criteriaArg.ValueAsRangeInfo)
89+
{
90+
if (cell.Value != null)
91+
{
92+
criteria.Add(cell.Value.ToString());
93+
}
94+
}
95+
}
96+
else
97+
{
98+
criteria.Add(criteriaArg.ValueFirst != null ? criteriaArg.ValueFirst.ToString() : null);
99+
}
100+
return criteria;
101+
}
102+
103+
private double CalculateWithSumRange(ExcelDataProvider.IRangeInfo range, IEnumerable<string> criteria, ExcelDataProvider.IRangeInfo sumRange, ParsingContext context)
75104
{
76105
var retVal = 0d;
77106
foreach (var cell in range)
78107
{
79-
if (criteria != default(string) && _evaluator.Evaluate(cell.Value, criteria))
108+
if (_evaluator.Evaluate(cell.Value, criteria))
80109
{
81110
var rowOffset = cell.Row - range.Address._fromRow;
82111
var columnOffset = cell.Column - range.Address._fromCol;
@@ -95,12 +124,12 @@ private double CalculateWithSumRange(ExcelDataProvider.IRangeInfo range, string
95124
return retVal;
96125
}
97126

98-
private double CalculateSingleRange(ExcelDataProvider.IRangeInfo range, string expression, ParsingContext context)
127+
private double CalculateSingleRange(ExcelDataProvider.IRangeInfo range, IEnumerable<string> expressions, ParsingContext context)
99128
{
100129
var retVal = 0d;
101130
foreach (var candidate in range)
102131
{
103-
if (expression != default(string) && IsNumeric(candidate.Value) && _evaluator.Evaluate(candidate.Value, expression) && IsNumeric(candidate.Value))
132+
if (IsNumeric(candidate.Value) && _evaluator.Evaluate(candidate.Value, expressions) && IsNumeric(candidate.Value))
104133
{
105134
if (candidate.IsExcelError)
106135
{

src/EPPlus/FormulaParsing/ExcelUtilities/ExpressionEvaluator.cs

+27
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Date Author Change
1111
01/27/2020 EPPlus Software AB Initial release EPPlus 5
1212
*************************************************************************************************/
1313
using System;
14+
using System.Collections.Generic;
15+
using System.Linq;
1416
using System.Text.RegularExpressions;
1517
using OfficeOpenXml.FormulaParsing.Excel.Operators;
1618
using OfficeOpenXml.FormulaParsing.ExpressionGraph;
@@ -92,6 +94,31 @@ public bool TryConvertToDouble(object op, out double d)
9294
return false;
9395
}
9496

97+
/// <summary>
98+
/// Returns true if any of the supplied expressions evaluates to true
99+
/// </summary>
100+
/// <param name="left">The object to evaluate</param>
101+
/// <param name="expressions">The expressions to evaluate the object against</param>
102+
/// <returns>True if any of the supplied expressions evaluates to true</returns>
103+
public bool Evaluate(object left, IEnumerable<string> expressions)
104+
{
105+
if (expressions == null || !expressions.Any()) return false;
106+
foreach(var expression in expressions)
107+
{
108+
if(Evaluate(left, expression))
109+
{
110+
return true;
111+
}
112+
}
113+
return false;
114+
}
115+
116+
/// <summary>
117+
/// Returns true if the supplied expression evaluates to true
118+
/// </summary>
119+
/// <param name="left">The object to evaluate</param>
120+
/// <param name="expression">The expressions to evaluate the object against</param>
121+
/// <returns></returns>
95122
public bool Evaluate(object left, string expression)
96123
{
97124
if (expression == string.Empty)

src/EPPlus/FormulaParsing/LexicalAnalysis/TokenSeparatorHandlers/StringHandler.cs

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public override bool Handle(char c, Token tokenSeparator, TokenizerContext conte
4242
{
4343
context.AppendToCurrentToken(c);
4444
context.ToggleIsInString();
45+
context.AddToken(tokenSeparator);
46+
context.NewToken();
4547
return true;
4648
}
4749
if (context.LastToken != null && context.LastToken.Value.TokenTypeIsSet(TokenType.String))

src/EPPlusTest/FormulaParsing/Excel/Functions/Math/SumIfTests.cs

+55
Original file line numberDiff line numberDiff line change
@@ -343,5 +343,60 @@ public void SumIfShouldHandleBooleanArg()
343343
Assert.AreEqual(1d, sheet.Cells["C1"].Value);
344344
}
345345
}
346+
347+
[TestMethod]
348+
public void SumIfShouldHandleArrayOfCriterias()
349+
{
350+
using (var pck = new ExcelPackage())
351+
{
352+
var sheet = pck.Workbook.Worksheets.Add("test");
353+
sheet.Cells["A1"].Value = "A";
354+
sheet.Cells["A2"].Value = "B";
355+
sheet.Cells["A3"].Value = "A";
356+
sheet.Cells["A4"].Value = "B";
357+
sheet.Cells["A5"].Value = "C";
358+
sheet.Cells["A6"].Value = "B";
359+
360+
sheet.Cells["B1"].Value = 10;
361+
sheet.Cells["B2"].Value = 20;
362+
sheet.Cells["B3"].Value = 10;
363+
sheet.Cells["B4"].Value = 30;
364+
sheet.Cells["B5"].Value = 40;
365+
sheet.Cells["B6"].Value = 10;
366+
367+
sheet.Cells["A9"].Formula = "SUMIF(A1:A6,{\"A\",\"C\"}, B1:B6)";
368+
sheet.Calculate();
369+
Assert.AreEqual(60d, sheet.Cells["A9"].Value);
370+
}
371+
}
372+
373+
[TestMethod]
374+
public void SumIfShouldHandleRangeWithCriterias()
375+
{
376+
using (var pck = new ExcelPackage())
377+
{
378+
var sheet = pck.Workbook.Worksheets.Add("test");
379+
sheet.Cells["A1"].Value = "A";
380+
sheet.Cells["A2"].Value = "B";
381+
sheet.Cells["A3"].Value = "A";
382+
sheet.Cells["A4"].Value = "B";
383+
sheet.Cells["A5"].Value = "C";
384+
sheet.Cells["A6"].Value = "B";
385+
386+
sheet.Cells["B1"].Value = 10;
387+
sheet.Cells["B2"].Value = 20;
388+
sheet.Cells["B3"].Value = 10;
389+
sheet.Cells["B4"].Value = 30;
390+
sheet.Cells["B5"].Value = 40;
391+
sheet.Cells["B6"].Value = 10;
392+
393+
sheet.Cells["D9"].Value = "A";
394+
sheet.Cells["E9"].Value = "C";
395+
396+
sheet.Cells["A9"].Formula = "SUMIF(A1:A6,D9:E9, B1:B6)";
397+
sheet.Calculate();
398+
Assert.AreEqual(60d, sheet.Cells["A9"].Value);
399+
}
400+
}
346401
}
347402
}

src/EPPlusTest/FormulaParsing/LexicalAnalysis/SourceCodeTokenizerTests.cs

+13
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,19 @@ public void ShouldCreateTokensForEnumerableCorrectly()
123123
Assert.IsTrue(tokens[6].TokenTypeIsSet(TokenType.ClosingEnumerable));
124124
}
125125

126+
[TestMethod]
127+
public void ShouldCreateTokensWithStringForEnumerableCorrectly()
128+
{
129+
var input = "{\"1\",\"2\"}";
130+
var tokens = _tokenizer.Tokenize(input).ToArray();
131+
132+
Assert.AreEqual(9, tokens.Count());
133+
Assert.IsTrue(tokens[0].TokenTypeIsSet(TokenType.OpeningEnumerable));
134+
Assert.IsTrue(tokens[1].TokenTypeIsSet(TokenType.String));
135+
Assert.IsTrue(tokens[2].TokenTypeIsSet(TokenType.StringContent));
136+
Assert.IsTrue(tokens[8].TokenTypeIsSet(TokenType.ClosingEnumerable));
137+
}
138+
126139
[TestMethod]
127140
public void ShouldCreateTokensForExcelAddressCorrectly()
128141
{

0 commit comments

Comments
 (0)