Skip to content

Commit

Permalink
Rewrote handling om exceladdresses in functions. Introduced a cache f…
Browse files Browse the repository at this point in the history
…or addresses, available via ParsingContext. ExcelRanges are now always compiled in ExcelAddressExpression, the SkipArgumentEvaluation parameter is removed, and addresses can be accessed via cache references
  • Loading branch information
swmal committed Dec 27, 2018
1 parent a317905 commit 9b37e2e
Show file tree
Hide file tree
Showing 30 changed files with 344 additions and 140 deletions.
10 changes: 5 additions & 5 deletions EPPlus/FormulaParsing/Excel/Functions/BuiltInFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,14 @@ public BuiltInFunctions()
Functions["vlookup"] = new VLookup();
Functions["lookup"] = new Lookup();
Functions["match"] = new Match();
Functions["row"] = new Row(){SkipArgumentEvaluation = true};
Functions["rows"] = new Rows(){SkipArgumentEvaluation = true};
Functions["column"] = new Column(){SkipArgumentEvaluation = true};
Functions["columns"] = new Columns(){SkipArgumentEvaluation = true};
Functions["row"] = new Row();
Functions["rows"] = new Rows();
Functions["column"] = new Column();
Functions["columns"] = new Columns();
Functions["choose"] = new Choose();
Functions["index"] = new Index();
Functions["indirect"] = new Indirect();
Functions["offset"] = new Offset(){SkipArgumentEvaluation = true};
Functions["offset"] = new Offset();
// Date
Functions["date"] = new Date();
Functions["today"] = new Today();
Expand Down
12 changes: 11 additions & 1 deletion EPPlus/FormulaParsing/Excel/Functions/ExcelFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public virtual bool IsErrorHandlingFunction
/// Used for some Lookupfunctions to indicate that function arguments should
/// not be compiled before the function is called.
/// </summary>
public bool SkipArgumentEvaluation { get; set; }
//public bool SkipArgumentEvaluation { get; set; }
protected object GetFirstValue(IEnumerable<FunctionArgument> val)
{
var arg = ((IEnumerable<FunctionArgument>)val).FirstOrDefault();
Expand Down Expand Up @@ -181,6 +181,16 @@ protected string ArgToAddress(IEnumerable<FunctionArgument> arguments, int index
return arguments.ElementAt(index).IsExcelRange ? arguments.ElementAt(index).ValueAsRangeInfo.Address.FullAddress : ArgToString(arguments, index);
}

protected string ArgToAddress(IEnumerable<FunctionArgument> arguments, int index, ParsingContext context)
{
var arg = arguments.ElementAt(index);
if(arg.ExcelAddressReferenceId > 0)
{
return context.AddressCache.Get(arg.ExcelAddressReferenceId);
}
return ArgToAddress(arguments, index);
}

/// <summary>
/// Returns the value of the argument att the position of the 0-based
/// <paramref name="index"/> as an integer.
Expand Down
2 changes: 2 additions & 0 deletions EPPlus/FormulaParsing/Excel/Functions/FunctionArgument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public Type Type
get { return Value != null ? Value.GetType() : null; }
}

public int ExcelAddressReferenceId { get; set; }

