Move the FormulaEvaluator code out of scratchpad
[poi.git] / src / java / org / apache / poi / hssf / record / formula / functions / MultiOperandNumericFunction.java
blob0e7cce217e2ebc04e07c99e891ade018e83afb6e
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 package org.apache.poi.hssf.record.formula.functions;
20 import org.apache.poi.hssf.record.formula.eval.AreaEval;
21 import org.apache.poi.hssf.record.formula.eval.BlankEval;
22 import org.apache.poi.hssf.record.formula.eval.Eval;
23 import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
24 import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
25 import org.apache.poi.hssf.record.formula.eval.RefEval;
26 import org.apache.poi.hssf.record.formula.eval.ValueEval;
27 import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
29 /**
30 * @author Amol S. Deshmukh < amolweb at ya hoo dot com >
31 * This is the super class for all excel function evaluator
32 * classes that take variable number of operands, and
33 * where the order of operands does not matter
35 public abstract class MultiOperandNumericFunction extends NumericFunction {
36 static final double[] EMPTY_DOUBLE_ARRAY = { };
38 private static class DoubleList {
39 private double[] _array;
40 private int _count;
42 public DoubleList() {
43 _array = new double[8];
44 _count = 0;
47 public double[] toArray() {
48 if(_count < 1) {
49 return EMPTY_DOUBLE_ARRAY;
51 double[] result = new double[_count];
52 System.arraycopy(_array, 0, result, 0, _count);
53 return result;
56 public void add(double[] values) {
57 int addLen = values.length;
58 ensureCapacity(_count + addLen);
59 System.arraycopy(values, 0, _array, _count, addLen);
60 _count += addLen;
63 private void ensureCapacity(int reqSize) {
64 if(reqSize > _array.length) {
65 int newSize = reqSize * 3 / 2; // grow with 50% extra
66 double[] newArr = new double[newSize];
67 System.arraycopy(_array, 0, newArr, 0, _count);
68 _array = newArr;
72 public void add(double value) {
73 ensureCapacity(_count + 1);
74 _array[_count] = value;
75 _count++;
79 private static final int DEFAULT_MAX_NUM_OPERANDS = 30;
81 protected abstract ValueEvalToNumericXlator getXlator();
83 /**
84 * Maximum number of operands accepted by this function.
85 * Subclasses may override to change default value.
87 protected int getMaxNumOperands() {
88 return DEFAULT_MAX_NUM_OPERANDS;
91 /**
92 * Returns a double array that contains values for the numeric cells
93 * from among the list of operands. Blanks and Blank equivalent cells
94 * are ignored. Error operands or cells containing operands of type
95 * that are considered invalid and would result in #VALUE! error in
96 * excel cause this function to return <code>null</code>.
98 * @param operands
99 * @param srcRow
100 * @param srcCol
102 protected double[] getNumberArray(Eval[] operands, int srcRow, short srcCol) {
103 if (operands.length > getMaxNumOperands()) {
104 return null;
106 DoubleList retval = new DoubleList();
108 for (int i=0, iSize=operands.length; i<iSize; i++) {
109 double[] temp = getNumberArray(operands[i], srcRow, srcCol);
110 if (temp == null) {
111 return null; // error occurred.
113 retval.add(temp);
115 return retval.toArray();
119 * Same as getNumberArray(Eval[], int, short) except that this
120 * takes Eval instead of Eval[].
121 * @param operand
122 * @param srcRow
123 * @param srcCol
125 protected double[] getNumberArray(Eval operand, int srcRow, short srcCol) {
127 if (operand instanceof AreaEval) {
128 AreaEval ae = (AreaEval) operand;
129 ValueEval[] values = ae.getValues();
130 DoubleList retval = new DoubleList();
131 for (int j=0, jSize=values.length; j<jSize; j++) {
133 * TODO: For an AreaEval, we are constructing a RefEval
134 * per element.
135 * For now this is a tempfix solution since this may
136 * require a more generic fix at the level of
137 * HSSFFormulaEvaluator where we store an array
138 * of RefEvals as the "values" array.
140 RefEval re = new Ref2DEval(null, values[j]);
141 ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol);
143 if (ve instanceof NumericValueEval) {
144 NumericValueEval nve = (NumericValueEval) ve;
145 retval.add(nve.getNumberValue());
147 else if (ve instanceof BlankEval) {
148 // note - blanks are ignored, so returned array will be smaller.
150 else {
151 return null; // indicate to calling subclass that error occurred
154 return retval.toArray();
157 // for ValueEvals other than AreaEval
158 ValueEval ve = singleOperandEvaluate(operand, srcRow, srcCol);
160 if (ve instanceof NumericValueEval) {
161 NumericValueEval nve = (NumericValueEval) ve;
162 return new double[] { nve.getNumberValue(), };
165 if (ve instanceof BlankEval) {
166 // ignore blanks
167 return EMPTY_DOUBLE_ARRAY;
169 return null;
173 * Ensures that a two dimensional array has all sub-arrays present and the same length
174 * @return <code>false</code> if any sub-array is missing, or is of different length
176 protected static final boolean areSubArraysConsistent(double[][] values) {
178 if (values == null || values.length < 1) {
179 // TODO this doesn't seem right. Fix or add comment.
180 return true;
183 if (values[0] == null) {
184 return false;
186 int outerMax = values.length;
187 int innerMax = values[0].length;
188 for (int i=1; i<outerMax; i++) { // note - 'i=1' start at second sub-array
189 double[] subArr = values[i];
190 if (subArr == null) {
191 return false;
193 if (innerMax != subArr.length) {
194 return false;
197 return true;