/*
* Licensed to the Apache Software Foundation (ASF) Under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for Additional information regarding copyright ownership.
* The ASF licenses this file to You Under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed Under the License is distributed on an "AS Is" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations Under the License.
*/
namespace HH.WMS.Utils.NPOI.SS.Formula.Functions
{
using System;
using System.Collections.Generic;
using HH.WMS.Utils.NPOI.SS.Formula.Eval;
using HH.WMS.Utils.NPOI.SS.Formula;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
* This Is the base class for all excel function evaluator
* classes that take variable number of operands, and
* where the order of operands does not matter
*/
public abstract class MultiOperandNumericFunction : Function
{
static double[] EMPTY_DOUBLE_ARRAY = { };
private bool _isReferenceBoolCounted;
private bool _isBlankCounted;
protected MultiOperandNumericFunction(bool isReferenceBoolCounted, bool isBlankCounted)
{
_isReferenceBoolCounted = isReferenceBoolCounted;
_isBlankCounted = isBlankCounted;
}
protected internal abstract double Evaluate(double[] values);
public ValueEval Evaluate(ValueEval[] args, int srcCellRow, int srcCellCol)
{
double d;
try
{
double[] values = GetNumberArray(args);
d = Evaluate(values);
}
catch (EvaluationException e)
{
return e.GetErrorEval();
}
if (Double.IsNaN(d) || Double.IsInfinity(d))
return ErrorEval.NUM_ERROR;
return new NumberEval(d);
}
private class DoubleList
{
private double[] _array;
private int _Count;
public DoubleList()
{
_array = new double[8];
_Count = 0;
}
public double[] ToArray()
{
if (_Count < 1)
{
return EMPTY_DOUBLE_ARRAY;
}
double[] result = new double[_Count];
Array.Copy(_array, 0, result, 0, _Count);
return result;
}
public void Add(double[] values)
{
int AddLen = values.Length;
EnsureCapacity(_Count + AddLen);
Array.Copy(values, 0, _array, _Count, AddLen);
_Count += AddLen;
}
private void EnsureCapacity(int reqSize)
{
if (reqSize > _array.Length)
{
int newSize = reqSize * 3 / 2; // grow with 50% extra
double[] newArr = new double[newSize];
Array.Copy(_array, 0, newArr, 0, _Count);
_array = newArr;
}
}
public void Add(double value)
{
EnsureCapacity(_Count + 1);
_array[_Count] = value;
_Count++;
}
}
private static int DEFAULT_MAX_NUM_OPERANDS = 30;
/**
* Maximum number of operands accepted by this function.
* Subclasses may override to Change default value.
*/
protected int MaxNumOperands
{
get
{
return DEFAULT_MAX_NUM_OPERANDS;
}
}
/**
* Whether to count nested subtotals.
*/
public virtual bool IsSubtotalCounted
{
get
{
return true;
}
}
/**
* Collects values from a single argument
*/
private void CollectValues(ValueEval operand, DoubleList temp)
{
if (operand is TwoDEval)
{
TwoDEval ae = (TwoDEval)operand;
int width = ae.Width;
int height = ae.Height;
for (int rrIx = 0; rrIx < height; rrIx++)
{
for (int rcIx = 0; rcIx < width; rcIx++)
{
ValueEval ve = ae.GetValue(rrIx, rcIx);
if (!IsSubtotalCounted && ae.IsSubTotal(rrIx, rcIx)) continue;
CollectValue(ve, true, temp);
}
}
return;
}
if (operand is RefEval)
{
RefEval re = (RefEval)operand;
CollectValue(re.InnerValueEval, true, temp);
return;
}
CollectValue((ValueEval)operand, false, temp);
}
private void CollectValue(ValueEval ve, bool isViaReference, DoubleList temp)
{
if (ve == null)
{
throw new ArgumentException("ve must not be null");
}
if (ve is NumberEval)
{
NumberEval ne = (NumberEval)ve;
temp.Add(ne.NumberValue);
return;
}
if (ve is ErrorEval)
{
throw new EvaluationException((ErrorEval)ve);
}
if (ve is StringEval)
{
if (isViaReference)
{
// ignore all ref strings
return;
}
String s = ((StringEval)ve).StringValue;
Double d = OperandResolver.ParseDouble(s);
if (double.IsNaN(d))
{
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
temp.Add(d);
return;
}
if (ve is BoolEval)
{
if (!isViaReference || _isReferenceBoolCounted)
{
BoolEval boolEval = (BoolEval)ve;
temp.Add(boolEval.NumberValue);
}
return;
}
if (ve == BlankEval.instance)
{
if (_isBlankCounted)
{
temp.Add(0.0);
}
return;
}
throw new InvalidOperationException("Invalid ValueEval type passed for conversion: ("
+ ve.GetType() + ")");
}
/**
* Returns a double array that contains values for the numeric cells
* from among the list of operands. Blanks and Blank equivalent cells
* are ignored. Error operands or cells containing operands of type
* that are considered invalid and would result in #VALUE! error in
* excel cause this function to return null.
*
* @return never null
*/
protected double[] GetNumberArray(ValueEval[] operands)
{
if (operands.Length > MaxNumOperands)
{
throw EvaluationException.InvalidValue();
}
DoubleList retval = new DoubleList();
for (int i = 0, iSize = operands.Length; i < iSize; i++)
{
CollectValues(operands[i], retval);
}
return retval.ToArray();
}
/**
* Ensures that a two dimensional array has all sub-arrays present and the same Length
* @return false if any sub-array Is missing, or Is of different Length
*/
protected static bool AreSubArraysConsistent(double[][] values)
{
if (values == null || values.Length < 1)
{
// TODO this doesn't seem right. Fix or Add comment.
return true;
}
if (values[0] == null)
{
return false;
}
int outerMax = values.Length;
int innerMax = values[0].Length;
for (int i = 1; i < outerMax; i++)
{ // note - 'i=1' start at second sub-array
double[] subArr = values[i];
if (subArr == null)
{
return false;
}
if (innerMax != subArr.Length)
{
return false;
}
}
return true;
}
}
}