public bool IsExcelRange
{
get { return Value != null && Value is EpplusExcelDataProvider.IRangeInfo; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public override CompileResult Execute(IEnumerable<FunctionArgument> arguments, P
{
return CreateResult(context.Scopes.Current.Address.FromCol, DataType.Integer);
}
var rangeAddress = ArgToString(arguments, 0);
var rangeAddress = ArgToAddress(arguments, 0, context);
if (!ExcelAddressUtil.IsValidAddress(rangeAddress))
throw new ArgumentException("An invalid argument was supplied");
var factory = new RangeAddressFactory(context.ExcelDataProvider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public override CompileResult Execute(IEnumerable<FunctionArgument> arguments, P
}
else
{
var range = ArgToString(arguments, 0);
var range = ArgToAddress(arguments, 0, context);
if (ExcelAddressUtil.IsValidAddress(range))
{
var factory = new RangeAddressFactory(context.ExcelDataProvider);
Expand Down
6 changes: 3 additions & 3 deletions EPPlus/FormulaParsing/Excel/Functions/RefAndLookup/Lookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ private CompileResult HandleSingleRange(IEnumerable<FunctionArgument> arguments,
{
var searchedValue = arguments.ElementAt(0).Value;
Require.That(arguments.ElementAt(1).Value).Named("firstAddress").IsNotNull();
var firstAddress = ArgToString(arguments, 1);
var firstAddress = ArgToAddress(arguments, 1, context);
var rangeAddressFactory = new RangeAddressFactory(context.ExcelDataProvider);
var address = rangeAddressFactory.Create(firstAddress);
var nRows = address.ToRow - address.FromRow;
Expand All @@ -77,8 +77,8 @@ private CompileResult HandleTwoRanges(IEnumerable<FunctionArgument> arguments, P
var searchedValue = arguments.ElementAt(0).Value;
Require.That(arguments.ElementAt(1).Value).Named("firstAddress").IsNotNull();
Require.That(arguments.ElementAt(2).Value).Named("secondAddress").IsNotNull();
var firstAddress = ArgToString(arguments, 1);
var secondAddress = ArgToString(arguments, 2);
var firstAddress = ArgToAddress(arguments, 1, context);
var secondAddress = ArgToAddress(arguments, 2, context);
var rangeAddressFactory = new RangeAddressFactory(context.ExcelDataProvider);
var address1 = rangeAddressFactory.Create(firstAddress);
var address2 = rangeAddressFactory.Create(secondAddress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public override CompileResult Execute(IEnumerable<FunctionArgument> arguments, P
ValidateArguments(arguments, 2);

var searchedValue = arguments.ElementAt(0).Value;
var address = ArgToAddress(arguments,1);
var address = ArgToAddress(arguments,1, context);
var rangeAddressFactory = new RangeAddressFactory(context.ExcelDataProvider);
var rangeAddress = rangeAddressFactory.Create(address);
var matchType = GetMatchType(arguments);
Expand Down
6 changes: 3 additions & 3 deletions EPPlus/FormulaParsing/Excel/Functions/RefAndLookup/Offset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ public override CompileResult Execute(IEnumerable<FunctionArgument> arguments, P
{
var functionArguments = arguments as FunctionArgument[] ?? arguments.ToArray();
ValidateArguments(functionArguments, 3);
var startRange = ArgToAddress(functionArguments, 0);
var startRange = ArgToAddress(functionArguments, 0, context);
var rowOffset = ArgToInt(functionArguments, 1);
var colOffset = ArgToInt(functionArguments, 2);
int width = 0, height = 0;
if (functionArguments.Length > 3)
{
height = ArgToInt(functionArguments, 3);
ThrowExcelErrorValueExceptionIf(() => height == 0, eErrorType.Ref);
if (height == 0) return new CompileResult(eErrorType.Ref);
}
if (functionArguments.Length > 4)
{
width = ArgToInt(functionArguments, 4);
ThrowExcelErrorValueExceptionIf(() => width == 0, eErrorType.Ref);
if (width == 0) return new CompileResult(eErrorType.Ref);
}
var ws = context.Scopes.Current.Address.Worksheet;
var r =context.ExcelDataProvider.GetRange(ws,startRange);
Expand Down
2 changes: 1 addition & 1 deletion EPPlus/FormulaParsing/Excel/Functions/RefAndLookup/Row.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public override CompileResult Execute(IEnumerable<FunctionArgument> arguments, P
{
return CreateResult(context.Scopes.Current.Address.FromRow, DataType.Integer);
}
var rangeAddress = ArgToString(arguments, 0);
var rangeAddress = ArgToAddress(arguments, 0, context);
if (!ExcelAddressUtil.IsValidAddress(rangeAddress))
throw new ArgumentException("An invalid argument was supplied");
var factory = new RangeAddressFactory(context.ExcelDataProvider);
Expand Down
2 changes: 1 addition & 1 deletion EPPlus/FormulaParsing/Excel/Functions/RefAndLookup/Rows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public override CompileResult Execute(IEnumerable<FunctionArgument> arguments, P
}
else
{
var range = ArgToString(arguments, 0);
var range = ArgToAddress(arguments, 0, context);
if (ExcelAddressUtil.IsValidAddress(range))
{
var factory = new RangeAddressFactory(context.ExcelDataProvider);
Expand Down
82 changes: 82 additions & 0 deletions EPPlus/FormulaParsing/ExcelAddressCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace EPPlus.FormulaParsing
{
/// <summary>
/// Caches string by generated id's.
/// </summary>
public class ExcelAddressCache
{
private readonly object _myLock = new object();
private readonly Dictionary<int, string> _addressCache = new Dictionary<int, string>();
private readonly Dictionary<string, int> _lookupCache = new Dictionary<string, int>();
private int _nextId = 1;
private const bool EnableLookupCache = false;

/// <summary>
/// Returns an id to use for caching (when the <see cref="Add"/> method is called)
/// </summary>
/// <returns></returns>
public int GetNewId()
{
lock(_myLock)
{
return _nextId++;
}
}

/// <summary>
/// Adds an address to the cache
/// </summary>
/// <param name="id"></param>
/// <param name="address"></param>
/// <returns></returns>
public bool Add(int id, string address)
{
lock(_myLock)
{
if (_addressCache.ContainsKey(id)) return false;
_addressCache.Add(id, address);
if(EnableLookupCache && !_lookupCache.ContainsKey(address))
_lookupCache.Add(address, id);
return true;
}

}

/// <summary>
/// Number of items in the cache
/// </summary>
public int Count
{
get { return _addressCache.Count; }
}

/// <summary>
/// Returns an address by its cache id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public string Get(int id)
{
if (!_addressCache.ContainsKey(id)) return string.Empty;
return _addressCache[id];
}

/// <summary>
/// Clears the cache
/// </summary>
public void Clear()
{
lock(_myLock)
{
_addressCache.Clear();
_lookupCache.Clear();
_nextId = 1;
}
}

}
}
7 changes: 7 additions & 0 deletions EPPlus/FormulaParsing/ExpressionGraph/CompileResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,5 +185,12 @@ public bool IsDateString
public bool IsResultOfSubtotal { get; set; }

public bool IsHiddenCell { get; set; }

public int ExcelAddressReferenceId { get; set; }

public bool IsResultOfResolvedExcelRange
{
get { return ExcelAddressReferenceId > 0; }
}
}
}
21 changes: 15 additions & 6 deletions EPPlus/FormulaParsing/ExpressionGraph/ExcelAddressExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,23 @@ public override bool IsGroupedExpression

public override CompileResult Compile()
{
if (ParentIsLookupFunction)
//if (ParentIsLookupFunction)
//{
// return new CompileResult(ExpressionString, DataType.ExcelAddress);
//}
//else
//{
// return CompileRangeValues();
//}
var cache = _parsingContext.AddressCache;
var cacheId = cache.GetNewId();
if(!cache.Add(cacheId, ExpressionString))
{
return new CompileResult(ExpressionString, DataType.ExcelAddress);
}
else
{
return CompileRangeValues();
throw new InvalidOperationException("Catastropic error occurred, address caching failed");
}
var compileResult = CompileRangeValues();
compileResult.ExcelAddressReferenceId = cacheId;
return compileResult;
}

private CompileResult CompileRangeValues()
Expand Down
6 changes: 0 additions & 6 deletions EPPlus/FormulaParsing/ExpressionGraph/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,6 @@ public Expression(string expression)
Operator = null;
}

public virtual bool ParentIsLookupFunction
{
get;
set;
}

public virtual bool HasChildren
{
get { return _children.Any(); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,6 @@ public FunctionArgumentExpression(Expression function)
_function = function;
}

public override bool ParentIsLookupFunction
{
get
{
return base.ParentIsLookupFunction;
}
set
{
base.ParentIsLookupFunction = value;
foreach (var child in Children)
{
child.ParentIsLookupFunction = value;
}
}
}

public override bool IsGroupedExpression
{
get { return false; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ namespace OfficeOpenXml.FormulaParsing.ExpressionGraph.FunctionCompilers
{
public class DefaultCompiler : FunctionCompiler
{
public DefaultCompiler(ExcelFunction function)
: base(function)
public DefaultCompiler(ExcelFunction function, ParsingContext context)
: base(function, context)
{

}

public override CompileResult Compile(IEnumerable<Expression> children, ParsingContext context)
public override CompileResult Compile(IEnumerable<Expression> children)
{
var args = new List<FunctionArgument>();
Function.BeforeInvoke(context);
Function.BeforeInvoke(Context);
foreach (var child in children)
{
var compileResult = child.Compile();
Expand All @@ -60,10 +60,10 @@ public override CompileResult Compile(IEnumerable<Expression> children, ParsingC
}
else
{
BuildFunctionArguments(compileResult.Result, args);
BuildFunctionArguments(compileResult, args);
}
}
return Function.Execute(args, context);
return Function.Execute(args, Context);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,21 @@ namespace OfficeOpenXml.FormulaParsing.ExpressionGraph.FunctionCompilers
{
public class ErrorHandlingFunctionCompiler : FunctionCompiler
{
public ErrorHandlingFunctionCompiler(ExcelFunction function)
: base(function)
public ErrorHandlingFunctionCompiler(ExcelFunction function, ParsingContext context)
: base(function, context)
{

}
public override CompileResult Compile(IEnumerable<Expression> children, ParsingContext context)
public override CompileResult Compile(IEnumerable<Expression> children)
{
var args = new List<FunctionArgument>();
Function.BeforeInvoke(context);
Function.BeforeInvoke(Context);
foreach (var child in children)
{
try
{
var arg = child.Compile();
BuildFunctionArguments(arg != null ? arg.Result : null, args);
BuildFunctionArguments(arg != null ? arg : null, args);
}
catch (ExcelErrorValueException efe)
{
Expand All @@ -65,7 +65,7 @@ public override CompileResult Compile(IEnumerable<Expression> children, ParsingC
}

}
return Function.Execute(args, context);
return Function.Execute(args, Context);
}
}
}
Loading

0 comments on commit 9b37e2e

Please sign in to comment.