blob: d53a4acf20e7b23c162ed6e94add1ac8b8f1f992 [file] [log] [blame]
/*******************************************************************************
* You may amend and distribute as you like, but don't remove this header!
*
* EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
* See http://www.codeplex.com/EPPlus for details.
*
* Copyright (C) 2011 Jan Källman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
* If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
*
* All code and executables are provided "as is" with no warranty either express or implied.
* The author accepts no liability for any damage or loss of business that this product may cause.
*
* Code change notes:
*
* Author Change Date
* ******************************************************************************
* Mats Alm Added 2013-03-01 (Prior file history on https://github.com/swmal/ExcelFormulaParser)
*******************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OfficeOpenXml.FormulaParsing.ExpressionGraph;
using OfficeOpenXml.FormulaParsing.Exceptions;
using OfficeOpenXml.Utils;
namespace OfficeOpenXml.FormulaParsing.Excel.Operators
{
public class Operator : IOperator
{
private const int PrecedencePercent = 2;
private const int PrecedenceExp = 4;
private const int PrecedenceMultiplyDevide = 6;
private const int PrecedenceIntegerDivision = 8;
private const int PrecedenceModulus = 10;
private const int PrecedenceAddSubtract = 12;
private const int PrecedenceConcat = 15;
private const int PrecedenceComparison = 25;
private Operator() { }
private Operator(Operators @operator, int precedence, Func<CompileResult, CompileResult, CompileResult> implementation)
{
_implementation = implementation;
_precedence = precedence;
_operator = @operator;
}
private readonly Func<CompileResult, CompileResult, CompileResult> _implementation;
private readonly int _precedence;
private readonly Operators _operator;
int IOperator.Precedence
{
get { return _precedence; }
}
Operators IOperator.Operator
{
get { return _operator; }
}
public CompileResult Apply(CompileResult left, CompileResult right)
{
if (left.Result is ExcelErrorValue)
{
return new CompileResult(left.Result, DataType.ExcelError);
//throw(new ExcelErrorValueException((ExcelErrorValue)left.Result));
}
else if (right.Result is ExcelErrorValue)
{
return new CompileResult(right.Result, DataType.ExcelError);
//throw(new ExcelErrorValueException((ExcelErrorValue)right.Result));
}
return _implementation(left, right);
}
public override string ToString()
{
return "Operator: " + _operator;
}
private static IOperator _plus;
public static IOperator Plus
{
get
{
return _plus ?? (_plus = new Operator(Operators.Plus, PrecedenceAddSubtract, (l, r) =>
{
l = l == null || l.Result == null ? new CompileResult(0, DataType.Integer) : l;
r = r == null || r.Result == null ? new CompileResult(0, DataType.Integer) : r;
ExcelErrorValue errorVal;
if (EitherIsError(l, r, out errorVal))
{
return new CompileResult(errorVal);
}
if (l.DataType == DataType.Integer && r.DataType == DataType.Integer)
{
return new CompileResult(l.ResultNumeric + r.ResultNumeric, DataType.Integer);
}
else if ((l.IsNumeric || l.IsNumericString || l.Result is ExcelDataProvider.IRangeInfo) &&
(r.IsNumeric || r.IsNumericString || r.Result is ExcelDataProvider.IRangeInfo))
{
return new CompileResult(l.ResultNumeric + r.ResultNumeric, DataType.Decimal);
}
return new CompileResult(eErrorType.Value);
}));
}
}
private static IOperator _minus;
public static IOperator Minus
{
get
{
return _minus ?? (_minus = new Operator(Operators.Minus, PrecedenceAddSubtract, (l, r) =>
{
l = l == null || l.Result == null ? new CompileResult(0, DataType.Integer) : l;
r = r == null || r.Result == null ? new CompileResult(0, DataType.Integer) : r;
if (l.DataType == DataType.Integer && r.DataType == DataType.Integer)
{
return new CompileResult(l.ResultNumeric - r.ResultNumeric, DataType.Integer);
}
else if ((l.IsNumeric || l.IsNumericString || l.Result is ExcelDataProvider.IRangeInfo) &&
(r.IsNumeric || r.IsNumericString || r.Result is ExcelDataProvider.IRangeInfo))
{
return new CompileResult(l.ResultNumeric - r.ResultNumeric, DataType.Decimal);
}
return new CompileResult(eErrorType.Value);
}));
}
}
private static IOperator _multiply;
public static IOperator Multiply
{
get
{
return _multiply ?? (_multiply = new Operator(Operators.Multiply, PrecedenceMultiplyDevide, (l, r) =>
{
l = l ?? new CompileResult(0, DataType.Integer);
r = r ?? new CompileResult(0, DataType.Integer);
if (l.DataType == DataType.Integer && r.DataType == DataType.Integer)
{
return new CompileResult(l.ResultNumeric*r.ResultNumeric, DataType.Integer);
}
else if ((l.IsNumeric || l.IsNumericString || l.Result is ExcelDataProvider.IRangeInfo) &&
(r.IsNumeric || r.IsNumericString || r.Result is ExcelDataProvider.IRangeInfo))
{
return new CompileResult(l.ResultNumeric*r.ResultNumeric, DataType.Decimal);
}
return new CompileResult(eErrorType.Value);
}));
}
}
private static IOperator _divide;
public static IOperator Divide
{
get
{
return _divide ?? (_divide = new Operator(Operators.Divide, PrecedenceMultiplyDevide, (l, r) =>
{
if (!(l.IsNumeric || l.IsNumericString || l.Result is ExcelDataProvider.IRangeInfo) ||
!(r.IsNumeric || r.IsNumericString || r.Result is ExcelDataProvider.IRangeInfo))
{
return new CompileResult(eErrorType.Value);
}
var left = l.ResultNumeric;
var right = r.ResultNumeric;
if (Math.Abs(right - 0d) < double.Epsilon)
{
return new CompileResult(eErrorType.Div0);
}
else if ((l.IsNumeric || l.IsNumericString || l.Result is ExcelDataProvider.IRangeInfo) &&
(r.IsNumeric || r.IsNumericString || r.Result is ExcelDataProvider.IRangeInfo))
{
return new CompileResult(left/right, DataType.Decimal);
}
return new CompileResult(eErrorType.Value);
}));
}
}
public static IOperator Exp
{
get
{
return new Operator(Operators.Exponentiation, PrecedenceExp, (l, r) =>
{
if (l == null && r == null)
{
return new CompileResult(eErrorType.Value);
}
l = l ?? new CompileResult(0, DataType.Integer);
r = r ?? new CompileResult(0, DataType.Integer);
if ((l.IsNumeric || l.Result is ExcelDataProvider.IRangeInfo) && (r.IsNumeric || r.Result is ExcelDataProvider.IRangeInfo))
{
return new CompileResult(Math.Pow(l.ResultNumeric, r.ResultNumeric), DataType.Decimal);
}
return new CompileResult(0d, DataType.Decimal);
});
}
}
public static IOperator Concat
{
get
{
return new Operator(Operators.Concat, PrecedenceConcat, (l, r) =>
{
l = l ?? new CompileResult(string.Empty, DataType.String);
r = r ?? new CompileResult(string.Empty, DataType.String);
var lStr = l.Result != null ? l.ResultValue.ToString() : string.Empty;
var rStr = r.Result != null ? r.ResultValue.ToString() : string.Empty;
return new CompileResult(string.Concat(lStr, rStr), DataType.String);
});
}
}
private static IOperator _greaterThan;
public static IOperator GreaterThan
{
get
{
//return new Operator(Operators.GreaterThan, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) > 0, DataType.Boolean));
return _greaterThan ??
(_greaterThan =
new Operator(Operators.LessThanOrEqual, PrecedenceComparison,
(l, r) => Compare(l, r, (compRes) => compRes > 0)));
}
}
private static IOperator _eq;
public static IOperator Eq
{
get
{
//return new Operator(Operators.Equals, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) == 0, DataType.Boolean));
return _eq ??
(_eq =
new Operator(Operators.LessThanOrEqual, PrecedenceComparison,
(l, r) => Compare(l, r, (compRes) => compRes == 0)));
}
}
private static IOperator _notEqualsTo;
public static IOperator NotEqualsTo
{
get
{
//return new Operator(Operators.NotEqualTo, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) != 0, DataType.Boolean));
return _notEqualsTo ??
(_notEqualsTo =
new Operator(Operators.LessThanOrEqual, PrecedenceComparison,
(l, r) => Compare(l, r, (compRes) => compRes != 0)));
}
}
private static IOperator _greaterThanOrEqual;
public static IOperator GreaterThanOrEqual
{
get
{
//return new Operator(Operators.GreaterThanOrEqual, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) >= 0, DataType.Boolean));
return _greaterThanOrEqual ??
(_greaterThanOrEqual =
new Operator(Operators.LessThanOrEqual, PrecedenceComparison,
(l, r) => Compare(l, r, (compRes) => compRes >= 0)));
}
}
private static IOperator _lessThan;
public static IOperator LessThan
{
get
{
//return new Operator(Operators.LessThan, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) < 0, DataType.Boolean));
return _lessThan ??
(_lessThan =
new Operator(Operators.LessThanOrEqual, PrecedenceComparison,
(l, r) => Compare(l, r, (compRes) => compRes < 0)));
}
}
public static IOperator LessThanOrEqual
{
get
{
//return new Operator(Operators.LessThanOrEqual, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) <= 0, DataType.Boolean));
return new Operator(Operators.LessThanOrEqual, PrecedenceComparison, (l, r) => Compare(l, r, (compRes) => compRes <= 0));
}
}
private static IOperator _percent;
public static IOperator Percent
{
get
{
if (_percent == null)
{
_percent = new Operator(Operators.Percent, PrecedencePercent, (l, r) =>
{
l = l ?? new CompileResult(0, DataType.Integer);
r = r ?? new CompileResult(0, DataType.Integer);
if (l.DataType == DataType.Integer && r.DataType == DataType.Integer)
{
return new CompileResult(l.ResultNumeric * r.ResultNumeric, DataType.Integer);
}
else if ((l.IsNumeric || l.Result is ExcelDataProvider.IRangeInfo) && (r.IsNumeric || r.Result is ExcelDataProvider.IRangeInfo))
{
return new CompileResult(l.ResultNumeric * r.ResultNumeric, DataType.Decimal);
}
return new CompileResult(eErrorType.Value);
});
}
return _percent;
}
}
private static object GetObjFromOther(CompileResult obj, CompileResult other)
{
if (obj.Result == null)
{
if (other.DataType == DataType.String) return string.Empty;
else return 0d;
}
return obj.ResultValue;
}
private static CompileResult Compare(CompileResult l, CompileResult r, Func<int, bool> comparison )
{
ExcelErrorValue errorVal;
if (EitherIsError(l, r, out errorVal))
{
return new CompileResult(errorVal);
}
object left, right;
left = GetObjFromOther(l, r);
right = GetObjFromOther(r, l);
if (ConvertUtil.IsNumeric(left) && ConvertUtil.IsNumeric(right))
{
var lnum = ConvertUtil.GetValueDouble(left);
var rnum = ConvertUtil.GetValueDouble(right);
if (Math.Abs(lnum - rnum) < double.Epsilon)
{
return new CompileResult(comparison(0), DataType.Boolean);
}
var comparisonResult = lnum.CompareTo(rnum);
return new CompileResult(comparison(comparisonResult), DataType.Boolean);
}
else
{
var comparisonResult = CompareString(left, right);
return new CompileResult(comparison(comparisonResult), DataType.Boolean);
}
}
private static int CompareString(object l, object r)
{
var sl = (l ?? "").ToString();
var sr = (r ?? "").ToString();
return System.String.Compare(sl, sr, System.StringComparison.Ordinal);
}
private static bool EitherIsError(CompileResult l, CompileResult r, out ExcelErrorValue errorVal)
{
if (l.DataType == DataType.ExcelError)
{
errorVal = (ExcelErrorValue) l.Result;
return true;
}
if (r.DataType == DataType.ExcelError)
{
errorVal = (ExcelErrorValue) r.Result;
return true;
}
errorVal = null;
return false;
}
}
}