Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/EPPlus/Core/CellStore/RangeHashset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ internal bool Merge(ref FormulaRangeAddress newAddress)
{
var spillRanges = new List<long>();
byte isAdded = 0;
if(newAddress.FromCol < 1 || newAddress.FromRow < 1)
{
return false;
}
for (int c = newAddress.FromCol; c <= newAddress.ToCol; c++)
{
var rowSpan = (((long)newAddress.FromRow - 1) << 20) | ((long)newAddress.ToRow - 1);
Expand Down
32 changes: 16 additions & 16 deletions src/EPPlus/Core/Worksheet/WorksheetCopyHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,11 @@ internal static ExcelWorksheet Copy(ExcelWorksheets targetWorksheets, string nam
CopySlicers(sourceWorksheet, targetWorksheet);
CopyDrawing(sourceWorksheet, targetWorksheet);
}
List<KeyValuePair<string, string>> copiedTableNames = null;
Dictionary<string, string> copiedTableNames = null, copiedPivotTableNames=null;
if (sourceWorksheet.Tables.Count > 0)
{
copiedTableNames = CopyTable(sourceWorksheet, targetWorksheet);
}
copiedTableNames = CopyTable(sourceWorksheet, targetWorksheet); }

Dictionary<string, string> copiedPivotTableNames = null;
if (sourceWorksheet.PivotTables.Count > 0)
{
copiedPivotTableNames = CopyPivotTable(sourceWorksheet, targetWorksheet);
Expand All @@ -143,7 +141,7 @@ internal static ExcelWorksheet Copy(ExcelWorksheets targetWorksheets, string nam
//Copy dfx styles used in conditional formatting.
if (!(sourceWorksheet.Workbook == targetWorksheet.Workbook))
{
CopyDxfStyles(sourceWorksheet, targetWorksheet);
CopyDxfStyles(sourceWorksheet, targetWorksheet, copiedTableNames, copiedPivotTableNames);
}

//Copy the VBA code
Expand Down Expand Up @@ -193,7 +191,7 @@ internal static ExcelWorksheet Copy(ExcelWorksheets targetWorksheets, string nam
return targetWorksheet;
}

private static void ApplyTableCopyOptions(ExcelWorksheet added, ExcelWorksheetCopyOptions options, List<KeyValuePair<string, string>> copiedTableNames)
private static void ApplyTableCopyOptions(ExcelWorksheet added, ExcelWorksheetCopyOptions options, Dictionary<string, string> copiedTableNames)
{
if (options == null || options.TableCopyHandler == null || copiedTableNames == null)
{
Expand Down Expand Up @@ -1022,6 +1020,7 @@ private static void CopyDefinedNames(ExcelWorksheet Copy, ExcelWorksheet added)
wbName.IsNameHidden = name.IsNameHidden;
}
}

//Copy names from formulas.
if (sameWorkbook == false)
{
Expand Down Expand Up @@ -1101,9 +1100,9 @@ private static bool HasExternalReference(string formula)
return false;
}

private static List<KeyValuePair<string, string>> CopyTable(ExcelWorksheet sourceWs, ExcelWorksheet destWs)
private static Dictionary<string, string> CopyTable(ExcelWorksheet sourceWs, ExcelWorksheet destWs)
{
var copiedTableNames = new List<KeyValuePair<string, string>>();
var copiedTableNames = new Dictionary<string, string>();
string prevName = "";
//First copy the table XML
foreach (var tbl in sourceWs.Tables)
Expand Down Expand Up @@ -1137,7 +1136,7 @@ private static List<KeyValuePair<string, string>> CopyTable(ExcelWorksheet sourc

int Id = destWs.Workbook._nextTableID++;
prevName = name;
copiedTableNames.Add(new KeyValuePair<string, string>(tbl.Name, name));
copiedTableNames.Add(tbl.Name, name);

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);
Expand Down Expand Up @@ -1310,6 +1309,7 @@ private static Dictionary<string, string> CopyPivotTable(ExcelWorksheet sourceWs
}
//Can't have a cell selected when "group editing" avoids pop-up by not selecting sheet.
destWs.View.SetTabSelected(false);

return nameMap;
}

Expand Down Expand Up @@ -1381,32 +1381,32 @@ private static void ChangeToWsLocalPivotTable(ExcelWorksheet sourceWs, Dictionar
}
}
}
private static void CopyDxfStyles(ExcelWorksheet sourceWs, ExcelWorksheet destWs)
private static void CopyDxfStyles(ExcelWorksheet sourceWs, ExcelWorksheet destWs, Dictionary<string, string> copiedTableNames, Dictionary<string, string> copiedPivotTableNames)
{
//DxfStyleHandler.UpdateDxfXml(copy.Workbook);

var dxfStyleCashe = new Dictionary<int, int>();
CopyDxfStylesTables(sourceWs, destWs);
CopyDxfStylesPivotTables(sourceWs, destWs, dxfStyleCashe);
CopyDxfStylesTables(sourceWs, destWs, copiedTableNames);
CopyDxfStylesPivotTables(sourceWs, destWs, dxfStyleCashe, copiedPivotTableNames);
CopyDxfStylesConditionalFormatting(sourceWs, destWs, dxfStyleCashe);
}
private static void CopyDxfStylesTables(ExcelWorksheet sourceWs, ExcelWorksheet destWs)
private static void CopyDxfStylesTables(ExcelWorksheet sourceWs, ExcelWorksheet destWs, Dictionary<string, string> copiedTableNames)
{
//Table formats
for (int i = 0; i < sourceWs.Tables.Count; i++)
{
var tblFrom = sourceWs.Tables[i];
var tblTo = destWs.Tables[i]; //Use Name, as id can differ if the worksheets are in different workbooks.
var tblTo = destWs.Tables[copiedTableNames[tblFrom.Name]]; //Use Name, as id can differ if the worksheets are in different workbooks.
DxfStyleHandler.CopyDxfStylesTable(tblFrom, tblTo);
}
}
private static void CopyDxfStylesPivotTables(ExcelWorksheet sourceWs, ExcelWorksheet destWs, Dictionary<int, int> dxfStyleCache)
private static void CopyDxfStylesPivotTables(ExcelWorksheet sourceWs, ExcelWorksheet destWs, Dictionary<int, int> dxfStyleCache, Dictionary<string, string> copiedPivotTableNames)
{
//Table formats
foreach (var pt in sourceWs.PivotTables)
{
var ix = 0;
var newPt = destWs.PivotTables[pt.Name];
var newPt = destWs.PivotTables[copiedPivotTableNames[pt.Name]];
foreach (var a in pt.Styles._list)
{
var addedStyle = newPt.Styles[ix++];
Expand Down
2 changes: 1 addition & 1 deletion src/EPPlus/FormulaParsing/CalculateExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public static void Calculate(this ExcelWorkbook workbook, ExcelCalculationOption
try
{
#endif
var dc =RpnFormulaExecution.Execute(workbook, options);
var dc = RpnFormulaExecution.Execute(workbook, options);
dc._parsingContext.RangeCriteriaCache?.Clear();
if (workbook.FormulaParser.Logger != null)
{
Expand Down
77 changes: 67 additions & 10 deletions src/EPPlus/FormulaParsing/DependencyChain/RpnFormulaExecution.cs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ private static CompileResult CalculateFormulaChain(RpnOptimizedDependencyChain d
rd?.Merge(f._row, f._column);
depChain.StartOfChain();
}

ExecuteFormula:
try
{
Expand Down Expand Up @@ -497,15 +498,16 @@ private static CompileResult CalculateFormulaChain(RpnOptimizedDependencyChain d
addresses = f._expressions[f._tokenIndex].GetAddress();
}
depChain.AddFormulaToChain(f, addresses);

if (GetAddressesToFollow(depChain, f, options, ref addresses, ref rd, ref ws))
{
goto FollowChain;
}

f._tokenIndex++;
goto ExecuteFormula;
}
}

CompileResult cr;
if (f._tokenIndex == int.MaxValue) //int.MaxValue means we have an invalid formulas and we should return a name error
{
Expand All @@ -518,8 +520,8 @@ private static CompileResult CalculateFormulaChain(RpnOptimizedDependencyChain d

if (cr != null && f.IsLambda == false && (writeToCell || depChain._formulaStack.Count > 0)) // If calculating single cell via the FormulaParser.Parse method we should not write to the cells
{
SetValueToWorkbook(depChain, f, rd, cr, options, ref depChainPos);
SetValueToWorkbook(depChain, f, cr, options, ref depChainPos);

//We are in a dirty cell recalculation and have a new position in the chain.
//We should return to the caller and let it continue from the new position in the chain.
//We use this technique to avoid stack overflow exceptions when recalculating dirty cells with long dependency chains.
Expand All @@ -543,6 +545,7 @@ private static CompileResult CalculateFormulaChain(RpnOptimizedDependencyChain d
f._tokenIndex++;
goto ExecuteFormula;
}

rd = AddOrGetRDFromWsIx(depChain, f._enumeratorWorksheetIx);
goto NextFormula;
}
Expand All @@ -565,7 +568,9 @@ private static CompileResult CalculateFormulaChain(RpnOptimizedDependencyChain d
{
if (depChain.processedCells.Contains(ExcelCellBase.GetCellId(ws?.IndexInList ?? ushort.MaxValue, firstAddress.FromRow, firstAddress.FromCol)) == false)
{

rd?.Merge(firstAddress.FromRow, firstAddress.FromCol);

if (ws._formulas.Exists(firstAddress.FromRow, firstAddress.FromCol, ref v) && v != null)
{
depChain._formulaStack.Push(f);
Expand All @@ -574,6 +579,7 @@ private static CompileResult CalculateFormulaChain(RpnOptimizedDependencyChain d
}
}
f._tokenIndex++;

goto ExecuteFormula;
}
else
Expand Down Expand Up @@ -605,7 +611,6 @@ private static CompileResult CalculateFormulaChain(RpnOptimizedDependencyChain d
goto NextFormula;
}
}

MergeToRd(rd, row, col, rPos, fe, true);

f._formulaEnumerator = null;
Expand Down Expand Up @@ -655,6 +660,12 @@ private static bool GetAddressesToFollow(RpnOptimizedDependencyChain depChain, R
var needsClean = false;
for (int i = 0; i < addresses.Length; i++)
{
if (addresses[i].FromRow<1 || addresses[i].FromCol<1)
{
addresses[i] = null;
needsClean = true;
continue;
}
var address = addresses[i].Clone();
if (address.ExternalReferenceIx > 0) //We don't follow dep chain into external references.
{
Expand Down Expand Up @@ -739,7 +750,7 @@ private static void CheckAndClearRichData(RpnFormula f)
}
f._ws._metadataStore.Clear(f._row, f._column, 1, 1);
}
private static void SetValueToWorkbook(RpnOptimizedDependencyChain depChain, RpnFormula f, RangeHashset rd, CompileResult cr, ExcelCalculationOption options, ref int insertDepChainPos)
private static void SetValueToWorkbook(RpnOptimizedDependencyChain depChain, RpnFormula f/*, RangeHashset rd*/, CompileResult cr, ExcelCalculationOption options, ref int insertDepChainPos)
{
if(cr.DataType == DataType.LambdaCalculation)
{
Expand All @@ -760,6 +771,7 @@ private static void SetValueToWorkbook(RpnOptimizedDependencyChain depChain, Rpn
}
else
{
var rd = AddOrGetRDFromWsIx(depChain, f._ws.IndexInList);
if ((cr.DataType == DataType.ExcelRange && ((IRangeInfo)cr.Result).Address.IsSingleCell == false)) //A range. When we add support for dynamic array formulas we will alter this.
{
var ri = (IRangeInfo)cr.Result;
Expand All @@ -774,6 +786,7 @@ private static void SetValueToWorkbook(RpnOptimizedDependencyChain depChain, Rpn
{
//Add dynamic array formula support here.
var dirtyRange = ArrayFormulaOutput.FillDynamicArrayFromRangeInfo(f, ri, rd, depChain);

if (dirtyRange != null && dirtyRange.Length > 0)
{
RecalculateDirtyCells(dirtyRange, depChain, rd, options);
Expand All @@ -791,11 +804,14 @@ private static void SetValueToWorkbook(RpnOptimizedDependencyChain depChain, Rpn
(f._flags & FormulaFlags.IsAlwaysDynamic) == FormulaFlags.IsAlwaysDynamic) &&
f.CanBeDynamicArray)
{

var dirtyRange = ArrayFormulaOutput.FillDynamicArraySingleValue(f, cr, rd, depChain);

if (dirtyRange != null && dirtyRange.Length > 0)
{
RecalculateDirtyCells(dirtyRange, depChain, rd, options);
}

depChain.HasAnyArrayFormula = true;
}
else if (cr.ResultType == CompileResultType.LocalImage)
Expand Down Expand Up @@ -1259,9 +1275,11 @@ private static FormulaRangeAddress[] ExecuteNextToken(RpnOptimizedDependencyChai
f._tokenIndex++;
continue;
}

}
return e.GetAddress();
if (t.TokenType == TokenType.CellAddress || t.TokenType == TokenType.ExcelAddress) //Full column and full row addresses will be returned when processing the : operator.
{
return e.GetAddress();
}
}
break;
case TokenType.NameValue:
Expand All @@ -1281,7 +1299,10 @@ private static FormulaRangeAddress[] ExecuteNextToken(RpnOptimizedDependencyChai
{
if (IsSingleAddress(f))
{
return nameAddress;
foreach(var a in nameAddress)
{
return GetCriteriaRange(depChain._parsingContext, f, a);
}
}
}
}
Expand Down Expand Up @@ -1388,7 +1409,7 @@ private static FormulaRangeAddress[] ExecuteNextToken(RpnOptimizedDependencyChai
{
if ((f._funcStack.Count == 0 || ShouldIgnoreAddress(f._funcStack.Peek()) == false) && r.Address != null)
{
return [r.Address.Clone()];
return GetCriteriaRange(depChain._parsingContext, f, r.Address.Clone());
}
}
}
Expand Down Expand Up @@ -1419,7 +1440,7 @@ private static FormulaRangeAddress[] ExecuteNextToken(RpnOptimizedDependencyChai
var cr = s.Peek().Compile();
if (cr.Address != null)
{
return [cr.Address];
return GetCriteriaRange(depChain._parsingContext, f, cr.Address);
}
}

Expand Down Expand Up @@ -1488,6 +1509,42 @@ private static FormulaRangeAddress[] ExecuteNextToken(RpnOptimizedDependencyChai
return null;
}

private static FormulaRangeAddress[] GetCriteriaRange(ParsingContext ctx,RpnFormula f, FormulaRangeAddress address)
{

if (address.ExternalReferenceIx <=0 && f._funcStack.Count > 0)
{
var lfe = f._funcStack.Peek();
var pi = lfe._function.ParametersInfo.GetParameterInfo(lfe._argPos);
if (pi == FunctionParameterInformation.AdjustCriteriaParameterAddress)
{
var q = new Queue<FormulaRangeAddress>();
lfe._function.GetNewParameterAddress(CreateArgumentsForParameterAddress(f, lfe),lfe._argPos, ctx, ref q);
return q.ToArray();
}
}
return [address];
}

private static IList<CompileResult> CreateArgumentsForParameterAddress(RpnFormula f, FunctionExpression fe)
{
var ix = 0;
var l = new List<CompileResult>();
foreach(var e in f.ExpressionStack.Reverse())
{
if (fe._function.ParametersInfo.GetParameterInfo(ix)!=FunctionParameterInformation.AdjustParameterAddress)
{
l.Add(e.Compile());
}
else
{
l.Add(null);
}
ix++;
}
return l;
}

private static ExpressionCondition GetCondition(CompileResult v)
{
if (v.ResultValue is IRangeInfo ri)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,9 @@ public enum FunctionParameterInformation
/// The parameter is a variable which value is calculated by the next parameter.
/// </summary>
IsParameterVariable = 0x80,
/// <summary>
/// A hierarcal criteria
/// </summary>
AdjustCriteriaParameterAddress = 0x100
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ internal class AverageIfs : RangeCriteriaFunction
{
return FunctionParameterInformation.Normal;
}
return FunctionParameterInformation.AdjustParameterAddress;
return FunctionParameterInformation.AdjustCriteriaParameterAddress;
}));

public override void GetNewParameterAddress(IList<CompileResult> args, int index, ParsingContext ctx, ref Queue<FormulaRangeAddress> addresses)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ internal class CountIfs : RangeCriteriaFunction
}
if (argumentIndex % 2 == 0)
{
return FunctionParameterInformation.AdjustParameterAddress;
return FunctionParameterInformation.AdjustCriteriaParameterAddress;
}
return FunctionParameterInformation.IgnoreErrorInPreExecute;
}));
Expand Down
Loading
Loading