View | Details | Raw Unified | Return to bug 48292
Collapse All | Expand All

(-)a/src/java/org/apache/poi/hssf/record/ArrayRecord.java (-1 / +45 lines)
Lines 17-23 Link Here
17
17
18
package org.apache.poi.hssf.record;
18
package org.apache.poi.hssf.record;
19
19
20
import org.apache.poi.hssf.record.formula.AreaPtgBase;
20
import org.apache.poi.hssf.record.formula.Ptg;
21
import org.apache.poi.hssf.record.formula.Ptg;
22
import org.apache.poi.hssf.record.formula.RefPtgBase;
23
import org.apache.poi.hssf.util.CellRangeAddress8Bit;
21
import org.apache.poi.ss.formula.Formula;
24
import org.apache.poi.ss.formula.Formula;
22
import org.apache.poi.util.HexDump;
25
import org.apache.poi.util.HexDump;
23
import org.apache.poi.util.LittleEndianOutput;
26
import org.apache.poi.util.LittleEndianOutput;
Lines 28-33 import org.apache.poi.util.LittleEndianOutput; Link Here
28
 * Treated in a similar way to SharedFormulaRecord
31
 * Treated in a similar way to SharedFormulaRecord
29
 *
32
 *
30
 * @author Josh Micich
33
 * @author Josh Micich
34
 * @author Vladimirs Abramovs(Vladimirs.Abramovs at exigenservices.com) - Array Formula support
31
 */
35
 */
32
public final class ArrayRecord extends SharedValueRecordBase {
36
public final class ArrayRecord extends SharedValueRecordBase {
33
37
Lines 35-41 public final class ArrayRecord extends SharedValueRecordBase { Link Here
35
	private static final int OPT_ALWAYS_RECALCULATE = 0x0001;
39
	private static final int OPT_ALWAYS_RECALCULATE = 0x0001;
36
	private static final int OPT_CALCULATE_ON_OPEN  = 0x0002;
40
	private static final int OPT_CALCULATE_ON_OPEN  = 0x0002;
37
41
38
	private int	_options;
42
	private int _options;
39
	private int _field3notUsed;
43
	private int _field3notUsed;
40
	private Formula _formula;
44
	private Formula _formula;
41
45
Lines 48-53 public final class ArrayRecord extends SharedValueRecordBase { Link Here
48
		_formula = Formula.read(formulaTokenLen, in, totalFormulaLen);
52
		_formula = Formula.read(formulaTokenLen, in, totalFormulaLen);
49
	}
53
	}
50
54
55
	public ArrayRecord(Formula formula, CellRangeAddress8Bit range ) {
56
		super(range);
57
		_options = OPT_CALCULATE_ON_OPEN;
58
		_field3notUsed = 0;
59
		_formula = formula;
60
	}
61
51
	public boolean isAlwaysRecalculate() {
62
	public boolean isAlwaysRecalculate() {
52
		return (_options & OPT_ALWAYS_RECALCULATE) != 0;
63
		return (_options & OPT_ALWAYS_RECALCULATE) != 0;
53
	}
64
	}
Lines 84-87 public final class ArrayRecord extends SharedValueRecordBase { Link Here
84
		sb.append("]");
95
		sb.append("]");
85
		return sb.toString();
96
		return sb.toString();
86
	}
97
	}
98
99
	/**
100
	 * @return the equivalent {@link Ptg} array that the formula would have,
101
	 *         were it not shared.
102
	 */
103
	public Ptg[] getFormulaTokens() {
104
		int formulaRow = this.getFirstRow();
105
		int formulaColumn = this.getLastColumn();
106
107
		// Use SharedFormulaRecord static method to convert formula
108
109
		Ptg[] ptgs = _formula.getTokens();
110
111
		// Convert from relative addressing to absolute
112
		// because all formulas in array need to be referenced to the same
113
		// ref/range
114
		for (int i = 0; i < ptgs.length; i++) {
115
			Ptg ptg = ptgs[i];
116
			if (ptg instanceof AreaPtgBase) {
117
				AreaPtgBase aptg = (AreaPtgBase) ptg;
118
				aptg.setFirstRowRelative(false);
119
				aptg.setLastRowRelative(false);
120
				aptg.setFirstColRelative(false);
121
				aptg.setLastColRelative(false);
122
123
			} else if (ptg instanceof RefPtgBase) {
124
				RefPtgBase rptg = (RefPtgBase) ptg;
125
				rptg.setRowRelative(false);
126
				rptg.setColRelative(false);
127
			}
128
		}
129
		return SharedFormulaRecord.convertSharedFormulas(ptgs, formulaRow, formulaColumn);
130
	}
87
}
131
}
(-)a/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java (-3 / +45 lines)
Lines 17-22 Link Here
17
17
18
package org.apache.poi.hssf.record.aggregates;
18
package org.apache.poi.hssf.record.aggregates;
19
19
20
import org.apache.poi.hssf.record.ArrayRecord;
20
import org.apache.poi.hssf.record.CellValueRecordInterface;
21
import org.apache.poi.hssf.record.CellValueRecordInterface;
21
import org.apache.poi.hssf.record.FormulaRecord;
22
import org.apache.poi.hssf.record.FormulaRecord;
22
import org.apache.poi.hssf.record.Record;
23
import org.apache.poi.hssf.record.Record;
Lines 25-37 import org.apache.poi.hssf.record.SharedFormulaRecord; Link Here
25
import org.apache.poi.hssf.record.StringRecord;
26
import org.apache.poi.hssf.record.StringRecord;
26
import org.apache.poi.hssf.record.formula.ExpPtg;
27
import org.apache.poi.hssf.record.formula.ExpPtg;
27
import org.apache.poi.hssf.record.formula.Ptg;
28
import org.apache.poi.hssf.record.formula.Ptg;
29
import org.apache.poi.hssf.util.CellRangeAddress8Bit;
28
import org.apache.poi.hssf.util.CellReference;
30
import org.apache.poi.hssf.util.CellReference;
31
import org.apache.poi.ss.formula.Formula;
32
import org.apache.poi.ss.util.CellRangeAddress;
29
33
30
/**
34
/**
31
 * The formula record aggregate is used to join together the formula record and it's
35
 * The formula record aggregate is used to join together the formula record and it's
32
 * (optional) string record and (optional) Shared Formula Record (template reads, excel optimization).
36
 * (optional) string record and (optional) Shared Formula Record (template reads, excel optimization).
33
 *
37
 *
34
 * @author Glen Stampoultzis (glens at apache.org)
38
 * @author Glen Stampoultzis (glens at apache.org)
39
 * @author Vladimirs Abramovs(Vladimirs.Abramovs at exigenservices.com) - Array Formula support
35
 */
40
 */
36
public final class FormulaRecordAggregate extends RecordAggregate implements CellValueRecordInterface {
41
public final class FormulaRecordAggregate extends RecordAggregate implements CellValueRecordInterface {
37
42
Lines 181-190 public final class FormulaRecordAggregate extends RecordAggregate implements Cel Link Here
181
	}
186
	}
182
187
183
	public Ptg[] getFormulaTokens() {
188
	public Ptg[] getFormulaTokens() {
184
		if (_sharedFormulaRecord == null) {
189
		if (_sharedFormulaRecord != null) {
185
			return _formulaRecord.getParsedExpression();
190
			return _sharedFormulaRecord.getFormulaTokens(_formulaRecord);
191
		}
192
		CellReference expRef = _formulaRecord.getFormula().getExpReference();
193
		if (expRef != null) {
194
			ArrayRecord arec = _sharedValueManager.getArrayRecord(expRef.getRow(), expRef.getCol());
195
			return arec.getFormulaTokens();
186
		}
196
		}
187
		return _sharedFormulaRecord.getFormulaTokens(_formulaRecord);
197
		return _formulaRecord.getParsedExpression();
188
	}
198
	}
189
199
190
	/**
200
	/**
Lines 216-219 public final class FormulaRecordAggregate extends RecordAggregate implements Cel Link Here
216
			_sharedValueManager.unlink(_sharedFormulaRecord);
226
			_sharedValueManager.unlink(_sharedFormulaRecord);
217
		}
227
		}
218
	}
228
	}
229
	public boolean isPartOfArrayFormula() {
230
		if (_sharedFormulaRecord != null) {
231
			return false;
232
		}
233
		return _formulaRecord.getFormula().getExpReference() != null;
234
	}
235
236
	public CellRangeAddress getArrayFormulaRange() {
237
		if (_sharedFormulaRecord != null) {
238
			throw new IllegalStateException("not an array formula cell.");
239
		}
240
		CellReference expRef = _formulaRecord.getFormula().getExpReference();
241
		if (expRef == null) {
242
			throw new IllegalStateException("not an array formula cell.");
243
		}
244
		ArrayRecord arec = _sharedValueManager.getArrayRecord(expRef.getRow(), expRef.getCol());
245
		CellRangeAddress8Bit a = arec.getRange();
246
		return new CellRangeAddress(a.getFirstRow(), a.getLastRow(), a.getFirstColumn(),a.getLastColumn());
247
	}
248
	public void setArrayFormula(CellRangeAddress r, Ptg[] ptgs) {
249
250
		ArrayRecord arr = new ArrayRecord(Formula.create(ptgs), new CellRangeAddress8Bit(r.getFirstRow(), r.getLastRow(), r.getFirstColumn(), r.getLastColumn()));
251
		_sharedValueManager.addArrayRecord(arr);
252
	}
253
	/**
254
	 * Removes an array formula
255
	 * @return the range of the array formula containing the specified cell. Never <code>null</code>
256
	 */
257
	public CellRangeAddress removeArrayFormula(int rowIndex, int columnIndex) {
258
		CellRangeAddress8Bit a = _sharedValueManager.removeArrayFormula(rowIndex, columnIndex);
259
		return new CellRangeAddress(a.getFirstRow(), a.getLastRow(), a.getFirstColumn(), a.getLastColumn());
260
	}
219
}
261
}
(-)a/src/java/org/apache/poi/hssf/record/aggregates/SharedValueManager.java (-7 / +51 lines)
Lines 17-25 Link Here
17
17
18
package org.apache.poi.hssf.record.aggregates;
18
package org.apache.poi.hssf.record.aggregates;
19
19
20
import java.util.ArrayList;
20
import java.util.Arrays;
21
import java.util.Arrays;
21
import java.util.Comparator;
22
import java.util.Comparator;
22
import java.util.HashMap;
23
import java.util.HashMap;
24
import java.util.List;
23
import java.util.Map;
25
import java.util.Map;
24
26
25
import org.apache.poi.hssf.record.ArrayRecord;
27
import org.apache.poi.hssf.record.ArrayRecord;
Lines 41-46 import org.apache.poi.ss.util.CellReference; Link Here
41
 * </ul>
43
 * </ul>
42
 *
44
 *
43
 * @author Josh Micich
45
 * @author Josh Micich
46
 * @author Vladimirs Abramovs(Vladimirs.Abramovs at exigenservices.com) - handling of ArrayRecords
44
 */
47
 */
45
public final class SharedValueManager {
48
public final class SharedValueManager {
46
49
Lines 111-117 public final class SharedValueManager { Link Here
111
114
112
	public static final SharedValueManager EMPTY = new SharedValueManager(
115
	public static final SharedValueManager EMPTY = new SharedValueManager(
113
			new SharedFormulaRecord[0], new CellReference[0], new ArrayRecord[0], new TableRecord[0]);
116
			new SharedFormulaRecord[0], new CellReference[0], new ArrayRecord[0], new TableRecord[0]);
114
	private final ArrayRecord[] _arrayRecords;
117
	private final List<ArrayRecord> _arrayRecords;
115
	private final TableRecord[] _tableRecords;
118
	private final TableRecord[] _tableRecords;
116
	private final Map<SharedFormulaRecord, SharedFormulaGroup> _groupsBySharedFormulaRecord;
119
	private final Map<SharedFormulaRecord, SharedFormulaGroup> _groupsBySharedFormulaRecord;
117
	/** cached for optimization purposes */
120
	/** cached for optimization purposes */
Lines 123-129 public final class SharedValueManager { Link Here
123
		if (nShF != firstCells.length) {
126
		if (nShF != firstCells.length) {
124
			throw new IllegalArgumentException("array sizes don't match: " + nShF + "!=" + firstCells.length + ".");
127
			throw new IllegalArgumentException("array sizes don't match: " + nShF + "!=" + firstCells.length + ".");
125
		}
128
		}
126
		_arrayRecords = arrayRecords;
129
		_arrayRecords = toList(arrayRecords);
127
		_tableRecords = tableRecords;
130
		_tableRecords = tableRecords;
128
		Map<SharedFormulaRecord, SharedFormulaGroup> m = new HashMap<SharedFormulaRecord, SharedFormulaGroup>(nShF * 3 / 2);
131
		Map<SharedFormulaRecord, SharedFormulaGroup> m = new HashMap<SharedFormulaRecord, SharedFormulaGroup>(nShF * 3 / 2);
129
		for (int i = 0; i < nShF; i++) {
132
		for (int i = 0; i < nShF; i++) {
Lines 133-138 public final class SharedValueManager { Link Here
133
		_groupsBySharedFormulaRecord = m;
136
		_groupsBySharedFormulaRecord = m;
134
	}
137
	}
135
138
139
	private static <Z> List<Z> toList(Z[] zz) {
140
		List<Z> result = new ArrayList<Z>(zz.length);
141
		for (int i = 0; i < zz.length; i++) {
142
			result.add(zz[i]);
143
		}
144
		return result;
145
	}
146
136
	/**
147
	/**
137
	 * @param firstCells
148
	 * @param firstCells
138
	 * @param recs list of sheet records (possibly contains records for other parts of the Excel file)
149
	 * @param recs list of sheet records (possibly contains records for other parts of the Excel file)
Lines 248-261 public final class SharedValueManager { Link Here
248
		// The first cell will be the top left in the range.  So we can match the
259
		// The first cell will be the top left in the range.  So we can match the
249
		// ARRAY/TABLE record directly.
260
		// ARRAY/TABLE record directly.
250
261
251
		for (int i = 0; i < _tableRecords.length; i++) {
262
		for (TableRecord tr : _tableRecords) {
252
			TableRecord tr = _tableRecords[i];
253
			if (tr.isFirstCell(row, column)) {
263
			if (tr.isFirstCell(row, column)) {
254
				return tr;
264
				return tr;
255
			}
265
			}
256
		}
266
		}
257
		for (int i = 0; i < _arrayRecords.length; i++) {
267
		for (ArrayRecord ar : _arrayRecords) {
258
			ArrayRecord ar = _arrayRecords[i];
259
			if (ar.isFirstCell(row, column)) {
268
			if (ar.isFirstCell(row, column)) {
260
				return ar;
269
				return ar;
261
			}
270
			}
Lines 269-278 public final class SharedValueManager { Link Here
269
	 */
278
	 */
270
	public void unlink(SharedFormulaRecord sharedFormulaRecord) {
279
	public void unlink(SharedFormulaRecord sharedFormulaRecord) {
271
		SharedFormulaGroup svg = _groupsBySharedFormulaRecord.remove(sharedFormulaRecord);
280
		SharedFormulaGroup svg = _groupsBySharedFormulaRecord.remove(sharedFormulaRecord);
272
		_groups = null; // be sure to reset cached value
273
		if (svg == null) {
281
		if (svg == null) {
274
			throw new IllegalStateException("Failed to find formulas for shared formula");
282
			throw new IllegalStateException("Failed to find formulas for shared formula");
275
		}
283
		}
284
		_groups = null; // be sure to reset cached value
276
		svg.unlinkSharedFormulas();
285
		svg.unlinkSharedFormulas();
277
	}
286
	}
287
288
	/**
289
	 * Add specified Array Record.
290
	 */
291
	public void addArrayRecord(ArrayRecord ar) {
292
		// could do a check here to make sure none of the ranges overlap
293
		_arrayRecords.add(ar);
294
	}
295
296
	/**
297
	 * Removes the {@link ArrayRecord} for the cell group containing the specified cell.
298
	 * The caller should clear (set blank) all cells in the returned range.
299
	 * @return the range of the array formula which was just removed. Never <code>null</code>.
300
	 */
301
	public CellRangeAddress8Bit removeArrayFormula(int rowIndex, int columnIndex) {
302
		for (ArrayRecord ar : _arrayRecords) {
303
			if (ar.isInRange(rowIndex, columnIndex)) {
304
				_arrayRecords.remove(ar);
305
				return ar.getRange();
306
			}
307
		}
308
		throw new IllegalArgumentException("Specified cell is not part of an array formula.");
309
	}
310
311
	/**
312
	 * @return the shared ArrayRecord identified by (firstRow, firstColumn). never <code>null</code>.
313
	 */
314
	public ArrayRecord getArrayRecord(int firstRow, int firstColumn) {
315
		for(ArrayRecord ar : _arrayRecords) {
316
			if(ar.isFirstCell(firstRow, firstColumn)) {
317
				return ar;
318
			}
319
		}
320
		return null;
321
	}
278
}
322
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java (-1 / +6 lines)
Lines 25-34 import org.apache.poi.hssf.record.formula.eval.ValueEval; Link Here
25
25
26
/**
26
/**
27
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
27
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
28
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com) - array support;
28
 */
29
 */
29
public abstract class AggregateFunction extends MultiOperandNumericFunction {
30
public abstract class AggregateFunction extends MultiOperandNumericFunction {
30
31
31
	private static final class LargeSmall extends Fixed2ArgFunction {
32
	private static final class LargeSmall extends Fixed2ArgFunction implements FunctionWithArraySupport {
32
		private final boolean _isLarge;
33
		private final boolean _isLarge;
33
		protected LargeSmall(boolean isLarge) {
34
		protected LargeSmall(boolean isLarge) {
34
			_isLarge = isLarge;
35
			_isLarge = isLarge;
Lines 66-71 public abstract class AggregateFunction extends MultiOperandNumericFunction { Link Here
66
67
67
			return new NumberEval(result);
68
			return new NumberEval(result);
68
		}
69
		}
70
71
		public boolean supportArray(int paramIndex) {
72
			return paramIndex != 1;
73
		}
69
	}
74
	}
70
	private static final class ValueCollector extends MultiOperandNumericFunction {
75
	private static final class ValueCollector extends MultiOperandNumericFunction {
71
		private static final ValueCollector instance = new ValueCollector();
76
		private static final ValueCollector instance = new ValueCollector();
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/ArrayMode.java (+30 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.hssf.record.formula.functions;
19
20
import org.apache.poi.hssf.record.formula.eval.ValueEval;
21
22
/**
23
 * Interface for those functions that behaves differently in array formula
24
 *
25
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com)
26
 */
27
public interface ArrayMode {
28
29
	ValueEval evaluateInArrayFormula(ValueEval[] args, int srcRowIndex, int srcColumnIndex);
30
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/BooleanFunction.java (-1 / +5 lines)
Lines 36-42 import org.apache.poi.ss.formula.TwoDEval; Link Here
36
 *
36
 *
37
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
37
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
38
 */
38
 */
39
public abstract class BooleanFunction implements Function {
39
public abstract class BooleanFunction implements FunctionWithArraySupport {
40
40
41
	public final ValueEval evaluate(ValueEval[] args, int srcRow, int srcCol) {
41
	public final ValueEval evaluate(ValueEval[] args, int srcRow, int srcCol) {
42
		if (args.length < 1) {
42
		if (args.length < 1) {
Lines 51-56 public abstract class BooleanFunction implements Function { Link Here
51
		return BoolEval.valueOf(boolResult);
51
		return BoolEval.valueOf(boolResult);
52
	}
52
	}
53
53
54
	public boolean supportArray(int paramIndex) {
55
		return true;
56
	}
57
54
	private boolean calculate(ValueEval[] args) throws EvaluationException {
58
	private boolean calculate(ValueEval[] args) throws EvaluationException {
55
59
56
		boolean result = getInitialResultValue();
60
		boolean result = getInitialResultValue();
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Column.java (-1 / +14 lines)
Lines 22-29 import org.apache.poi.hssf.record.formula.eval.ErrorEval; Link Here
22
import org.apache.poi.hssf.record.formula.eval.NumberEval;
22
import org.apache.poi.hssf.record.formula.eval.NumberEval;
23
import org.apache.poi.hssf.record.formula.eval.RefEval;
23
import org.apache.poi.hssf.record.formula.eval.RefEval;
24
import org.apache.poi.hssf.record.formula.eval.ValueEval;
24
import org.apache.poi.hssf.record.formula.eval.ValueEval;
25
import org.apache.poi.ss.formula.ArrayEval;
25
26
26
public final class Column implements Function0Arg, Function1Arg {
27
public final class Column implements Function0Arg, Function1Arg, ArrayMode {
27
28
28
    public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
29
    public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
29
        return new NumberEval(srcColumnIndex+1);
30
        return new NumberEval(srcColumnIndex+1);
Lines 51-54 public final class Column implements Function0Arg, Function1Arg { Link Here
51
        }
52
        }
52
        return ErrorEval.VALUE_INVALID;
53
        return ErrorEval.VALUE_INVALID;
53
    }
54
    }
55
56
    public ValueEval evaluateInArrayFormula(ValueEval[] evals, int srcCellRow, int srcCellCol) {
57
        if ((evals.length == 1) && (evals[0] instanceof AreaEval)) {
58
            AreaEval ae = (AreaEval) evals[0];
59
            ValueEval[][] result = new ValueEval[1][ae.getWidth()];
60
            for (int c = 0; c < ae.getWidth(); c++) {
61
                result[0][c] = new NumberEval(ae.getFirstColumn() + c + 1);
62
            }
63
            return new ArrayEval(result);
64
        }
65
        return evaluate(evals, srcCellRow, srcCellCol);
66
    }
54
}
67
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Columns.java (-1 / +5 lines)
Lines 28-34 import org.apache.poi.ss.formula.TwoDEval; Link Here
28
 *
28
 *
29
 * @author Josh Micich
29
 * @author Josh Micich
30
 */
30
 */
31
public final class Columns extends Fixed1ArgFunction {
31
public final class Columns extends Fixed1ArgFunction implements FunctionWithArraySupport {
32
32
33
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
33
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
34
34
Lines 42-45 public final class Columns extends Fixed1ArgFunction { Link Here
42
		}
42
		}
43
		return new NumberEval(result);
43
		return new NumberEval(result);
44
	}
44
	}
45
46
	public boolean supportArray(int paramIndex) {
47
		return true;
48
	}
45
}
49
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Count.java (-1 / +5 lines)
Lines 34-40 import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate; Link Here
34
 * TODO: Check this properly matches excel on edge cases
34
 * TODO: Check this properly matches excel on edge cases
35
 *  like formula cells, error cells etc
35
 *  like formula cells, error cells etc
36
 */
36
 */
37
public final class Count implements Function {
37
public final class Count implements FunctionWithArraySupport {
38
38
39
	public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
39
	public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
40
		int nArgs = args.length;
40
		int nArgs = args.length;
Lines 74-77 public final class Count implements Function { Link Here
74
			return false;
74
			return false;
75
		}
75
		}
76
	};
76
	};
77
78
	public boolean supportArray(int paramIndex) {
79
		return true;
80
	}
77
}
81
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Counta.java (-1 / +5 lines)
Lines 32-38 import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate; Link Here
32
 *
32
 *
33
 * @author Josh Micich
33
 * @author Josh Micich
34
 */
34
 */
35
public final class Counta implements Function {
35
public final class Counta implements FunctionWithArraySupport {
36
36
37
	public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
37
	public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
38
		int nArgs = args.length;
38
		int nArgs = args.length;
Lines 69-72 public final class Counta implements Function { Link Here
69
			return true;
69
			return true;
70
		}
70
		}
71
	};
71
	};
72
73
	public boolean supportArray(int paramIndex) {
74
		return true;
75
	}
72
}
76
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Countif.java (-1 / +8 lines)
Lines 44-50 import org.apache.poi.ss.usermodel.ErrorConstants; Link Here
44
 *
44
 *
45
 * @author Josh Micich
45
 * @author Josh Micich
46
 */
46
 */
47
public final class Countif extends Fixed2ArgFunction {
47
public final class Countif extends Fixed2ArgFunction implements FunctionWithArraySupport {
48
48
49
	private static final class CmpOp {
49
	private static final class CmpOp {
50
		public static final int NONE = 0;
50
		public static final int NONE = 0;
Lines 525-528 public final class Countif extends Fixed2ArgFunction { Link Here
525
		}
525
		}
526
		return null;
526
		return null;
527
	}
527
	}
528
529
	public boolean supportArray(int paramIndex) {
530
		if (paramIndex==0) {
531
			return true;
532
		}
533
		return false;  // TODO - countif does not allow first param as array - only as range
534
	}
528
}
535
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/FunctionWithArraySupport.java (+30 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.hssf.record.formula.functions;
19
20
/**
21
 * TODO - Later this interface should be merged with Function interface
22
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com)
23
 */
24
public interface FunctionWithArraySupport extends Function {
25
26
	/**
27
	 * @return <code>true</code> if parameter accept array, <code>false</code> otherwise
28
	 */
29
	public boolean supportArray(int paramIndex);
30
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Hlookup.java (-1 / +5 lines)
Lines 39-45 import org.apache.poi.ss.formula.TwoDEval; Link Here
39
 *
39
 *
40
 * @author Josh Micich
40
 * @author Josh Micich
41
 */
41
 */
42
public final class Hlookup extends Var3or4ArgFunction  {
42
public final class Hlookup extends Var3or4ArgFunction implements FunctionWithArraySupport {
43
	private static final ValueEval DEFAULT_ARG3 = BoolEval.TRUE;
43
	private static final ValueEval DEFAULT_ARG3 = BoolEval.TRUE;
44
44
45
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
45
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
Lines 77-80 public final class Hlookup extends Var3or4ArgFunction { Link Here
77
		}
77
		}
78
		return LookupUtils.createRowVector(tableArray, rowIndex);
78
		return LookupUtils.createRowVector(tableArray, rowIndex);
79
	}
79
	}
80
81
	public boolean supportArray(int paramIndex) {
82
		return paramIndex == 1;
83
	}
80
}
84
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Index.java (-6 / +59 lines)
Lines 25-30 import org.apache.poi.hssf.record.formula.eval.MissingArgEval; Link Here
25
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
25
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
26
import org.apache.poi.hssf.record.formula.eval.RefEval;
26
import org.apache.poi.hssf.record.formula.eval.RefEval;
27
import org.apache.poi.hssf.record.formula.eval.ValueEval;
27
import org.apache.poi.hssf.record.formula.eval.ValueEval;
28
import org.apache.poi.ss.formula.ArrayEval;
28
import org.apache.poi.ss.formula.TwoDEval;
29
import org.apache.poi.ss.formula.TwoDEval;
29
30
30
/**
31
/**
Lines 44-73 import org.apache.poi.ss.formula.TwoDEval; Link Here
44
 * </p>
45
 * </p>
45
 *
46
 *
46
 * @author Josh Micich
47
 * @author Josh Micich
48
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com) - array support
47
 */
49
 */
48
public final class Index implements Function2Arg, Function3Arg, Function4Arg {
50
public final class Index implements Function2Arg, Function3Arg, Function4Arg, FunctionWithArraySupport, ArrayMode {
49
51
50
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
52
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
53
		return evaluateX(srcRowIndex, srcColumnIndex, arg0, arg1, false);
54
	}
55
56
57
	private ValueEval evaluateX(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, boolean supportRowColumn) {
58
59
		if (arg0 instanceof ArrayEval) {
60
			supportRowColumn = true;
61
		}
51
		TwoDEval reference = convertFirstArg(arg0);
62
		TwoDEval reference = convertFirstArg(arg0);
52
63
53
		boolean colArgWasPassed = false;
64
		boolean colArgWasPassed = false;
54
		int columnIx = 0;
65
		int columnIx = 0;
55
		try {
66
		try {
56
			int rowIx = resolveIndexArg(arg1, srcRowIndex, srcColumnIndex);
67
			int rowIx = resolveIndexArg(arg1, srcRowIndex, srcColumnIndex);
57
			return getValueFromArea(reference, rowIx, columnIx, colArgWasPassed, srcRowIndex, srcColumnIndex);
68
			return getValueFromArea(reference, rowIx, columnIx, colArgWasPassed, srcRowIndex, srcColumnIndex, supportRowColumn);
58
		} catch (EvaluationException e) {
69
		} catch (EvaluationException e) {
59
			return e.getErrorEval();
70
			return e.getErrorEval();
60
		}
71
		}
61
	}
72
	}
62
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
73
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
63
			ValueEval arg2) {
74
			ValueEval arg2) {
75
		return evaluateX(srcRowIndex, srcColumnIndex, arg0, arg1, arg2, false);
76
	}
77
	private ValueEval evaluateX(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
78
			ValueEval arg2, boolean supportRowColumn) {
79
80
		if (arg0 instanceof ArrayEval) {
81
			supportRowColumn = true;
82
		}
64
		TwoDEval reference = convertFirstArg(arg0);
83
		TwoDEval reference = convertFirstArg(arg0);
65
84
66
		boolean colArgWasPassed = true;
85
		boolean colArgWasPassed = true;
67
		try {
86
		try {
68
			int columnIx = resolveIndexArg(arg2, srcRowIndex, srcColumnIndex);
87
			int columnIx = resolveIndexArg(arg2, srcRowIndex, srcColumnIndex);
69
			int rowIx = resolveIndexArg(arg1, srcRowIndex, srcColumnIndex);
88
			int rowIx = resolveIndexArg(arg1, srcRowIndex, srcColumnIndex);
70
			return getValueFromArea(reference, rowIx, columnIx, colArgWasPassed, srcRowIndex, srcColumnIndex);
89
			return getValueFromArea(reference, rowIx, columnIx, colArgWasPassed, srcRowIndex, srcColumnIndex, supportRowColumn);
71
		} catch (EvaluationException e) {
90
		} catch (EvaluationException e) {
72
			return e.getErrorEval();
91
			return e.getErrorEval();
73
		}
92
		}
Lines 99-109 public final class Index implements Function2Arg, Function3Arg, Function4Arg { Link Here
99
	}
118
	}
100
119
101
	public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
120
	public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
121
		return evaluateX(args, srcRowIndex, srcColumnIndex, false);
122
	}
123
124
	private ValueEval evaluateX(ValueEval[] args, int srcRowIndex, int srcColumnIndex, boolean supportRowColumn) {
102
		switch (args.length) {
125
		switch (args.length) {
103
			case 2:
126
			case 2:
104
				return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1]);
127
				return evaluateX(srcRowIndex, srcColumnIndex, args[0], args[1], supportRowColumn);
105
			case 3:
128
			case 3:
106
				return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2]);
129
				return evaluateX(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], supportRowColumn);
107
			case 4:
130
			case 4:
108
				return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], args[3]);
131
				return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], args[3]);
109
		}
132
		}
Lines 119-130 public final class Index implements Function2Arg, Function3Arg, Function4Arg { Link Here
119
	 *            different when only 2 args are passed.
142
	 *            different when only 2 args are passed.
120
	 */
143
	 */
121
	private static ValueEval getValueFromArea(TwoDEval ae, int pRowIx, int pColumnIx,
144
	private static ValueEval getValueFromArea(TwoDEval ae, int pRowIx, int pColumnIx,
122
			boolean colArgWasPassed, int srcRowIx, int srcColIx) throws EvaluationException {
145
			boolean colArgWasPassed, int srcRowIx, int srcColIx, boolean supportRowColumn) throws EvaluationException {
123
		boolean rowArgWasEmpty = pRowIx == 0;
146
		boolean rowArgWasEmpty = pRowIx == 0;
124
		boolean colArgWasEmpty = pColumnIx == 0;
147
		boolean colArgWasEmpty = pColumnIx == 0;
125
		int rowIx;
148
		int rowIx;
126
		int columnIx;
149
		int columnIx;
127
150
151
		// implementation of this function isn't support all features of the Excel
152
		// here I'm adding only support for return of entire row or columm
153
		if (supportRowColumn
154
				&& (rowArgWasEmpty && !ae.isRow() && pColumnIx <= ae.getWidth() && !colArgWasEmpty || colArgWasEmpty
155
						&& !ae.isColumn() && pRowIx <= ae.getHeight() && !rowArgWasEmpty)) {
156
			// return row or column
157
			ValueEval[][] result;
158
			if (rowArgWasEmpty ) { // entire column
159
				result = new ValueEval[ae.getHeight()][1];
160
				for( int r=0; r<ae.getHeight(); r++ ) {
161
					result[r][0] = ae.getValue(r, pColumnIx-1);
162
				}
163
			} else { //entire row
164
				result = new ValueEval[1][ae.getWidth()];
165
				for (int c=0; c<ae.getWidth(); c++) {
166
					result[0][c] = ae.getValue(	pRowIx-1,c);
167
				}
168
			}
169
			return new ArrayEval(result);
170
		}
171
128
		// when the area ref is a single row or a single column,
172
		// when the area ref is a single row or a single column,
129
		// there are special rules for conversion of rowIx and columnIx
173
		// there are special rules for conversion of rowIx and columnIx
130
		if (ae.isRow()) {
174
		if (ae.isRow()) {
Lines 228-231 public final class Index implements Function2Arg, Function3Arg, Function4Arg { Link Here
228
		}
272
		}
229
		return result;
273
		return result;
230
	}
274
	}
275
276
	public ValueEval evaluateInArrayFormula(ValueEval[] args, int srcCellRow, int srcCellCol) {
277
		// in array formula index(reference,row,0) and index(reference,0,col) should return entire row/column
278
		return evaluateX(args, srcCellRow, srcCellCol, true);
279
	}
280
281
	public boolean supportArray(int paramIndex) {
282
		return paramIndex == 0;
283
	}
231
}
284
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Lookup.java (-1 / +11 lines)
Lines 36-43 import org.apache.poi.ss.formula.TwoDEval; Link Here
36
 * <b>result_vector</b> Single row or single column area reference from which the result value is chosen.<br/>
36
 * <b>result_vector</b> Single row or single column area reference from which the result value is chosen.<br/>
37
 *
37
 *
38
 * @author Josh Micich
38
 * @author Josh Micich
39
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com) - array support
39
 */
40
 */
40
public final class Lookup extends Var2or3ArgFunction {
41
public final class Lookup extends Var2or3ArgFunction implements FunctionWithArraySupport {
41
42
42
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
43
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
43
		// complex rules to choose lookupVector and resultVector from the single area ref
44
		// complex rules to choose lookupVector and resultVector from the single area ref
Lines 73-76 public final class Lookup extends Var2or3ArgFunction { Link Here
73
		// extra complexity required to emulate the way LOOKUP can handles these abnormal cases.
74
		// extra complexity required to emulate the way LOOKUP can handles these abnormal cases.
74
		throw new RuntimeException("non-vector lookup or result areas not supported yet");
75
		throw new RuntimeException("non-vector lookup or result areas not supported yet");
75
	}
76
	}
77
78
	public boolean supportArray(int paramIndex) {
79
		switch (paramIndex) {
80
			case 1:
81
			case 2:
82
				return true;
83
		}
84
		return false;
85
	}
76
}
86
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Match.java (-1 / +5 lines)
Lines 63-69 import org.apache.poi.ss.formula.TwoDEval; Link Here
63
 *
63
 *
64
 * @author Josh Micich
64
 * @author Josh Micich
65
 */
65
 */
66
public final class Match extends Var2or3ArgFunction {
66
public final class Match extends Var2or3ArgFunction implements FunctionWithArraySupport {
67
67
68
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
68
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
69
		// default match_type is 1.0
69
		// default match_type is 1.0
Lines 248-251 public final class Match extends Var2or3ArgFunction { Link Here
248
		}
248
		}
249
		return false;
249
		return false;
250
	}
250
	}
251
252
	public boolean supportArray(int paramIndex) {
253
		return paramIndex == 1;
254
	}
251
}
255
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Mode.java (-1 / +5 lines)
Lines 35-41 import org.apache.poi.ss.formula.TwoDEval; Link Here
35
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
35
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
36
 *
36
 *
37
 */
37
 */
38
public final class Mode implements Function {
38
public final class Mode implements FunctionWithArraySupport {
39
39
40
	/**
40
	/**
41
	 * if v is zero length or contains no duplicates, return value is
41
	 * if v is zero length or contains no duplicates, return value is
Lines 130-133 public final class Mode implements Function { Link Here
130
		}
130
		}
131
		throw new RuntimeException("Unexpected value type (" + arg.getClass().getName() + ")");
131
		throw new RuntimeException("Unexpected value type (" + arg.getClass().getName() + ")");
132
	}
132
	}
133
134
	public boolean supportArray(int paramIndex) {
135
		return true;
136
	}
133
}
137
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java (-1 / +5 lines)
Lines 34-40 import org.apache.poi.ss.formula.TwoDEval; Link Here
34
 * classes that take variable number of operands, and
34
 * classes that take variable number of operands, and
35
 * where the order of operands does not matter
35
 * where the order of operands does not matter
36
 */
36
 */
37
public abstract class MultiOperandNumericFunction implements Function {
37
public abstract class MultiOperandNumericFunction implements FunctionWithArraySupport {
38
38
39
	private final boolean _isReferenceBoolCounted;
39
	private final boolean _isReferenceBoolCounted;
40
	private final boolean _isBlankCounted;
40
	private final boolean _isBlankCounted;
Lines 194-197 public abstract class MultiOperandNumericFunction implements Function { Link Here
194
		throw new RuntimeException("Invalid ValueEval type passed for conversion: ("
194
		throw new RuntimeException("Invalid ValueEval type passed for conversion: ("
195
				+ ve.getClass() + ")");
195
				+ ve.getClass() + ")");
196
	}
196
	}
197
198
	public boolean supportArray(int paramIndex) {
199
		return true;
200
	}
197
}
201
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Offset.java (-1 / +6 lines)
Lines 38-45 import org.apache.poi.hssf.record.formula.eval.ValueEval; Link Here
38
 * <b>width</b> (default same width as base reference) is the column count for the returned area reference.<br/>
38
 * <b>width</b> (default same width as base reference) is the column count for the returned area reference.<br/>
39
 *
39
 *
40
 * @author Josh Micich
40
 * @author Josh Micich
41
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com) - array support
41
 */
42
 */
42
public final class Offset implements Function {
43
public final class Offset implements FunctionWithArraySupport {
43
	// These values are specific to BIFF8
44
	// These values are specific to BIFF8
44
	private static final int LAST_VALID_ROW_INDEX = 0xFFFF;
45
	private static final int LAST_VALID_ROW_INDEX = 0xFFFF;
45
	private static final int LAST_VALID_COLUMN_INDEX = 0xFF;
46
	private static final int LAST_VALID_COLUMN_INDEX = 0xFF;
Lines 224-227 public final class Offset implements Function { Link Here
224
		ValueEval ve = OperandResolver.getSingleValue(eval, srcCellRow, srcCellCol);
225
		ValueEval ve = OperandResolver.getSingleValue(eval, srcCellRow, srcCellCol);
225
		return OperandResolver.coerceValueToInt(ve);
226
		return OperandResolver.coerceValueToInt(ve);
226
	}
227
	}
228
229
	public boolean supportArray(int paramIndex) {
230
		return paramIndex == 0;
231
	}
227
}
232
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Row.java (-1 / +14 lines)
Lines 22-29 import org.apache.poi.hssf.record.formula.eval.ErrorEval; Link Here
22
import org.apache.poi.hssf.record.formula.eval.NumberEval;
22
import org.apache.poi.hssf.record.formula.eval.NumberEval;
23
import org.apache.poi.hssf.record.formula.eval.RefEval;
23
import org.apache.poi.hssf.record.formula.eval.RefEval;
24
import org.apache.poi.hssf.record.formula.eval.ValueEval;
24
import org.apache.poi.hssf.record.formula.eval.ValueEval;
25
import org.apache.poi.ss.formula.ArrayEval;
25
26
26
public final class Row implements Function0Arg, Function1Arg {
27
public final class Row implements Function0Arg, Function1Arg, ArrayMode {
27
28
28
    public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
29
    public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
29
        return new NumberEval(srcRowIndex+1);
30
        return new NumberEval(srcRowIndex+1);
Lines 52-55 public final class Row implements Function0Arg, Function1Arg { Link Here
52
        return ErrorEval.VALUE_INVALID;
53
        return ErrorEval.VALUE_INVALID;
53
    }
54
    }
54
55
56
    public ValueEval evaluateInArrayFormula(ValueEval[] evals, int srcCellRow, int srcCellCol) {
57
        if ((evals.length == 1) && (evals[0] instanceof AreaEval)) {
58
            AreaEval ae = (AreaEval) evals[0];
59
60
            ValueEval[][] result = new ValueEval[ae.getHeight()][1];
61
            for (int r = 0; r < ae.getHeight(); r++) {
62
                result[r][0] = new NumberEval(ae.getFirstRow() + r + 1);
63
            }
64
            return new ArrayEval(result);
65
        }
66
        return evaluate(evals, srcCellRow, srcCellCol);
67
    }
55
}
68
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Rows.java (-1 / +5 lines)
Lines 28-34 import org.apache.poi.ss.formula.TwoDEval; Link Here
28
 *
28
 *
29
 * @author Josh Micich
29
 * @author Josh Micich
30
 */
30
 */
31
public final class Rows extends Fixed1ArgFunction {
31
public final class Rows extends Fixed1ArgFunction implements FunctionWithArraySupport {
32
32
33
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
33
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
34
34
Lines 42-45 public final class Rows extends Fixed1ArgFunction { Link Here
42
		}
42
		}
43
		return new NumberEval(result);
43
		return new NumberEval(result);
44
	}
44
	}
45
46
	public boolean supportArray(int paramIndex) {
47
		return true;
48
	}
45
}
49
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Sumif.java (-1 / +8 lines)
Lines 37-44 import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate; Link Here
37
 *    </table><br/>
37
 *    </table><br/>
38
 * </p>
38
 * </p>
39
 * @author Josh Micich
39
 * @author Josh Micich
40
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com) - array support
40
 */
41
 */
41
public final class Sumif extends Var2or3ArgFunction {
42
public final class Sumif extends Var2or3ArgFunction implements FunctionWithArraySupport {
42
43
43
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
44
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
44
45
Lines 124-127 public final class Sumif extends Var2or3ArgFunction { Link Here
124
		throw new EvaluationException(ErrorEval.VALUE_INVALID);
125
		throw new EvaluationException(ErrorEval.VALUE_INVALID);
125
	}
126
	}
126
127
128
	public boolean supportArray(int paramIndex) {
129
		if (paramIndex == 1) { // TODO - should throw exception if array instead of range
130
			return false;
131
		}
132
		return true;
133
	}
127
}
134
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Sumproduct.java (-4 / +9 lines)
Lines 50-72 import org.apache.poi.ss.formula.TwoDEval; Link Here
50
 *  )
50
 *  )
51
 * </p>
51
 * </p>
52
 * @author Josh Micich
52
 * @author Josh Micich
53
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com) - array support
53
 */
54
 */
54
public final class Sumproduct implements Function {
55
public final class Sumproduct implements FunctionWithArraySupport {
55
56
56
57
57
	public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
58
	public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
58
59
59
		int maxN = args.length;
60
		int maxN = args.length;
60
61
61
		if(maxN < 1) {
62
		if (maxN < 1) {
62
			return ErrorEval.VALUE_INVALID;
63
			return ErrorEval.VALUE_INVALID;
63
		}
64
		}
64
		ValueEval firstArg = args[0];
65
		ValueEval firstArg = args[0];
65
		try {
66
		try {
66
			if(firstArg instanceof NumericValueEval) {
67
			if (firstArg instanceof NumericValueEval) {
67
				return evaluateSingleProduct(args);
68
				return evaluateSingleProduct(args);
68
			}
69
			}
69
			if(firstArg instanceof RefEval) {
70
			if (firstArg instanceof RefEval) {
70
				return evaluateSingleProduct(args);
71
				return evaluateSingleProduct(args);
71
			}
72
			}
72
			if (firstArg instanceof TwoDEval) {
73
			if (firstArg instanceof TwoDEval) {
Lines 228-231 public final class Sumproduct implements Function { Link Here
228
		throw new RuntimeException("Unexpected value eval class ("
229
		throw new RuntimeException("Unexpected value eval class ("
229
				+ ve.getClass().getName() + ")");
230
				+ ve.getClass().getName() + ")");
230
	}
231
	}
232
233
	public boolean supportArray(int paramIndex) {
234
		return true;
235
	}
231
}
236
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/Vlookup.java (-1 / +6 lines)
Lines 38-45 import org.apache.poi.ss.formula.TwoDEval; Link Here
38
 * the lookup_value.  If FALSE, only exact matches will be considered<br/>
38
 * the lookup_value.  If FALSE, only exact matches will be considered<br/>
39
 *
39
 *
40
 * @author Josh Micich
40
 * @author Josh Micich
41
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com) - array support
41
 */
42
 */
42
public final class Vlookup extends Var3or4ArgFunction {
43
public final class Vlookup extends Var3or4ArgFunction implements FunctionWithArraySupport {
43
	private static final ValueEval DEFAULT_ARG3 = BoolEval.TRUE;
44
	private static final ValueEval DEFAULT_ARG3 = BoolEval.TRUE;
44
45
45
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
46
	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
Lines 78-81 public final class Vlookup extends Var3or4ArgFunction { Link Here
78
		}
79
		}
79
		return LookupUtils.createColumnVector(tableArray, colIndex);
80
		return LookupUtils.createColumnVector(tableArray, colIndex);
80
	}
81
	}
82
83
	public boolean supportArray(int paramIndex) {
84
		return paramIndex == 1;
85
	}
81
}
86
}
(-)a/src/java/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java (-1 / +5 lines)
Lines 28-34 import org.apache.poi.ss.formula.TwoDEval; Link Here
28
/**
28
/**
29
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
29
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
30
 */
30
 */
31
public abstract class XYNumericFunction extends Fixed2ArgFunction {
31
public abstract class XYNumericFunction extends Fixed2ArgFunction implements FunctionWithArraySupport {
32
32
33
	private static abstract class ValueArray implements ValueVector {
33
	private static abstract class ValueArray implements ValueVector {
34
		private final int _size;
34
		private final int _size;
Lines 174-177 public abstract class XYNumericFunction extends Fixed2ArgFunction { Link Here
174
		}
174
		}
175
		return new SingleCellValueArray(arg);
175
		return new SingleCellValueArray(arg);
176
	}
176
	}
177
178
	public boolean supportArray(int paramIndex) {
179
		return true;
180
	}
177
}
181
}
(-)a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java (+28 lines)
Lines 43-48 import org.apache.poi.hssf.record.SubRecord; Link Here
43
import org.apache.poi.hssf.record.TextObjectRecord;
43
import org.apache.poi.hssf.record.TextObjectRecord;
44
import org.apache.poi.hssf.record.UnicodeString;
44
import org.apache.poi.hssf.record.UnicodeString;
45
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
45
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
46
import org.apache.poi.hssf.record.formula.ExpPtg;
46
import org.apache.poi.hssf.record.formula.Ptg;
47
import org.apache.poi.hssf.record.formula.Ptg;
47
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
48
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
48
import org.apache.poi.ss.usermodel.Cell;
49
import org.apache.poi.ss.usermodel.Cell;
Lines 50-55 import org.apache.poi.ss.usermodel.CellStyle; Link Here
50
import org.apache.poi.ss.usermodel.Comment;
51
import org.apache.poi.ss.usermodel.Comment;
51
import org.apache.poi.ss.usermodel.Hyperlink;
52
import org.apache.poi.ss.usermodel.Hyperlink;
52
import org.apache.poi.ss.usermodel.RichTextString;
53
import org.apache.poi.ss.usermodel.RichTextString;
54
import org.apache.poi.ss.util.CellRangeAddress;
53
import org.apache.poi.ss.util.NumberToTextConverter;
55
import org.apache.poi.ss.util.NumberToTextConverter;
54
import org.apache.poi.ss.formula.FormulaType;
56
import org.apache.poi.ss.formula.FormulaType;
55
import org.apache.poi.ss.SpreadsheetVersion;
57
import org.apache.poi.ss.SpreadsheetVersion;
Lines 1160-1163 public class HSSFCell implements Cell { Link Here
1160
        }
1162
        }
1161
        return ((FormulaRecordAggregate)_record).getFormulaRecord().getCachedResultType();
1163
        return ((FormulaRecordAggregate)_record).getFormulaRecord().getCachedResultType();
1162
    }
1164
    }
1165
1166
    void setCellArrayFormula(CellRangeAddress range) {
1167
        int row=_record.getRow();
1168
        short col=_record.getColumn();
1169
        short styleIndex=_record.getXFIndex();
1170
        setCellType(CELL_TYPE_FORMULA, false, row, col, styleIndex);
1171
1172
        // Billet for formula in rec
1173
        Ptg[] ptgsForCell = { new ExpPtg(range.getFirstRow(), range.getFirstColumn()) };
1174
        FormulaRecordAggregate agg = (FormulaRecordAggregate) _record;
1175
        agg.setParsedExpression(ptgsForCell);
1176
    }
1177
1178
    public CellRangeAddress getArrayFormulaRange() {
1179
        if (_cellType != CELL_TYPE_FORMULA) {
1180
            throw new IllegalArgumentException("Only formula cells can have array ranges");
1181
        }
1182
        return ((FormulaRecordAggregate)_record).getArrayFormulaRange();
1183
    }
1184
1185
    public boolean isPartOfArrayFormulaGroup() {
1186
        if (_cellType != CELL_TYPE_FORMULA) {
1187
            return false;
1188
        }
1189
        return ((FormulaRecordAggregate)_record).isPartOfArrayFormula();
1190
    }
1163
}
1191
}
(-)a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationCell.java (-1 / +6 lines)
Lines 19-27 package org.apache.poi.hssf.usermodel; Link Here
19
19
20
import org.apache.poi.ss.formula.EvaluationCell;
20
import org.apache.poi.ss.formula.EvaluationCell;
21
import org.apache.poi.ss.formula.EvaluationSheet;
21
import org.apache.poi.ss.formula.EvaluationSheet;
22
import org.apache.poi.ss.util.CellRangeAddress;
22
/**
23
/**
23
 * HSSF wrapper for a cell under evaluation
24
 * HSSF wrapper for a cell under evaluation
24
 * 
25
 *
25
 * @author Josh Micich
26
 * @author Josh Micich
26
 */
27
 */
27
final class HSSFEvaluationCell implements EvaluationCell {
28
final class HSSFEvaluationCell implements EvaluationCell {
Lines 69-72 final class HSSFEvaluationCell implements EvaluationCell { Link Here
69
	public String getStringCellValue() {
70
	public String getStringCellValue() {
70
		return _cell.getRichStringCellValue().getString();
71
		return _cell.getRichStringCellValue().getString();
71
	}
72
	}
73
74
	public boolean isArrayFormula() {
75
		return _cell.isPartOfArrayFormulaGroup();
76
	}
72
}
77
}
(-)a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java (-5 / +101 lines)
Lines 25-37 import org.apache.poi.hssf.record.formula.eval.NumberEval; Link Here
25
import org.apache.poi.hssf.record.formula.eval.StringEval;
25
import org.apache.poi.hssf.record.formula.eval.StringEval;
26
import org.apache.poi.hssf.record.formula.eval.ValueEval;
26
import org.apache.poi.hssf.record.formula.eval.ValueEval;
27
import org.apache.poi.hssf.record.formula.udf.UDFFinder;
27
import org.apache.poi.hssf.record.formula.udf.UDFFinder;
28
import org.apache.poi.ss.formula.ArrayEval;
28
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;
29
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment;
29
import org.apache.poi.ss.formula.IStabilityClassifier;
30
import org.apache.poi.ss.formula.IStabilityClassifier;
30
import org.apache.poi.ss.formula.WorkbookEvaluator;
31
import org.apache.poi.ss.formula.WorkbookEvaluator;
32
import org.apache.poi.ss.usermodel.ArrayFormulaEvaluatorHelper;
31
import org.apache.poi.ss.usermodel.Cell;
33
import org.apache.poi.ss.usermodel.Cell;
32
import org.apache.poi.ss.usermodel.CellValue;
34
import org.apache.poi.ss.usermodel.CellValue;
33
import org.apache.poi.ss.usermodel.FormulaEvaluator;
35
import org.apache.poi.ss.usermodel.FormulaEvaluator;
34
import org.apache.poi.ss.usermodel.Row;
36
import org.apache.poi.ss.usermodel.Row;
37
import org.apache.poi.ss.usermodel.Sheet;
38
import org.apache.poi.ss.util.CellRangeAddress;
35
39
36
/**
40
/**
37
 * Evaluates formula cells.<p/>
41
 * Evaluates formula cells.<p/>
Lines 42-47 import org.apache.poi.ss.usermodel.Row; Link Here
42
 *
46
 *
43
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
47
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
44
 * @author Josh Micich
48
 * @author Josh Micich
49
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com) - Array Formula support
50
 * @author Vladimirs Abramovs(Vladimirs.Abramovs at exigenservices.com) - Array Formula support
45
 */
51
 */
46
public class HSSFFormulaEvaluator implements FormulaEvaluator  {
52
public class HSSFFormulaEvaluator implements FormulaEvaluator  {
47
53
Lines 207-215 public class HSSFFormulaEvaluator implements FormulaEvaluator { Link Here
207
		if (cell == null || cell.getCellType() != HSSFCell.CELL_TYPE_FORMULA) {
213
		if (cell == null || cell.getCellType() != HSSFCell.CELL_TYPE_FORMULA) {
208
			return -1;
214
			return -1;
209
		}
215
		}
210
		CellValue cv = evaluateFormulaCellValue(cell);
211
		// cell remains a formula cell, but the cached value is changed
216
		// cell remains a formula cell, but the cached value is changed
212
		setCellValue(cell, cv);
217
		CellValue cv;
218
		if (cell.isPartOfArrayFormulaGroup()) { // Array Formula Context
219
			CellValue[][] cvs = evaluateFormulaCellArrayValues((HSSFCell) cell);
220
			int rowIndex = cell.getRowIndex() - cell.getArrayFormulaRange().getFirstRow();
221
			int colIndex = cell.getColumnIndex() - cell.getArrayFormulaRange().getFirstColumn();
222
			CellValue[][] values = setCellValues(cell, cvs);
223
			cv = values[rowIndex][colIndex];
224
		} else { // Single Formula
225
226
			cv = evaluateFormulaCellValue(cell);
227
			setCellValue(cell, cv);
228
		}
213
		return cv.getCellType();
229
		return cv.getCellType();
214
	}
230
	}
215
231
Lines 234-242 public class HSSFFormulaEvaluator implements FormulaEvaluator { Link Here
234
		}
250
		}
235
		HSSFCell result = (HSSFCell) cell;
251
		HSSFCell result = (HSSFCell) cell;
236
		if (cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
252
		if (cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
237
			CellValue cv = evaluateFormulaCellValue(cell);
253
			if (cell.isPartOfArrayFormulaGroup()) { // Array Formula Context
238
			setCellValue(cell, cv);
254
				CellValue[][] cvs = evaluateFormulaCellArrayValues((HSSFCell) cell);
239
			setCellType(cell, cv); // cell will no longer be a formula cell
255
				setCellValues(cell, cvs);
256
				setCellsTypes(cell, cvs); // cells will no longer be a formula cell
257
			} else { // Single Formula
258
				CellValue cv = evaluateFormulaCellValue(cell);
259
				setCellValue(cell, cv);
260
				setCellType(cell, cv); // cell will no longer be a formula cell
261
			}
240
		}
262
		}
241
		return result;
263
		return result;
242
	}
264
	}
Lines 281-286 public class HSSFFormulaEvaluator implements FormulaEvaluator { Link Here
281
		}
303
		}
282
	}
304
	}
283
305
306
	private void setCellsTypes(Cell cell, CellValue[][] cvs) {
307
		CellRangeAddress range = cell.getArrayFormulaRange();
308
		int rowStart = range.getFirstRow();
309
		int colStart = range.getFirstColumn();
310
		Sheet sheet = cell.getSheet();
311
		for(int i=rowStart;i<=range.getLastRow();i++ )
312
			for(int j=colStart; j<=range.getLastColumn();j++) {
313
				Row row = sheet.getRow(i);
314
				Cell c = row.getCell(j);
315
				if ((i-rowStart)<cvs.length && (j-colStart)<cvs[i-rowStart].length) {
316
					setCellType(c,cvs[i-rowStart][j-colStart]);
317
				}
318
			}
319
	}
320
321
	/**
322
	 * Set value in Range
323
	 * @param cell
324
	 * @param cvs
325
	 * @return
326
	 */
327
	private CellValue[][] setCellValues(Cell cell, CellValue[][] cvs) {
328
		CellRangeAddress range = cell.getArrayFormulaRange();
329
		int rowStart = range.getFirstRow();
330
		int colStart = range.getFirstColumn();
331
		Sheet sheet = cell.getSheet();
332
		CellValue[][] answer = ArrayFormulaEvaluatorHelper.transformToRange(cvs, range);
333
		for(int i=rowStart;i<=range.getLastRow();i++ ) {
334
			for(int j=colStart; j<=range.getLastColumn();j++) {
335
				Row row = sheet.getRow(i);
336
				if (row == null) {
337
					row = sheet.createRow(i);
338
				}
339
				Cell c = row.getCell(j);
340
				if (c == null) {
341
					c = row.createCell(j);
342
				}
343
				CellValue cellValue = answer[i-rowStart][j-colStart];
344
				setCellValue(c,cellValue);
345
			}
346
		}
347
		return answer;
348
	}
349
284
	/**
350
	/**
285
	 * Loops over all cells in all sheets of the supplied
351
	 * Loops over all cells in all sheets of the supplied
286
	 *  workbook.
352
	 *  workbook.
Lines 315-320 public class HSSFFormulaEvaluator implements FormulaEvaluator { Link Here
315
	 */
381
	 */
316
	private CellValue evaluateFormulaCellValue(Cell cell) {
382
	private CellValue evaluateFormulaCellValue(Cell cell) {
317
		ValueEval eval = _bookEvaluator.evaluate(new HSSFEvaluationCell((HSSFCell)cell));
383
		ValueEval eval = _bookEvaluator.evaluate(new HSSFEvaluationCell((HSSFCell)cell));
384
		if (eval instanceof ArrayEval) {// support of arrays
385
			if (cell.isPartOfArrayFormulaGroup()) {
386
				eval = ArrayFormulaEvaluatorHelper.dereferenceValue((ArrayEval) eval, cell);
387
			} else {
388
				eval = ((ArrayEval) eval).getValue(0, 0);
389
			}
390
		}
318
		if (eval instanceof NumberEval) {
391
		if (eval instanceof NumberEval) {
319
			NumberEval ne = (NumberEval) eval;
392
			NumberEval ne = (NumberEval) eval;
320
			return new CellValue(ne.getNumberValue());
393
			return new CellValue(ne.getNumberValue());
Lines 332-335 public class HSSFFormulaEvaluator implements FormulaEvaluator { Link Here
332
		}
405
		}
333
		throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
406
		throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
334
	}
407
	}
408
409
	/**
410
	 * Returns a Array CellValue wrapper around the supplied ArrayEval instance.
411
	 */
412
	private CellValue[][] evaluateFormulaCellArrayValues(HSSFCell cell) {
413
		ValueEval eval = _bookEvaluator.evaluate(new HSSFEvaluationCell(cell));
414
		if (eval instanceof ArrayEval) {// support of arrays
415
			ArrayEval ae = (ArrayEval) eval;
416
			int rowCount = ae.getHeight();
417
			int ColCount = ae.getWidth();
418
			CellValue[][] answer = new CellValue[rowCount][ColCount];
419
			for (int i = 0; i < rowCount; i++)
420
				for (int j = 0; j < ColCount; j++) {
421
					ValueEval val = ae.getValue(i, j);
422
					answer[i][j] = ArrayFormulaEvaluatorHelper.evalToCellValue(val);
423
				}
424
			return answer;
425
		}
426
		// non-array (usually from aggregate function)
427
		CellValue[][] answer = new CellValue[1][1];
428
		answer[0][0] = ArrayFormulaEvaluatorHelper.evalToCellValue(eval);
429
		return answer;
430
	}
335
}
431
}
(-)a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java (-3 / +50 lines)
Lines 31-36 import java.util.List; Link Here
31
import java.util.TreeMap;
31
import java.util.TreeMap;
32
32
33
import org.apache.poi.ddf.EscherRecord;
33
import org.apache.poi.ddf.EscherRecord;
34
import org.apache.poi.hssf.model.HSSFFormulaParser;
34
import org.apache.poi.hssf.model.Sheet;
35
import org.apache.poi.hssf.model.Sheet;
35
import org.apache.poi.hssf.model.Workbook;
36
import org.apache.poi.hssf.model.Workbook;
36
import org.apache.poi.hssf.record.CellValueRecordInterface;
37
import org.apache.poi.hssf.record.CellValueRecordInterface;
Lines 44-53 import org.apache.poi.hssf.record.SCLRecord; Link Here
44
import org.apache.poi.hssf.record.WSBoolRecord;
45
import org.apache.poi.hssf.record.WSBoolRecord;
45
import org.apache.poi.hssf.record.WindowTwoRecord;
46
import org.apache.poi.hssf.record.WindowTwoRecord;
46
import org.apache.poi.hssf.record.aggregates.DataValidityTable;
47
import org.apache.poi.hssf.record.aggregates.DataValidityTable;
48
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
47
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
49
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
48
import org.apache.poi.hssf.record.formula.FormulaShifter;
50
import org.apache.poi.hssf.record.formula.FormulaShifter;
51
import org.apache.poi.hssf.record.formula.Ptg;
49
import org.apache.poi.hssf.util.PaneInformation;
52
import org.apache.poi.hssf.util.PaneInformation;
50
import org.apache.poi.hssf.util.Region;
53
import org.apache.poi.hssf.util.Region;
54
import org.apache.poi.ss.formula.FormulaType;
51
import org.apache.poi.ss.usermodel.Cell;
55
import org.apache.poi.ss.usermodel.Cell;
52
import org.apache.poi.ss.usermodel.CellStyle;
56
import org.apache.poi.ss.usermodel.CellStyle;
53
import org.apache.poi.ss.usermodel.Row;
57
import org.apache.poi.ss.usermodel.Row;
Lines 64-69 import org.apache.poi.util.POILogger; Link Here
64
 * @author  Shawn Laubach (slaubach at apache dot org) (Just a little)
68
 * @author  Shawn Laubach (slaubach at apache dot org) (Just a little)
65
 * @author  Jean-Pierre Paris (jean-pierre.paris at m4x dot org) (Just a little, too)
69
 * @author  Jean-Pierre Paris (jean-pierre.paris at m4x dot org) (Just a little, too)
66
 * @author  Yegor Kozlov (yegor at apache.org) (Autosizing columns)
70
 * @author  Yegor Kozlov (yegor at apache.org) (Autosizing columns)
71
 * @author Petr Udalau(Petr.Udalau at exigenservices.com) - set/remove array formulas
67
 */
72
 */
68
public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
73
public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
69
    private static final POILogger log = POILogFactory.getLogger(HSSFSheet.class);
74
    private static final POILogger log = POILogFactory.getLogger(HSSFSheet.class);
Lines 636-647 public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { Link Here
636
641
637
    /**
642
    /**
638
     * Sets whether the worksheet is displayed from right to left instead of from left to right.
643
     * Sets whether the worksheet is displayed from right to left instead of from left to right.
639
     * 
644
     *
640
     * @param value true for right to left, false otherwise.
645
     * @param value true for right to left, false otherwise.
641
     */
646
     */
642
    public void setRightToLeft(boolean value)
647
    public void setRightToLeft(boolean value)
643
    {
648
    {
644
	    _sheet.getWindowTwo().setArabic(value);
649
        _sheet.getWindowTwo().setArabic(value);
645
    }
650
    }
646
651
647
    /**
652
    /**
Lines 651-657 public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { Link Here
651
     */
656
     */
652
    public boolean isRightToLeft()
657
    public boolean isRightToLeft()
653
    {
658
    {
654
	    return _sheet.getWindowTwo().getArabic();
659
        return _sheet.getWindowTwo().getArabic();
655
    }
660
    }
656
661
657
    /**
662
    /**
Lines 1870-1873 public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { Link Here
1870
        return wb.getSheetName(idx);
1875
        return wb.getSheetName(idx);
1871
    }
1876
    }
1872
1877
1878
    public void setArrayFormula(String formula, CellRangeAddress range) {
1879
        // make sure the formula parses OK first
1880
        Ptg[] ptgs = HSSFFormulaParser.parse(formula, _workbook, FormulaType.ARRAY, _workbook.getSheetIndex(this));
1881
        int firstRow = range.getFirstRow();
1882
        int firstColumn = range.getFirstColumn();
1883
        for (int rowIn = firstRow; rowIn <= range.getLastRow(); rowIn++) {
1884
            for (int colIn = firstColumn; colIn <= range.getLastColumn(); colIn++) {
1885
                HSSFRow row = getRow(rowIn);
1886
                if (row == null) {
1887
                    row = createRow(rowIn);
1888
                }
1889
                HSSFCell cell = row.getCell(colIn);
1890
                if (cell == null) {
1891
                    cell = row.createCell(colIn);
1892
                }
1893
                cell.setCellArrayFormula(range);
1894
            }
1895
        }
1896
        HSSFCell firstArrayFormulaCell = getRow(firstRow).getCell(firstColumn);
1897
        FormulaRecordAggregate agg = (FormulaRecordAggregate) firstArrayFormulaCell.getCellValueRecord();
1898
        agg.setArrayFormula(range, ptgs);
1899
    }
1900
1901
1902
    public void removeArrayFormula(Cell cell) {
1903
        CellValueRecordInterface rec = ((HSSFCell) cell).getCellValueRecord();
1904
        if (!(rec instanceof FormulaRecordAggregate)) {
1905
            throw new IllegalArgumentException("Specified cell is not a formula cell.");
1906
        }
1907
        FormulaRecordAggregate fra = (FormulaRecordAggregate) rec;
1908
        CellRangeAddress range = fra.removeArrayFormula(cell.getRowIndex(), cell.getColumnIndex());
1909
        if (range == null) {
1910
            throw new IllegalArgumentException("Specified cell does not contain an array formula.");
1911
        }
1912
        // clear all cells in the range
1913
        for (int rowIn = range.getFirstRow(); rowIn <= range.getLastRow(); rowIn++) {
1914
            for (int colIn = range.getFirstColumn(); colIn <= range.getLastColumn(); colIn++) {
1915
                Cell rCell = getRow(rowIn).getCell(colIn);
1916
                rCell.setCellType(Cell.CELL_TYPE_BLANK);
1917
            }
1918
        }
1919
    }
1873
}
1920
}
(-)a/src/java/org/apache/poi/ss/formula/ArrayEval.java (+259 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.ss.formula;
19
20
import java.util.ArrayList;
21
import java.util.List;
22
23
import org.apache.poi.hssf.record.constant.ErrorConstant;
24
import org.apache.poi.hssf.record.formula.ArrayPtg;
25
import org.apache.poi.hssf.record.formula.eval.AreaEval;
26
import org.apache.poi.hssf.record.formula.eval.BoolEval;
27
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
28
import org.apache.poi.hssf.record.formula.eval.NumberEval;
29
import org.apache.poi.hssf.record.formula.eval.RefEval;
30
import org.apache.poi.hssf.record.formula.eval.StringEval;
31
import org.apache.poi.hssf.record.formula.eval.ValueEval;
32
import org.apache.poi.ss.util.NumberToTextConverter;
33
34
/**
35
 *  Class to support evaluated array of values
36
 *
37
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com)
38
 */
39
public final class ArrayEval implements TwoDEval {
40
41
	private final ValueEval[][] _values;
42
43
	public ArrayEval(ArrayPtg ptg) {
44
		if (ptg == null) {
45
			throw new IllegalArgumentException("ArrayPtg should not be null");
46
		}
47
		Object[][] tokenValues = ptg.getTokenArrayValues();
48
		int nRows = tokenValues.length;
49
		int nCols = tokenValues[0].length;
50
		ValueEval[][] values = new ValueEval[nRows][nCols];
51
		for (int r=0; r< nRows; r++) {
52
			Object[] tokenRow = tokenValues[r];
53
			ValueEval[] row = values[r];
54
			for (int c=0; c< nCols; c++) {
55
				row[c] = constructEval(tokenRow[c]);
56
			}
57
		}
58
		_values = values;
59
	}
60
61
	public ArrayEval(ValueEval[][] values) {
62
		if (values == null) {
63
			throw new IllegalArgumentException("null is not allowed");
64
		}
65
		int nRows = values.length;
66
		int nCols = values[0].length;
67
		for (int r=0; r< nRows; r++) {
68
			ValueEval[] row = values[r];
69
			for (int c=0; c< nCols; c++) {
70
				validateValueEval(row[c]);
71
			}
72
		}
73
74
		_values = values;
75
	}
76
77
	private void validateValueEval(ValueEval valueEval) {
78
		if (valueEval instanceof NumberEval) {
79
			return;
80
		}
81
		if (valueEval instanceof StringEval) {
82
			return;
83
		}
84
		if (valueEval instanceof BoolEval) {
85
			return;
86
		}
87
		if (valueEval instanceof ErrorEval) {
88
			return;
89
		}
90
91
		if (valueEval == null) {
92
			if (false) {
93
				// TODO throw new IllegalArgumentException("Array elements cannot be null.");
94
			}
95
			return;
96
		}
97
98
		if (valueEval instanceof RefEval) {
99
			throw new IllegalArgumentException("Array elements cannot be of type RefEval");
100
		}
101
		if (valueEval instanceof AreaEval) {
102
			throw new IllegalArgumentException("Array elements cannot be of type AreaEval");
103
		}
104
		throw new IllegalArgumentException("Unexpected eval type ("
105
				+ valueEval.getClass().getSimpleName() + ").");
106
	}
107
108
	public String toString() {
109
		StringBuilder sb = new StringBuilder();
110
		sb.append(getClass().getName()).append(" [");
111
		sb.append("{");
112
		for (int r = 0; r < _values.length; r++) {
113
			if (r > 0) {
114
				sb.append(";");
115
			}
116
			for (int c = 0; c < _values[r].length; c++) {
117
				if (c > 0) {
118
					sb.append(",");
119
				}
120
				Object o = _values[r][c];
121
				sb.append(getConstantText(o));
122
			}
123
		}
124
		sb.append("}]");
125
		return sb.toString();
126
	}
127
128
	/**
129
	 * TODO - remove this method
130
	 */
131
	public ValueEval[][] getArrayValues() {
132
		return _values;
133
	}
134
135
	/**
136
	 * get element of array as Value Eval
137
	 * @param row
138
	 * @param col
139
	 * @return
140
	 */
141
	public ValueEval getValue(int row, int col) {
142
		return _values[row][col];
143
	}
144
145
	/**
146
	 * Convert Object to ValueEval
147
	 */
148
	private static ValueEval constructEval(Object o) {
149
		if (o == null) {
150
			throw new RuntimeException("Array item cannot be null");
151
		}
152
		if (o instanceof String) {
153
			return new StringEval( (String)o );
154
		}
155
		if (o instanceof Double) {
156
			return new NumberEval(((Double)o).doubleValue());
157
		}
158
		if (o instanceof Boolean) {
159
			return BoolEval.valueOf(((Boolean)o).booleanValue());
160
		}
161
		if (o instanceof ErrorConstant) {
162
			return ErrorEval.valueOf(((ErrorConstant)o).getErrorCode());
163
		}
164
		throw new IllegalArgumentException("Unexpected constant class (" + o.getClass());
165
	}
166
167
168
	/**
169
	 * get String contains object's value
170
	 * @param o
171
	 * @return
172
	 */
173
	private static String getConstantText(Object o) {
174
175
		if (o == null) {
176
			return "Error - null";
177
// TODO			throw new RuntimeException("Array item cannot be null");
178
		}
179
		if (o instanceof StringEval) {
180
			return "\"" + ((StringEval)o).getStringValue() + "\"";
181
		}
182
		if (o instanceof NumberEval) {
183
			return NumberToTextConverter.toText(((NumberEval)o).getNumberValue());
184
		}
185
		if (o instanceof Boolean) {
186
			return ((Boolean)o).booleanValue() ? "TRUE" : "FALSE";
187
		}
188
		if (o instanceof ErrorEval) {
189
			return ErrorEval.getText(((ErrorEval)o).getErrorCode());
190
		}
191
		throw new IllegalArgumentException("Unexpected constant class (" + o.getClass().getName() + ")");
192
	}
193
194
	/**
195
	 * return Array as ValueEval list
196
	 * @return
197
	 */
198
	public List<ValueEval> getArrayAsEval() {
199
200
		List<ValueEval> l = new ArrayList<ValueEval>();
201
		for(int r=0; r< _values.length; r++) {
202
			ValueEval[] row = _values[r];
203
			for (int c=0; c<row.length; c++) {
204
				l.add(row[c]);
205
			}
206
		}
207
		return l;
208
	}
209
210
	/**
211
	 * get row count
212
	 * @return
213
	 */
214
	public int getHeight() {
215
		return _values.length;
216
	}
217
218
	/**
219
	 * get column count
220
	 * @return
221
	 */
222
	public int getWidth() {
223
		return _values[0].length;
224
	}
225
226
	public boolean isRow() {
227
		return _values.length == 1;
228
	}
229
230
	public boolean isColumn() {
231
		return _values[0].length == 1;
232
	}
233
	public enum BooleanContent{ONLY_FALSE,ONLY_TRUE,MIXED}
234
235
	/**
236
	 *  Check if content of boolean array ONLY_FALSE, ONLY_TRUE or MIXED
237
	 *  if content is not boolean then return MIXED
238
	 * @return
239
	 */
240
	public BooleanContent checkBooleanContent() {
241
		try {
242
			BoolEval first = (BoolEval) _values[0][0];
243
			for (int i = 0; i < _values.length; i++) {
244
				for (int j = 0; j < _values[i].length; j++) {
245
					if (first.equals(_values[i][j])) {
246
						return BooleanContent.MIXED;
247
					}
248
				}
249
			}
250
251
			if (first.getBooleanValue()) {
252
				return BooleanContent.ONLY_TRUE;
253
			}
254
			return BooleanContent.ONLY_FALSE;
255
		} catch (Exception e) {
256
			return BooleanContent.MIXED;
257
		}
258
	}
259
}
(-)a/src/java/org/apache/poi/ss/formula/EvaluationCell.java (-2 / +8 lines)
Lines 22-31 import java.util.HashMap; Link Here
22
/**
22
/**
23
 * Abstracts a cell for the purpose of formula evaluation.  This interface represents both formula
23
 * Abstracts a cell for the purpose of formula evaluation.  This interface represents both formula
24
 * and non-formula cells.<br/>
24
 * and non-formula cells.<br/>
25
 * 
25
 *
26
 * For POI internal use only
26
 * For POI internal use only
27
 * 
27
 *
28
 * @author Josh Micich
28
 * @author Josh Micich
29
 * @author Petr Udalau(Petr.Udalau at exigenservices.com) - array formula support
29
 */
30
 */
30
public interface EvaluationCell {
31
public interface EvaluationCell {
31
	/**
32
	/**
Lines 42-45 public interface EvaluationCell { Link Here
42
	String getStringCellValue();
43
	String getStringCellValue();
43
	boolean getBooleanCellValue();
44
	boolean getBooleanCellValue();
44
	int getErrorCellValue();
45
	int getErrorCellValue();
46
47
	/**
48
	 * @return <code>true<code> if cell belongs to to range of Array Formula
49
	 */
50
	boolean isArrayFormula();
45
}
51
}
(-)a/src/java/org/apache/poi/ss/formula/OperandClassTransformer.java (+3 lines)
Lines 72-77 final class OperandClassTransformer { Link Here
72
			case FormulaType.CELL:
72
			case FormulaType.CELL:
73
				rootNodeOperandClass = Ptg.CLASS_VALUE;
73
				rootNodeOperandClass = Ptg.CLASS_VALUE;
74
				break;
74
				break;
75
			case FormulaType.ARRAY:
76
				rootNodeOperandClass = Ptg.CLASS_ARRAY;
77
				break;
75
            case FormulaType.NAMEDRANGE:
78
            case FormulaType.NAMEDRANGE:
76
			case FormulaType.DATAVALIDATION_LIST:
79
			case FormulaType.DATAVALIDATION_LIST:
77
				rootNodeOperandClass = Ptg.CLASS_REF;
80
				rootNodeOperandClass = Ptg.CLASS_REF;
(-)a/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java (-1 / +7 lines)
Lines 44-58 public final class OperationEvaluationContext { Link Here
44
	private final int _columnIndex;
44
	private final int _columnIndex;
45
	private final EvaluationTracker _tracker;
45
	private final EvaluationTracker _tracker;
46
	private final WorkbookEvaluator _bookEvaluator;
46
	private final WorkbookEvaluator _bookEvaluator;
47
	private final boolean _inArrayFormulaContext;
47
48
48
	public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, EvaluationWorkbook workbook, int sheetIndex, int srcRowNum,
49
	public OperationEvaluationContext(WorkbookEvaluator bookEvaluator, EvaluationWorkbook workbook, int sheetIndex, int srcRowNum,
49
			int srcColNum, EvaluationTracker tracker) {
50
			int srcColNum, EvaluationTracker tracker, boolean inArrayFormulaContext) {
50
		_bookEvaluator = bookEvaluator;
51
		_bookEvaluator = bookEvaluator;
51
		_workbook = workbook;
52
		_workbook = workbook;
52
		_sheetIndex = sheetIndex;
53
		_sheetIndex = sheetIndex;
53
		_rowIndex = srcRowNum;
54
		_rowIndex = srcRowNum;
54
		_columnIndex = srcColNum;
55
		_columnIndex = srcColNum;
55
		_tracker = tracker;
56
		_tracker = tracker;
57
		_inArrayFormulaContext = inArrayFormulaContext;
56
	}
58
	}
57
59
58
	public EvaluationWorkbook getWorkbook() {
60
	public EvaluationWorkbook getWorkbook() {
Lines 67-72 public final class OperationEvaluationContext { Link Here
67
		return _columnIndex;
69
		return _columnIndex;
68
	}
70
	}
69
71
72
	public boolean isInArrayFormulaContext() {
73
		return _inArrayFormulaContext;
74
	}
75
70
	SheetRefEvaluator createExternSheetRefEvaluator(ExternSheetReferenceToken ptg) {
76
	SheetRefEvaluator createExternSheetRefEvaluator(ExternSheetReferenceToken ptg) {
71
		return createExternSheetRefEvaluator(ptg.getExternSheetIndex());
77
		return createExternSheetRefEvaluator(ptg.getExternSheetIndex());
72
	}
78
	}
(-)a/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java (-9 / +37 lines)
Lines 52-65 import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval; Link Here
52
import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
52
import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
53
import org.apache.poi.hssf.record.formula.eval.ValueEval;
53
import org.apache.poi.hssf.record.formula.eval.ValueEval;
54
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
54
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
55
import org.apache.poi.hssf.record.formula.functions.ArrayMode;
55
import org.apache.poi.hssf.record.formula.functions.Function;
56
import org.apache.poi.hssf.record.formula.functions.Function;
56
import org.apache.poi.hssf.record.formula.functions.Indirect;
57
import org.apache.poi.hssf.record.formula.functions.Indirect;
58
import org.apache.poi.ss.usermodel.ArrayFormulaEvaluatorHelper;
57
59
58
/**
60
/**
59
 * This class creates <tt>OperationEval</tt> instances to help evaluate <tt>OperationPtg</tt>
61
 * This class creates <tt>OperationEval</tt> instances to help evaluate <tt>OperationPtg</tt>
60
 * formula tokens.
62
 * formula tokens.
61
 *
63
 *
62
 * @author Josh Micich
64
 * @author Josh Micich
65
 * @author Petr Udalau(Petr.Udalau at exigenservices.com) - evaluations of array formulas
63
 */
66
 */
64
final class OperationEvaluatorFactory {
67
final class OperationEvaluatorFactory {
65
68
Lines 110-125 final class OperationEvaluatorFactory { Link Here
110
	 */
113
	 */
111
	public static ValueEval evaluate(OperationPtg ptg, ValueEval[] args,
114
	public static ValueEval evaluate(OperationPtg ptg, ValueEval[] args,
112
			OperationEvaluationContext ec) {
115
			OperationEvaluationContext ec) {
113
		if(ptg == null) {
116
		if (ptg == null) {
114
			throw new IllegalArgumentException("ptg must not be null");
117
			throw new IllegalArgumentException("ptg must not be null");
115
		}
118
		}
116
		Function result = _instancesByPtgClass.get(ptg);
119
		Function func = _instancesByPtgClass.get(ptg);
117
120
118
		if (result != null) {
121
		if (func == null && ptg instanceof AbstractFunctionPtg) {
119
			return  result.evaluate(args, ec.getRowIndex(), (short) ec.getColumnIndex());
120
		}
121
122
		if (ptg instanceof AbstractFunctionPtg) {
123
			AbstractFunctionPtg fptg = (AbstractFunctionPtg)ptg;
122
			AbstractFunctionPtg fptg = (AbstractFunctionPtg)ptg;
124
			int functionIndex = fptg.getFunctionIndex();
123
			int functionIndex = fptg.getFunctionIndex();
125
			switch (functionIndex) {
124
			switch (functionIndex) {
Lines 128-136 final class OperationEvaluatorFactory { Link Here
128
				case FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL:
127
				case FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL:
129
					return UserDefinedFunction.instance.evaluate(args, ec);
128
					return UserDefinedFunction.instance.evaluate(args, ec);
130
			}
129
			}
131
130
			func = FunctionEval.getBasicFunction(functionIndex);
132
			return FunctionEval.getBasicFunction(functionIndex).evaluate(args, ec.getRowIndex(), (short) ec.getColumnIndex());
131
		}
132
		if (func != null) {
133
			if (func instanceof ArrayMode && ec.isInArrayFormulaContext()) {
134
				return evaluateInSpecialModeForArrayFormulas((ArrayMode) func, args, ec);
135
			} // TODO - else not array ... still invoking array though ?
136
			return invokeOperationInArrayContext(func, args, ec);
133
		}
137
		}
134
		throw new RuntimeException("Unexpected operation ptg class (" + ptg.getClass().getName() + ")");
138
		throw new RuntimeException("Unexpected operation ptg class (" + ptg.getClass().getName() + ")");
135
	}
139
	}
140
141
	private static ValueEval evaluateInSpecialModeForArrayFormulas(ArrayMode function, ValueEval[] ops,
142
			OperationEvaluationContext ec) {
143
		return function.evaluateInArrayFormula(ops, ec.getRowIndex(), ec.getColumnIndex());
144
	}
145
146
	private static ValueEval invokeOperationInArrayContext(Function func, ValueEval[] ops, OperationEvaluationContext ec) {
147
		boolean isArrayFormula = ec.isInArrayFormulaContext();
148
		ArrayEval arrayResult = ArrayFormulaEvaluatorHelper.prepareEmptyResult(func, ops, isArrayFormula);
149
		int srcRowIndex = ec.getRowIndex();
150
		int srcColIndex = ec.getColumnIndex();
151
		if (arrayResult == null) {
152
			return func.evaluate(ops, srcRowIndex, srcColIndex);
153
		}
154
		ValueEval[][] values = arrayResult.getArrayValues();
155
		for (int row = 0; row < values.length; row++) {
156
			for (int col = 0; col < values[row].length; col++) {
157
				ValueEval[] singleInvocationArgs = ArrayFormulaEvaluatorHelper.prepareArgsForLoop(func, ops, row, col);
158
				ValueEval elemResult = func.evaluate(singleInvocationArgs, srcRowIndex, srcColIndex);
159
				values[row][col] = WorkbookEvaluator.dereferenceValue(elemResult, srcRowIndex, srcColIndex);
160
			}
161
		}
162
		return arrayResult;
163
	}
136
}
164
}
(-)a/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java (-15 / +82 lines)
Lines 21-29 import java.util.IdentityHashMap; Link Here
21
import java.util.Map;
21
import java.util.Map;
22
import java.util.Stack;
22
import java.util.Stack;
23
23
24
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
24
import org.apache.poi.hssf.record.formula.Area3DPtg;
25
import org.apache.poi.hssf.record.formula.Area3DPtg;
25
import org.apache.poi.hssf.record.formula.AreaErrPtg;
26
import org.apache.poi.hssf.record.formula.AreaErrPtg;
26
import org.apache.poi.hssf.record.formula.AreaPtg;
27
import org.apache.poi.hssf.record.formula.AreaPtg;
28
import org.apache.poi.hssf.record.formula.ArrayPtg;
27
import org.apache.poi.hssf.record.formula.AttrPtg;
29
import org.apache.poi.hssf.record.formula.AttrPtg;
28
import org.apache.poi.hssf.record.formula.BoolPtg;
30
import org.apache.poi.hssf.record.formula.BoolPtg;
29
import org.apache.poi.hssf.record.formula.ControlPtg;
31
import org.apache.poi.hssf.record.formula.ControlPtg;
Lines 60-65 import org.apache.poi.hssf.record.formula.eval.NumberEval; Link Here
60
import org.apache.poi.hssf.record.formula.eval.RefEval;
62
import org.apache.poi.hssf.record.formula.eval.RefEval;
61
import org.apache.poi.hssf.record.formula.eval.StringEval;
63
import org.apache.poi.hssf.record.formula.eval.StringEval;
62
import org.apache.poi.hssf.record.formula.eval.ValueEval;
64
import org.apache.poi.hssf.record.formula.eval.ValueEval;
65
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
63
import org.apache.poi.hssf.record.formula.functions.Choose;
66
import org.apache.poi.hssf.record.formula.functions.Choose;
64
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
67
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
65
import org.apache.poi.hssf.record.formula.functions.If;
68
import org.apache.poi.hssf.record.formula.functions.If;
Lines 67-72 import org.apache.poi.hssf.record.formula.udf.UDFFinder; Link Here
67
import org.apache.poi.hssf.util.CellReference;
70
import org.apache.poi.hssf.util.CellReference;
68
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException;
71
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException;
69
import org.apache.poi.ss.formula.eval.NotImplementedException;
72
import org.apache.poi.ss.formula.eval.NotImplementedException;
73
import org.apache.poi.ss.usermodel.ArrayFormulaEvaluatorHelper;
70
import org.apache.poi.ss.usermodel.Cell;
74
import org.apache.poi.ss.usermodel.Cell;
71
75
72
/**
76
/**
Lines 203-209 public final class WorkbookEvaluator { Link Here
203
207
204
	public ValueEval evaluate(EvaluationCell srcCell) {
208
	public ValueEval evaluate(EvaluationCell srcCell) {
205
		int sheetIndex = getSheetIndex(srcCell.getSheet());
209
		int sheetIndex = getSheetIndex(srcCell.getSheet());
206
		return evaluateAny(srcCell, sheetIndex, srcCell.getRowIndex(), srcCell.getColumnIndex(), new EvaluationTracker(_cache));
210
        return evaluateAny(srcCell, sheetIndex, srcCell.getRowIndex(), srcCell.getColumnIndex(), new EvaluationTracker(_cache));
207
	}
211
	}
208
212
209
	/**
213
	/**
Lines 251-257 public final class WorkbookEvaluator { Link Here
251
			if (!tracker.startEvaluate(cce)) {
255
			if (!tracker.startEvaluate(cce)) {
252
				return ErrorEval.CIRCULAR_REF_ERROR;
256
				return ErrorEval.CIRCULAR_REF_ERROR;
253
			}
257
			}
254
			OperationEvaluationContext ec = new OperationEvaluationContext(this, _workbook, sheetIndex, rowIndex, columnIndex, tracker);
258
			OperationEvaluationContext ec = new OperationEvaluationContext(this, _workbook, sheetIndex, rowIndex,
259
                    columnIndex, tracker, srcCell.isArrayFormula());
255
260
256
			try {
261
			try {
257
262
Lines 271-277 public final class WorkbookEvaluator { Link Here
271
				tracker.endEvaluate(cce);
276
				tracker.endEvaluate(cce);
272
			}
277
			}
273
		} else {
278
		} else {
274
			if(evalListener != null) {
279
			if (evalListener != null) {
275
				evalListener.onCacheHit(sheetIndex, rowIndex, columnIndex, cce.getValue());
280
				evalListener.onCacheHit(sheetIndex, rowIndex, columnIndex, cce.getValue());
276
			}
281
			}
277
			return cce.getValue();
282
			return cce.getValue();
Lines 333-338 public final class WorkbookEvaluator { Link Here
333
	// visibility raised for testing
338
	// visibility raised for testing
334
	/* package */ ValueEval evaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) {
339
	/* package */ ValueEval evaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) {
335
340
341
		Stack<Boolean> stackSkip = new Stack<Boolean>();
342
		Boolean isSkipActive = Boolean.TRUE;
336
		Stack<ValueEval> stack = new Stack<ValueEval>();
343
		Stack<ValueEval> stack = new Stack<ValueEval>();
337
		for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
344
		for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
338
345
Lines 347-357 public final class WorkbookEvaluator { Link Here
347
				}
354
				}
348
				if (attrPtg.isOptimizedChoose()) {
355
				if (attrPtg.isOptimizedChoose()) {
349
					ValueEval arg0 = stack.pop();
356
					ValueEval arg0 = stack.pop();
357
					if (arg0 instanceof ArrayEval && ((ArrayEval)arg0).checkBooleanContent()== ArrayEval.BooleanContent.MIXED) {
358
						stack.push(arg0);
359
						stackSkip.push(isSkipActive);
360
						// Switch off skip option only for this level
361
						isSkipActive = Boolean.FALSE;
362
						continue;
363
					}
364
					ValueEval cond = arg0;
365
					if (arg0 instanceof ArrayEval) {
366
						cond = ((ArrayEval)arg0).getValue(0,0);
367
					}
350
					int[] jumpTable = attrPtg.getJumpTable();
368
					int[] jumpTable = attrPtg.getJumpTable();
351
					int dist;
369
					int dist;
352
					int nChoices = jumpTable.length;
370
					int nChoices = jumpTable.length;
353
					try {
371
					try {
354
						int switchIndex = Choose.evaluateFirstArg(arg0, ec.getRowIndex(), ec.getColumnIndex());
372
						int switchIndex = Choose.evaluateFirstArg(cond, ec.getRowIndex(), ec.getColumnIndex());
355
						if (switchIndex<1 || switchIndex > nChoices) {
373
						if (switchIndex<1 || switchIndex > nChoices) {
356
							stack.push(ErrorEval.VALUE_INVALID);
374
							stack.push(ErrorEval.VALUE_INVALID);
357
							dist = attrPtg.getChooseFuncOffset() + 4; // +4 for tFuncFar(CHOOSE)
375
							dist = attrPtg.getChooseFuncOffset() + 4; // +4 for tFuncFar(CHOOSE)
Lines 371-378 public final class WorkbookEvaluator { Link Here
371
				if (attrPtg.isOptimizedIf()) {
389
				if (attrPtg.isOptimizedIf()) {
372
					ValueEval arg0 = stack.pop();
390
					ValueEval arg0 = stack.pop();
373
					boolean evaluatedPredicate;
391
					boolean evaluatedPredicate;
392
					if (arg0 instanceof ArrayEval && ((ArrayEval)arg0).checkBooleanContent()== ArrayEval.BooleanContent.MIXED) {
393
						stack.push(arg0);
394
						stackSkip.push(isSkipActive);
395
						// Switch off skip option only for this level
396
						isSkipActive = new Boolean(false);
397
						continue;
398
					}
399
					ValueEval cond = arg0;
400
					if (arg0 instanceof ArrayEval) {
401
						cond = ((ArrayEval)arg0).getValue(0,0);
402
					}
374
					try {
403
					try {
375
						evaluatedPredicate = If.evaluateFirstArg(arg0, ec.getRowIndex(), ec.getColumnIndex());
404
						evaluatedPredicate = If.evaluateFirstArg(cond, ec.getRowIndex(), ec.getColumnIndex());
376
					} catch (EvaluationException e) {
405
					} catch (EvaluationException e) {
377
						stack.push(e.getErrorEval());
406
						stack.push(e.getErrorEval());
378
						int dist = attrPtg.getData();
407
						int dist = attrPtg.getData();
Lines 397-407 public final class WorkbookEvaluator { Link Here
397
					continue;
426
					continue;
398
				}
427
				}
399
				if (attrPtg.isSkip()) {
428
				if (attrPtg.isSkip()) {
400
					int dist = attrPtg.getData()+1;
429
					if (isSkipActive.booleanValue()) {
401
					i+= countTokensToBeSkipped(ptgs, i, dist);
430
						int dist = attrPtg.getData()+1;
402
					if (stack.peek() == MissingArgEval.instance) {
431
						i+= countTokensToBeSkipped(ptgs, i, dist);
403
						stack.pop();
432
						if (stack.peek() == MissingArgEval.instance) {
404
						stack.push(BlankEval.instance);
433
							stack.pop();
434
							stack.push(BlankEval.instance);
435
						}
405
					}
436
					}
406
					continue;
437
					continue;
407
				}
438
				}
Lines 435-440 public final class WorkbookEvaluator { Link Here
435
				}
466
				}
436
//				logDebug("invoke " + operation + " (nAgs=" + numops + ")");
467
//				logDebug("invoke " + operation + " (nAgs=" + numops + ")");
437
				opResult = OperationEvaluatorFactory.evaluate(optg, ops, ec);
468
				opResult = OperationEvaluatorFactory.evaluate(optg, ops, ec);
469
				if (!isSkipActive.booleanValue() && isSkipSensitive(optg)) {
470
					isSkipActive = stackSkip.pop();
471
				}
438
			} else {
472
			} else {
439
				opResult = getEvalForPtg(ptg, ec);
473
				opResult = getEvalForPtg(ptg, ec);
440
			}
474
			}
Lines 460-465 public final class WorkbookEvaluator { Link Here
460
	}
494
	}
461
495
462
	/**
496
	/**
497
	 *  Has this function "optimized" form?
498
	 * @return <code>true</code> if the operation can be optimised with tAttr tokens (if, choose, skip)
499
	 */
500
	private boolean isSkipSensitive(OperationPtg optg) {
501
		if (optg instanceof AbstractFunctionPtg){
502
			// Skip sensitive is only "optimized" function - just only "IF" and "choose"
503
			AbstractFunctionPtg fptg = (AbstractFunctionPtg)optg;
504
			switch (fptg.getFunctionIndex()) {
505
				case FunctionMetadataRegistry.FUNCTION_INDEX_IF:
506
				case FunctionMetadataRegistry.FUNCTION_INDEX_CHOOSE:
507
					return true;
508
			}
509
		}
510
		return false;
511
	}
512
513
	/**
463
	 * Calculates the number of tokens that the evaluator should skip upon reaching a tAttrSkip.
514
	 * Calculates the number of tokens that the evaluator should skip upon reaching a tAttrSkip.
464
	 *
515
	 *
465
	 * @return the number of tokens (starting from <tt>startIndex+1</tt>) that need to be skipped
516
	 * @return the number of tokens (starting from <tt>startIndex+1</tt>) that need to be skipped
Lines 481-492 public final class WorkbookEvaluator { Link Here
481
		return index-startIndex;
532
		return index-startIndex;
482
	}
533
	}
483
	/**
534
	/**
535
	 * Dereferences a single value from any ArrayEval evaluation result.
536
	 * @param evaluationResult
537
	 * @param cell
538
	 * @return
539
	 */
540
	private static ValueEval dereferenceValue(ArrayEval evaluationResult, EvaluationCell evalCell ) {
541
		Cell cell = (Cell)evalCell.getIdentityKey();
542
		return ArrayFormulaEvaluatorHelper.dereferenceValue(evaluationResult,cell);
543
	}
544
	/**
484
	 * Dereferences a single value from any AreaEval or RefEval evaluation result.
545
	 * Dereferences a single value from any AreaEval or RefEval evaluation result.
485
	 * If the supplied evaluationResult is just a plain value, it is returned as-is.
546
	 * If the supplied evaluationResult is just a plain value, it is returned as-is.
486
	 * @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
547
	 * @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
487
	 *  <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
548
	 *  <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
488
	 */
549
	 */
489
	private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, int srcColNum) {
550
	public static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, int srcColNum) {
490
		if (evaluationResult instanceof RefEval) {
551
		if (evaluationResult instanceof RefEval) {
491
			RefEval rv = (RefEval) evaluationResult;
552
			RefEval rv = (RefEval) evaluationResult;
492
			return rv.getInnerValueEval();
553
			return rv.getInnerValueEval();
Lines 494-500 public final class WorkbookEvaluator { Link Here
494
		if (evaluationResult instanceof AreaEval) {
555
		if (evaluationResult instanceof AreaEval) {
495
			AreaEval ae = (AreaEval) evaluationResult;
556
			AreaEval ae = (AreaEval) evaluationResult;
496
			if (ae.isRow()) {
557
			if (ae.isRow()) {
497
				if(ae.isColumn()) {
558
				if (ae.isColumn()) {
498
					return ae.getRelativeValue(0, 0);
559
					return ae.getRelativeValue(0, 0);
499
				}
560
				}
500
				return ae.getAbsoluteValue(ae.getFirstRow(), srcColNum);
561
				return ae.getAbsoluteValue(ae.getFirstRow(), srcColNum);
Lines 572-578 public final class WorkbookEvaluator { Link Here
572
			AreaPtg aptg = (AreaPtg) ptg;
633
			AreaPtg aptg = (AreaPtg) ptg;
573
			return ec.getAreaEval(aptg.getFirstRow(), aptg.getFirstColumn(), aptg.getLastRow(), aptg.getLastColumn());
634
			return ec.getAreaEval(aptg.getFirstRow(), aptg.getFirstColumn(), aptg.getLastRow(), aptg.getLastColumn());
574
		}
635
		}
575
636
		if (ptg instanceof ArrayPtg) {
637
			return new ArrayEval((ArrayPtg)ptg);
638
		}
576
		if (ptg instanceof UnknownPtg) {
639
		if (ptg instanceof UnknownPtg) {
577
			// POI uses UnknownPtg when the encoded Ptg array seems to be corrupted.
640
			// POI uses UnknownPtg when the encoded Ptg array seems to be corrupted.
578
			// This seems to occur in very rare cases (e.g. unused name formulas in bug 44774, attachment 21790)
641
			// This seems to occur in very rare cases (e.g. unused name formulas in bug 44774, attachment 21790)
Lines 597-607 public final class WorkbookEvaluator { Link Here
597
	/**
660
	/**
598
	 * Used by the lazy ref evals whenever they need to get the value of a contained cell.
661
	 * Used by the lazy ref evals whenever they need to get the value of a contained cell.
599
	 */
662
	 */
600
	/* package */ ValueEval evaluateReference(EvaluationSheet sheet, int sheetIndex, int rowIndex,
663
	ValueEval evaluateReference(EvaluationSheet sheet, int sheetIndex, int rowIndex,
601
			int columnIndex, EvaluationTracker tracker) {
664
			int columnIndex, EvaluationTracker tracker) {
602
665
603
		EvaluationCell cell = sheet.getCell(rowIndex, columnIndex);
666
		EvaluationCell cell = sheet.getCell(rowIndex, columnIndex);
604
		return evaluateAny(cell, sheetIndex, rowIndex, columnIndex, tracker);
667
		ValueEval result = evaluateAny(cell, sheetIndex, rowIndex, columnIndex, tracker);
668
		if (result instanceof ArrayEval && cell.isArrayFormula()) {
669
			result = dereferenceValue((ArrayEval) result, cell);
670
		}
671
		return result;
605
	}
672
	}
606
	public FreeRefFunction findUserDefinedFunction(String functionName) {
673
	public FreeRefFunction findUserDefinedFunction(String functionName) {
607
		return _udfFinder.findFunction(functionName);
674
		return _udfFinder.findFunction(functionName);
(-)a/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationCell.java (+4 lines)
Lines 129-132 final class ForkedEvaluationCell implements EvaluationCell { Link Here
129
	public int getColumnIndex() {
129
	public int getColumnIndex() {
130
		return _masterCell.getColumnIndex();
130
		return _masterCell.getColumnIndex();
131
	}
131
	}
132
133
	public boolean isArrayFormula() {
134
		return _masterCell.isArrayFormula();
135
	}
132
}
136
}
(-)a/src/java/org/apache/poi/ss/usermodel/ArrayFormulaEvaluatorHelper.java (+289 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.ss.usermodel;
19
20
import org.apache.poi.hssf.record.formula.eval.AreaEval;
21
import org.apache.poi.hssf.record.formula.eval.BoolEval;
22
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
23
import org.apache.poi.hssf.record.formula.eval.NumberEval;
24
import org.apache.poi.hssf.record.formula.eval.StringEval;
25
import org.apache.poi.hssf.record.formula.eval.ValueEval;
26
import org.apache.poi.hssf.record.formula.functions.Function;
27
import org.apache.poi.hssf.record.formula.functions.FunctionWithArraySupport;
28
import org.apache.poi.ss.formula.ArrayEval;
29
import org.apache.poi.ss.formula.TwoDEval;
30
import org.apache.poi.ss.util.CellRangeAddress;
31
32
/**
33
 * Helper class to manipulate with array formula
34
 * This class contains methods, common for HSSF and XSSF FormulaEvaluator
35
 * Better solution - to have abstract class for FormulaEvaluator Interface Implementation
36
 *  All method need to be static
37
 *
38
 * @author Vladimirs Abramovs (Vladimirs.Abramovs at exigenservices.com)
39
 */
40
public class ArrayFormulaEvaluatorHelper {
41
	private static final CellValue CELL_VALUE_NA = CellValue.getError(ErrorConstants.ERROR_NA);
42
	final static int SCALAR_TYPE = 0;
43
	final static int ARRAY_TYPE = 1;
44
	private static final ValueEval ERROR_EVAL_NA = ErrorEval.NA;
45
46
47
	private ArrayFormulaEvaluatorHelper () {
48
		// has no instance
49
	}
50
51
52
	/** Transform evaluated Array Values according aimed Range
53
	 *   Depending on dimensions correlation,
54
	 *   result array may be restricted
55
	 *	or his single row/column will cloned
56
	 *	or some cell will set to #N/A
57
	 * @param cvs
58
	 * @param range
59
	 * @return
60
	 */
61
	public static ValueEval[][] transformToRange(ValueEval[][] cvs, CellRangeAddress range) {
62
		return (ValueEval[][]) transformToRange(cvs, range, false);
63
	}
64
	/** Transform evaluated Array Values according aimed Range
65
	 *   Depending on dimensions correlation,
66
	 *   result array may be restricted
67
	 *	or his single row/column will cloned
68
	 *	or some cell will set to #N/A
69
	 * @param cvs
70
	 * @param range
71
	 * @return
72
	 */
73
	public static CellValue[][] transformToRange(CellValue[][] cvs, CellRangeAddress range) {
74
		return (CellValue[][]) transformToRange(cvs, range, true);
75
	}
76
	/** Transform evaluated Array Values according aimed Range
77
	 *   Depending on dimensions correlation,
78
	 *   result array may be restricted
79
	 *	or his single row/column will cloned
80
	 *	or some cell will set to #N/A
81
	 * @param cvs
82
	 * @param range
83
	 * @return
84
	 */
85
	private static Object[][] transformToRange(Object[][] cvs, CellRangeAddress range,
86
			boolean isCellValue) {
87
88
		int nRows = range.getLastRow() - range.getFirstRow() + 1;
89
		int nColumns = range.getLastColumn() - range.getFirstColumn() + 1;
90
		Object[][] result = isCellValue ? new CellValue[nRows][nColumns] : new ValueEval[nRows][nColumns];
91
		int rowStart = range.getFirstRow();
92
		int colStart = range.getFirstColumn();
93
		for (int i = rowStart; i <= range.getLastRow(); i++) {
94
			for (int j = colStart; j <= range.getLastColumn(); j++) {
95
				Object value;
96
				if (i - rowStart < cvs.length && j - colStart < cvs[i - rowStart].length) {
97
					value = cvs[i - rowStart][j - colStart];
98
				} else {
99
					boolean needClone = false;
100
					int cloneRow = 0;
101
					int cloneCol = 0;
102
					if (cvs.length == 1) { // Need to clone first colm of cvs
103
						cloneCol = j - colStart;
104
						needClone = true;
105
					}
106
					if (cvs[0].length == 1) { // Need to clone first row of cvs
107
						cloneRow = i - rowStart;
108
						needClone = true;
109
					}
110
					if (needClone && cloneCol < cvs[0].length && cloneRow < cvs.length) {
111
						value = cvs[cloneRow][cloneCol];
112
					} else {
113
						// For other cases set cell value to #N/A
114
						// For those cells we changes also their type to Error
115
						value = isCellValue ? CELL_VALUE_NA : ErrorEval.NA;
116
					}
117
				}
118
				result[i - rowStart][j - colStart] = value;
119
			}
120
		}
121
		return result;
122
	}
123
124
	/**
125
	 * Convert Eval value to CellValue
126
	 * @param val
127
	 * @return
128
	 */
129
	public static CellValue evalToCellValue(ValueEval val) {
130
		if (val instanceof BoolEval) {
131
			return CellValue.valueOf(((BoolEval)val).getBooleanValue());
132
		}
133
		if (val instanceof NumberEval) {
134
			return  new CellValue(((NumberEval)val).getNumberValue());
135
		}
136
		if (val instanceof StringEval) {
137
			return  new CellValue(((StringEval)val).getStringValue());
138
		}
139
		if (val instanceof ErrorEval) {
140
			return  new CellValue(((ErrorEval)val).getErrorCode());
141
		}
142
		throw new IllegalStateException("Unexpected value (" + val + ")");
143
	}
144
	/**
145
	 * Get single value from ArrayEval  for desired cell
146
	 * @param evaluationResult
147
	 * @param cell
148
	 * @return
149
	 */
150
	public static ValueEval dereferenceValue(ArrayEval evaluationResult, Cell cell) {
151
		CellRangeAddress range = cell.getArrayFormulaRange();
152
		ValueEval[][] rangeVal = ArrayFormulaEvaluatorHelper.transformToRange(evaluationResult.getArrayValues(),range);
153
		int rowInArray = cell.getRowIndex()- range.getFirstRow();
154
		int colInArray = cell.getColumnIndex() - range.getFirstColumn();
155
		return rangeVal[rowInArray][colInArray];
156
	}
157
	/**
158
	 * Get type of parameter (SCALAR_TYPE or ARRAY_TYPE) which support function
159
	 * for given argument
160
	 *
161
	 * @param function
162
	 * @param argIndex
163
	 * @return
164
	 */
165
	public static int getParameterType(Function function, int argIndex) {
166
167
		if (function instanceof FunctionWithArraySupport) {
168
			// ask new interface(ZS) for argument type
169
			if (((FunctionWithArraySupport) function).supportArray(argIndex)) {
170
				return ARRAY_TYPE;
171
			}
172
		}
173
		return SCALAR_TYPE;
174
	}
175
176
	/**
177
	 * Prepare empty template, which will keep result of evaluation
178
	 *   A few arguments of function may be array.
179
	 *   In this case result array will have
180
	 *   dimension as  such arguments intersection.
181
	 *   This method calculates result's dimension and prepare empty results
182
	 *
183
	 *
184
	 * @param function
185
	 * @param args
186
	 * @param arrayFormula
187
	 * @return <code>null</code> if function returns a scalar result
188
	 */
189
	public static ArrayEval prepareEmptyResult(Function function, ValueEval[] args, boolean arrayFormula) {
190
		boolean foundArrayArg = false;
191
		int rowCount = Integer.MIN_VALUE;
192
		int colCount = Integer.MIN_VALUE;
193
194
		for (int i = 0; i < args.length; i++) {
195
			int argRowCount = Integer.MIN_VALUE;
196
			int argColCount = Integer.MIN_VALUE;
197
			if (getParameterType(function, i) == SCALAR_TYPE) {
198
				ValueEval arg = args[i];
199
				if (arg instanceof ArrayEval) {
200
					ArrayEval aa = (ArrayEval) arg;
201
					argRowCount = aa.getHeight();
202
					argColCount = aa.getWidth();
203
				} else if (arg instanceof AreaEval && arrayFormula) {
204
					AreaEval aa = (AreaEval) arg;
205
					argRowCount = aa.getHeight();
206
					argColCount = aa.getWidth();
207
				} else {
208
					continue; // Arguments is not array - just skip it
209
				}
210
				foundArrayArg = true;
211
				rowCount = Math.max(rowCount, argRowCount);
212
				colCount = Math.max(colCount, argColCount);
213
			}
214
		}
215
216
		if (!foundArrayArg) {
217
			return null;
218
		}
219
220
		ValueEval[][] emptyArray = new ValueEval[rowCount][colCount];
221
		return new ArrayEval(emptyArray);
222
	}
223
224
	/**
225
	 * Prepare arguments for next iteration to call function
226
	 *  Each argument may be scalar, full array or element(according iteration) of array
227
	 *
228
	 * @param function
229
	 * @param args
230
	 * @param i
231
	 * @param j
232
	 * @return
233
	 */
234
	public static ValueEval[] prepareArgsForLoop(Function function, ValueEval[] args, int i, int j) {
235
		ValueEval[] answer = new ValueEval[args.length];
236
		for (int argIn = 0; argIn < args.length; argIn++) {
237
			ValueEval arg = args[argIn];
238
239
			if (getParameterType(function, argIn) == SCALAR_TYPE) {
240
				if (arg instanceof TwoDEval) {
241
					arg = getArrayValue((TwoDEval) arg, i, j);
242
				}
243
			}
244
			answer[argIn] = arg;
245
		}
246
247
		return answer;
248
	}
249
250
	private static ValueEval getArrayValue(TwoDEval tde, int pRowIndex, int pColIndex) {
251
		int rowIndex;
252
		if (pRowIndex >= tde.getHeight()) {
253
			if (!tde.isRow()) {
254
				return ERROR_EVAL_NA;
255
			}
256
			rowIndex = 0;
257
		} else {
258
			rowIndex = pRowIndex;
259
		}
260
		int colIndex;
261
		if (pColIndex >= tde.getWidth()) {
262
			if (!tde.isColumn()) {
263
				return ERROR_EVAL_NA;
264
			}
265
			colIndex = 0;
266
		} else {
267
			colIndex = pColIndex;
268
		}
269
		return tde.getValue(rowIndex, colIndex);
270
	}
271
272
273
	/**
274
	 * check if ops contain arrays and those should be iterated
275
	 *
276
	 * @param function
277
	 * @param ops
278
	 * @return
279
	 */
280
	public static boolean checkForArrays(Function function, ValueEval[] ops) {
281
282
		for (int i = 0; i < ops.length; i++) {
283
			if ((ops[i] instanceof ArrayEval) && (getParameterType(function, i) == SCALAR_TYPE)) {
284
				return true;
285
			}
286
		}
287
		return false;
288
	}
289
}
(-)a/src/java/org/apache/poi/ss/usermodel/Cell.java (+12 lines)
Lines 21-26 import java.util.Calendar; Link Here
21
import java.util.Date;
21
import java.util.Date;
22
22
23
import org.apache.poi.ss.formula.FormulaParseException;
23
import org.apache.poi.ss.formula.FormulaParseException;
24
import org.apache.poi.ss.util.CellRangeAddress;
24
25
25
/**
26
/**
26
 * High level representation of a cell in a row of a spreadsheet.
27
 * High level representation of a cell in a row of a spreadsheet.
Lines 372-375 public interface Cell { Link Here
372
     * @param link hypelrink associated with this cell
373
     * @param link hypelrink associated with this cell
373
     */
374
     */
374
    void setHyperlink(Hyperlink link);
375
    void setHyperlink(Hyperlink link);
376
377
    /**
378
     * Only valid for array formula cells
379
     * @return range of the array formula group that the cell belongs to.
380
     */
381
    CellRangeAddress getArrayFormulaRange();
382
383
    /**
384
     * @return <code>true</code> if this cell is part of group of cells having a common array formula.
385
     */
386
    boolean isPartOfArrayFormulaGroup();
375
}
387
}
(-)a/src/java/org/apache/poi/ss/usermodel/Sheet.java (+15 lines)
Lines 781-784 public interface Sheet extends Iterable<Row> { Link Here
781
     */
781
     */
782
    boolean isSelected();
782
    boolean isSelected();
783
783
784
785
    /**
786
     * Sets array formula to specified region for result.
787
     *
788
     * @param formula Formula
789
     * @param range Region of array formula for result.
790
     */
791
    void setArrayFormula(String formula, CellRangeAddress range);
792
793
    /**
794
     * Remove a Array Formula from this sheet.  All cells contained in the Array Formula range are removed as well
795
     *
796
     * @param cell   any cell within Array Formula range
797
     */
798
    void removeArrayFormula(Cell cell);
784
}
799
}
(-)a/src/java/org/apache/poi/ss/util/CellRangeAddress.java (-17 / +28 lines)
Lines 24-30 import org.apache.poi.util.LittleEndianOutput; Link Here
24
24
25
/**
25
/**
26
 * See OOO documentation: excelfileformat.pdf sec 2.5.14 - 'Cell Range Address'<p/>
26
 * See OOO documentation: excelfileformat.pdf sec 2.5.14 - 'Cell Range Address'<p/>
27
 * 
27
 *
28
 * Note - {@link SelectionRecord} uses the BIFF5 version of this structure
28
 * Note - {@link SelectionRecord} uses the BIFF5 version of this structure
29
 * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
29
 * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
30
 */
30
 */
Lines 51-57 public class CellRangeAddress extends CellRangeAddressBase { Link Here
51
		out.writeShort(getFirstColumn());
51
		out.writeShort(getFirstColumn());
52
		out.writeShort(getLastColumn());
52
		out.writeShort(getLastColumn());
53
	}
53
	}
54
	
54
55
	public CellRangeAddress(RecordInputStream in) {
55
	public CellRangeAddress(RecordInputStream in) {
56
		super(readUShortAndCheck(in), in.readUShort(), in.readUShort(), in.readUShort());
56
		super(readUShortAndCheck(in), in.readUShort(), in.readUShort(), in.readUShort());
57
	}
57
	}
Lines 72-91 public class CellRangeAddress extends CellRangeAddressBase { Link Here
72
		return numberOfItems * ENCODED_SIZE;
72
		return numberOfItems * ENCODED_SIZE;
73
	}
73
	}
74
74
75
    public String formatAsString() {
75
	public String formatAsString() {
76
        StringBuffer sb = new StringBuffer();
76
		StringBuffer sb = new StringBuffer();
77
        CellReference cellRefFrom = new CellReference(getFirstRow(), getFirstColumn());
77
		CellReference cellRefFrom = new CellReference(getFirstRow(), getFirstColumn());
78
        CellReference cellRefTo = new CellReference(getLastRow(), getLastColumn());
78
		CellReference cellRefTo = new CellReference(getLastRow(), getLastColumn());
79
        sb.append(cellRefFrom.formatAsString());
79
		sb.append(cellRefFrom.formatAsString());
80
        sb.append(':');
80
		sb.append(':');
81
        sb.append(cellRefTo.formatAsString());
81
		sb.append(cellRefTo.formatAsString());
82
        return sb.toString();
82
		return sb.toString();
83
    }
83
	}
84
84
85
    public static CellRangeAddress valueOf(String ref) {
85
	/**
86
        int sep = ref.indexOf(":");
86
	 * @param ref usually a standard area ref (e.g. "B1:D8").  May be a single cell
87
        CellReference cellFrom = new CellReference(ref.substring(0, sep));
87
	 *        ref (e.g. "B5") in which case the result is a 1 x 1 cell range.
88
        CellReference cellTo = new CellReference(ref.substring(sep + 1));
88
	 */
89
        return new CellRangeAddress(cellFrom.getRow(), cellTo.getRow(), cellFrom.getCol(), cellTo.getCol());
89
	public static CellRangeAddress valueOf(String ref) {
90
    }
90
		int sep = ref.indexOf(":");
91
		CellReference a;
92
		CellReference b;
93
		if (sep == -1) {
94
			a = new CellReference(ref);
95
			b = a;
96
		} else {
97
			a = new CellReference(ref.substring(0, sep));
98
			b = new CellReference(ref.substring(sep + 1));
99
		}
100
		return new CellRangeAddress(a.getRow(), b.getRow(), a.getCol(), b.getCol());
101
	}
91
}
102
}
(-)a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java (-3 / +31 lines)
Lines 37-42 import org.apache.poi.ss.usermodel.DateUtil; Link Here
37
import org.apache.poi.ss.usermodel.FormulaError;
37
import org.apache.poi.ss.usermodel.FormulaError;
38
import org.apache.poi.ss.usermodel.Hyperlink;
38
import org.apache.poi.ss.usermodel.Hyperlink;
39
import org.apache.poi.ss.usermodel.RichTextString;
39
import org.apache.poi.ss.usermodel.RichTextString;
40
import org.apache.poi.ss.util.CellRangeAddress;
40
import org.apache.poi.ss.util.CellReference;
41
import org.apache.poi.ss.util.CellReference;
41
import org.apache.poi.xssf.model.SharedStringsTable;
42
import org.apache.poi.xssf.model.SharedStringsTable;
42
import org.apache.poi.xssf.model.StylesTable;
43
import org.apache.poi.xssf.model.StylesTable;
Lines 344-350 public final class XSSFCell implements Cell { Link Here
344
        if(cellType != CELL_TYPE_FORMULA) throw typeMismatch(CELL_TYPE_FORMULA, cellType, false);
345
        if(cellType != CELL_TYPE_FORMULA) throw typeMismatch(CELL_TYPE_FORMULA, cellType, false);
345
346
346
        CTCellFormula f = _cell.getF();
347
        CTCellFormula f = _cell.getF();
347
        if(f.getT() == STCellFormulaType.SHARED){
348
        if (isPartOfArrayFormulaGroup() && f == null) {
349
            XSSFCell cell = getSheet().getFirstCellInArrayFormula(this);
350
            return cell.getCellFormula();
351
        }
352
        if (f.getT() == STCellFormulaType.SHARED) {
348
            return convertSharedFormula((int)f.getSi());
353
            return convertSharedFormula((int)f.getSi());
349
        }
354
        }
350
        return f.getStringValue();
355
        return f.getStringValue();
Lines 371-376 public final class XSSFCell implements Cell { Link Here
371
    }
376
    }
372
377
373
    public void setCellFormula(String formula) {
378
    public void setCellFormula(String formula) {
379
        setFormula(formula, FormulaType.CELL);
380
    }
381
382
    /* package */ void setCellArrayFormula(String formula, CellRangeAddress range) {
383
        setFormula(formula, FormulaType.ARRAY);
384
        CTCellFormula cellFormula = _cell.getF();
385
        cellFormula.setT(STCellFormulaType.ARRAY);
386
        cellFormula.setRef(range.formatAsString());
387
    }
388
389
    private void setFormula(String formula, int formulaType) {
374
        XSSFWorkbook wb = _row.getSheet().getWorkbook();
390
        XSSFWorkbook wb = _row.getSheet().getWorkbook();
375
        if (formula == null) {
391
        if (formula == null) {
376
            wb.onDeleteFormula(this);
392
            wb.onDeleteFormula(this);
Lines 380-386 public final class XSSFCell implements Cell { Link Here
380
396
381
        XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
397
        XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
382
        //validate through the FormulaParser
398
        //validate through the FormulaParser
383
        FormulaParser.parse(formula, fpb, FormulaType.CELL, wb.getSheetIndex(getSheet()));
399
        FormulaParser.parse(formula, fpb, formulaType, wb.getSheetIndex(getSheet()));
384
400
385
        CTCellFormula f =  CTCellFormula.Factory.newInstance();
401
        CTCellFormula f =  CTCellFormula.Factory.newInstance();
386
        f.setStringValue(formula);
402
        f.setStringValue(formula);
Lines 461-467 public final class XSSFCell implements Cell { Link Here
461
     */
477
     */
462
    public int getCellType() {
478
    public int getCellType() {
463
479
464
        if (_cell.getF() != null) {
480
        if (_cell.getF() != null || getSheet().isCellInArrayFormulaContext(this)) {
465
            return CELL_TYPE_FORMULA;
481
            return CELL_TYPE_FORMULA;
466
        }
482
        }
467
483
Lines 941-944 public final class XSSFCell implements Cell { Link Here
941
        }
957
        }
942
        throw new IllegalStateException("Unexpected formula result type (" + cellType + ")");
958
        throw new IllegalStateException("Unexpected formula result type (" + cellType + ")");
943
    }
959
    }
960
    public CellRangeAddress getArrayFormulaRange() {
961
        XSSFCell cell = getSheet().getFirstCellInArrayFormula(this);
962
        if (cell == null) {
963
            throw new IllegalStateException("not an array formula cell.");
964
        }
965
        String formulaRef = cell._cell.getF().getRef();
966
        return CellRangeAddress.valueOf(formulaRef);
967
    }
968
969
    public boolean isPartOfArrayFormulaGroup() {
970
        return getSheet().isCellInArrayFormulaContext(this);
971
    }
944
}
972
}
(-)a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationCell.java (-1 / +5 lines)
Lines 22-28 import org.apache.poi.ss.formula.EvaluationSheet; Link Here
22
22
23
/**
23
/**
24
 * XSSF wrapper for a cell under evaluation
24
 * XSSF wrapper for a cell under evaluation
25
 * 
25
 *
26
 * @author Josh Micich
26
 * @author Josh Micich
27
 */
27
 */
28
final class XSSFEvaluationCell implements EvaluationCell {
28
final class XSSFEvaluationCell implements EvaluationCell {
Lines 72-75 final class XSSFEvaluationCell implements EvaluationCell { Link Here
72
	public String getStringCellValue() {
72
	public String getStringCellValue() {
73
		return _cell.getRichStringCellValue().getString();
73
		return _cell.getRichStringCellValue().getString();
74
	}
74
	}
75
76
	public boolean isArrayFormula() {
77
		return _cell.isPartOfArrayFormulaGroup();
78
	}
75
}
79
}
(-)a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFormulaEvaluator.java (-9 / +109 lines)
Lines 25-37 import org.apache.poi.hssf.record.formula.eval.NumberEval; Link Here
25
import org.apache.poi.hssf.record.formula.eval.StringEval;
25
import org.apache.poi.hssf.record.formula.eval.StringEval;
26
import org.apache.poi.hssf.record.formula.eval.ValueEval;
26
import org.apache.poi.hssf.record.formula.eval.ValueEval;
27
import org.apache.poi.hssf.record.formula.udf.UDFFinder;
27
import org.apache.poi.hssf.record.formula.udf.UDFFinder;
28
import org.apache.poi.ss.formula.ArrayEval;
28
import org.apache.poi.ss.formula.IStabilityClassifier;
29
import org.apache.poi.ss.formula.IStabilityClassifier;
29
import org.apache.poi.ss.formula.WorkbookEvaluator;
30
import org.apache.poi.ss.formula.WorkbookEvaluator;
31
import org.apache.poi.ss.usermodel.ArrayFormulaEvaluatorHelper;
30
import org.apache.poi.ss.usermodel.Cell;
32
import org.apache.poi.ss.usermodel.Cell;
31
import org.apache.poi.ss.usermodel.CellValue;
33
import org.apache.poi.ss.usermodel.CellValue;
32
import org.apache.poi.ss.usermodel.FormulaEvaluator;
34
import org.apache.poi.ss.usermodel.FormulaEvaluator;
33
import org.apache.poi.ss.usermodel.Row;
35
import org.apache.poi.ss.usermodel.Row;
34
import org.apache.poi.ss.usermodel.Sheet;
36
import org.apache.poi.ss.usermodel.Sheet;
37
import org.apache.poi.ss.util.CellRangeAddress;
35
38
36
/**
39
/**
37
 * Evaluates formula cells.<p/>
40
 * Evaluates formula cells.<p/>
Lines 42-47 import org.apache.poi.ss.usermodel.Sheet; Link Here
42
 *
45
 *
43
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
46
 * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
44
 * @author Josh Micich
47
 * @author Josh Micich
48
 * @author Zahars Sulkins(Zahars.Sulkins at exigenservices.com) - Array Formula support
49
 * @author Vladimirs Abramovs(Vladimirs.Abramovs at exigenservices.com) - Array Formula support
45
 */
50
 */
46
public class XSSFFormulaEvaluator implements FormulaEvaluator {
51
public class XSSFFormulaEvaluator implements FormulaEvaluator {
47
52
Lines 54-63 public class XSSFFormulaEvaluator implements FormulaEvaluator { Link Here
54
	 * @param stabilityClassifier used to optimise caching performance. Pass <code>null</code>
59
	 * @param stabilityClassifier used to optimise caching performance. Pass <code>null</code>
55
	 * for the (conservative) assumption that any cell may have its definition changed after
60
	 * for the (conservative) assumption that any cell may have its definition changed after
56
	 * evaluation begins.
61
	 * evaluation begins.
57
	 * @deprecated (Sep 2009) (reduce overloading) use {@link #create(XSSFWorkbook, org.apache.poi.ss.formula.IStabilityClassifier, org.apache.poi.hssf.record.formula.udf.UDFFinder)} 
62
	 * @deprecated (Sep 2009) (reduce overloading) use {@link #create(XSSFWorkbook, org.apache.poi.ss.formula.IStabilityClassifier, org.apache.poi.hssf.record.formula.udf.UDFFinder)}
58
	 */
63
	 */
59
    @Deprecated
64
	@Deprecated
60
    public XSSFFormulaEvaluator(XSSFWorkbook workbook, IStabilityClassifier stabilityClassifier) {
65
	public XSSFFormulaEvaluator(XSSFWorkbook workbook, IStabilityClassifier stabilityClassifier) {
61
		_bookEvaluator = new WorkbookEvaluator(XSSFEvaluationWorkbook.create(workbook), stabilityClassifier, null);
66
		_bookEvaluator = new WorkbookEvaluator(XSSFEvaluationWorkbook.create(workbook), stabilityClassifier, null);
62
	}
67
	}
63
	private XSSFFormulaEvaluator(XSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
68
	private XSSFFormulaEvaluator(XSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
Lines 142-150 public class XSSFFormulaEvaluator implements FormulaEvaluator { Link Here
142
		if (cell == null || cell.getCellType() != XSSFCell.CELL_TYPE_FORMULA) {
147
		if (cell == null || cell.getCellType() != XSSFCell.CELL_TYPE_FORMULA) {
143
			return -1;
148
			return -1;
144
		}
149
		}
145
		CellValue cv = evaluateFormulaCellValue(cell);
146
		// cell remains a formula cell, but the cached value is changed
150
		// cell remains a formula cell, but the cached value is changed
147
		setCellValue(cell, cv);
151
		CellValue cv;
152
		if (cell.isPartOfArrayFormulaGroup()) { // Array Formula Context
153
			CellValue[][] cvs = evaluateFormulaCellArrayValues((XSSFCell) cell);
154
			CellValue[][] values = setCellValues(cell, cvs);
155
			int rowIndex = cell.getRowIndex() - cell.getArrayFormulaRange().getFirstRow();
156
			int colIndex = cell.getColumnIndex() - cell.getArrayFormulaRange().getFirstColumn();
157
			cv = values[rowIndex][colIndex];
158
		} else { // Single Formula
159
			cv = evaluateFormulaCellValue(cell);
160
			setCellValue(cell, cv);
161
		}
148
		return cv.getCellType();
162
		return cv.getCellType();
149
	}
163
	}
150
164
Lines 170-178 public class XSSFFormulaEvaluator implements FormulaEvaluator { Link Here
170
		}
184
		}
171
		XSSFCell result = (XSSFCell) cell;
185
		XSSFCell result = (XSSFCell) cell;
172
		if (cell.getCellType() == XSSFCell.CELL_TYPE_FORMULA) {
186
		if (cell.getCellType() == XSSFCell.CELL_TYPE_FORMULA) {
173
			CellValue cv = evaluateFormulaCellValue(cell);
187
			if (cell.isPartOfArrayFormulaGroup()) { // Array Formula Context
174
			setCellType(cell, cv); // cell will no longer be a formula cell
188
				CellValue[][] cvs = evaluateFormulaCellArrayValues((XSSFCell) cell);
175
			setCellValue(cell, cv);
189
				setCellsTypes(cell, cvs); // cells will no longer be a formula cell
190
				setCellValues(cell, cvs);
191
192
			} else { // Single Formula
193
				CellValue cv = evaluateFormulaCellValue(cell);
194
				setCellType(cell, cv); // cell will no longer be a formula cell
195
				setCellValue(cell, cv);
196
197
			}
176
		}
198
		}
177
		return result;
199
		return result;
178
	}
200
	}
Lines 217-222 public class XSSFFormulaEvaluator implements FormulaEvaluator { Link Here
217
		}
239
		}
218
	}
240
	}
219
241
242
	private void setCellsTypes(Cell cell, CellValue[][] cvs) {
243
		CellRangeAddress range = cell.getArrayFormulaRange();
244
		int rowStart = range.getFirstRow();
245
		int colStart = range.getFirstColumn();
246
		Sheet sheet = cell.getSheet();
247
		for (int i = rowStart; i <= range.getLastRow(); i++) {
248
			for (int j = colStart; j <= range.getLastColumn(); j++) {
249
				Row row = sheet.getRow(i);
250
				Cell c = row.getCell(j);
251
				if ((i - rowStart) < cvs.length && (j - colStart) < cvs[i - rowStart].length) {
252
					setCellType(c, cvs[i - rowStart][j - colStart]);
253
				}
254
			}
255
		}
256
	}
257
258
	/**
259
	 * Set value in Range
260
	 *
261
	 * @param cell
262
	 * @param cvs
263
	 * @return
264
	 */
265
	private CellValue[][] setCellValues(Cell cell, CellValue[][] cvs) {
266
		CellRangeAddress range = cell.getArrayFormulaRange();
267
		int rowStart = range.getFirstRow();
268
		int colStart = range.getFirstColumn();
269
		Sheet sheet = cell.getSheet();
270
		CellValue[][] answer = ArrayFormulaEvaluatorHelper.transformToRange(cvs, range);
271
		for (int i = rowStart; i <= range.getLastRow(); i++) {
272
			for (int j = colStart; j <= range.getLastColumn(); j++) {
273
				Row row = sheet.getRow(i);
274
				if (row == null) {
275
					row = sheet.createRow(i);
276
				}
277
				Cell c = row.getCell(j);
278
				if (c == null) {
279
					c = row.createCell(j);
280
				}
281
				CellValue cellValue = answer[i - rowStart][j - colStart];
282
				setCellValue(c, cellValue);
283
			}
284
		}
285
		return answer;
286
	}
287
220
	/**
288
	/**
221
	 * Loops over all cells in all sheets of the supplied
289
	 * Loops over all cells in all sheets of the supplied
222
	 *  workbook.
290
	 *  workbook.
Lines 238-245 public class XSSFFormulaEvaluator implements FormulaEvaluator { Link Here
238
306
239
				for (Iterator cit = r.cellIterator(); cit.hasNext();) {
307
				for (Iterator cit = r.cellIterator(); cit.hasNext();) {
240
					XSSFCell c = (XSSFCell) cit.next();
308
					XSSFCell c = (XSSFCell) cit.next();
241
					if (c.getCellType() == XSSFCell.CELL_TYPE_FORMULA)
309
					if (c.getCellType() == XSSFCell.CELL_TYPE_FORMULA) {
242
						evaluator.evaluateFormulaCell(c);
310
						evaluator.evaluateFormulaCell(c);
311
					}
243
				}
312
				}
244
			}
313
			}
245
		}
314
		}
Lines 250-255 public class XSSFFormulaEvaluator implements FormulaEvaluator { Link Here
250
	 */
319
	 */
251
	private CellValue evaluateFormulaCellValue(Cell cell) {
320
	private CellValue evaluateFormulaCellValue(Cell cell) {
252
		ValueEval eval = _bookEvaluator.evaluate(new XSSFEvaluationCell((XSSFCell) cell));
321
		ValueEval eval = _bookEvaluator.evaluate(new XSSFEvaluationCell((XSSFCell) cell));
322
		if (eval instanceof ArrayEval) {// support of arrays
323
			if (cell.isPartOfArrayFormulaGroup()) {
324
				eval = ArrayFormulaEvaluatorHelper.dereferenceValue((ArrayEval) eval, cell);
325
			} else {
326
				eval = ((ArrayEval) eval).getValue(0, 0);
327
			}
328
		}
253
		if (eval instanceof NumberEval) {
329
		if (eval instanceof NumberEval) {
254
			NumberEval ne = (NumberEval) eval;
330
			NumberEval ne = (NumberEval) eval;
255
			return new CellValue(ne.getNumberValue());
331
			return new CellValue(ne.getNumberValue());
Lines 267-270 public class XSSFFormulaEvaluator implements FormulaEvaluator { Link Here
267
		}
343
		}
268
		throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
344
		throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
269
	}
345
	}
346
347
	/**
348
	 * Returns a Array CellValue wrapper around the supplied ArrayEval instance.
349
	 */
350
	private CellValue[][] evaluateFormulaCellArrayValues(XSSFCell cell) {
351
		ValueEval eval = _bookEvaluator.evaluate(new XSSFEvaluationCell(cell));
352
		if (eval instanceof ArrayEval) {// support of arrays
353
			ArrayEval ae = (ArrayEval) eval;
354
			int rowCount = ae.getHeight();
355
			int colCount = ae.getWidth();
356
			CellValue[][] answer = new CellValue[rowCount][colCount];
357
			for (int i = 0; i < rowCount; i++) {
358
				for (int j = 0; j < colCount; j++) {
359
					ValueEval val = ae.getValue(i, j);
360
					answer[i][j] = ArrayFormulaEvaluatorHelper.evalToCellValue(val);
361
				}
362
			}
363
			return answer;
364
		}
365
		// non-array (usually from aggregate function)
366
		CellValue[][] answer = new CellValue[1][1];
367
		answer[0][0] = ArrayFormulaEvaluatorHelper.evalToCellValue(eval);
368
		return answer;
369
	}
270
}
370
}
(-)a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java (-1 / +56 lines)
Lines 79-84 public class XSSFSheet extends POIXMLDocumentPart implements Sheet { Link Here
79
    private ColumnHelper columnHelper;
79
    private ColumnHelper columnHelper;
80
    private CommentsTable sheetComments;
80
    private CommentsTable sheetComments;
81
    private Map<Integer, XSSFCell> sharedFormulas;
81
    private Map<Integer, XSSFCell> sharedFormulas;
82
    private List<CellRangeAddress> arrayFormulas;
82
83
83
    /**
84
    /**
84
     * Creates new XSSFSheet   - called by XSSFWorkbook to create a sheet from scratch.
85
     * Creates new XSSFSheet   - called by XSSFWorkbook to create a sheet from scratch.
Lines 153-158 public class XSSFSheet extends POIXMLDocumentPart implements Sheet { Link Here
153
    private void initRows(CTWorksheet worksheet) {
154
    private void initRows(CTWorksheet worksheet) {
154
        rows = new TreeMap<Integer, XSSFRow>();
155
        rows = new TreeMap<Integer, XSSFRow>();
155
        sharedFormulas = new HashMap<Integer, XSSFCell>();
156
        sharedFormulas = new HashMap<Integer, XSSFCell>();
157
        arrayFormulas = new ArrayList<CellRangeAddress>();
156
        for (CTRow row : worksheet.getSheetData().getRowArray()) {
158
        for (CTRow row : worksheet.getSheetData().getRowArray()) {
157
            XSSFRow r = new XSSFRow(row, this);
159
            XSSFRow r = new XSSFRow(row, this);
158
            rows.put(r.getRowNum(), r);
160
            rows.put(r.getRowNum(), r);
Lines 2316-2324 public class XSSFSheet extends POIXMLDocumentPart implements Sheet { Link Here
2316
        //collect cells holding shared formulas
2318
        //collect cells holding shared formulas
2317
        CTCell ct = cell.getCTCell();
2319
        CTCell ct = cell.getCTCell();
2318
        CTCellFormula f = ct.getF();
2320
        CTCellFormula f = ct.getF();
2319
        if(f != null && f.getT() == STCellFormulaType.SHARED && f.isSetRef() && f.getStringValue() != null){
2321
        if (f != null && f.getT() == STCellFormulaType.SHARED && f.isSetRef() && f.getStringValue() != null) {
2320
            sharedFormulas.put((int)f.getSi(), cell);
2322
            sharedFormulas.put((int)f.getSi(), cell);
2321
        }
2323
        }
2324
        if (f != null && f.getT() == STCellFormulaType.ARRAY && f.getRef() != null) {
2325
            arrayFormulas.add(CellRangeAddress.valueOf(f.getRef()));
2326
        }
2322
    }
2327
    }
2323
2328
2324
    @Override
2329
    @Override
Lines 2676-2679 public class XSSFSheet extends POIXMLDocumentPart implements Sheet { Link Here
2676
    private boolean sheetProtectionEnabled() {
2681
    private boolean sheetProtectionEnabled() {
2677
        return worksheet.getSheetProtection().getSheet();
2682
        return worksheet.getSheetProtection().getSheet();
2678
    }
2683
    }
2684
2685
    /* package */ boolean isCellInArrayFormulaContext(XSSFCell cell) {
2686
        for (CellRangeAddress range : arrayFormulas) {
2687
            if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) {
2688
                return true;
2689
            }
2690
        }
2691
        return false;
2692
    }
2693
2694
    /* package */ XSSFCell getFirstCellInArrayFormula(XSSFCell cell) {
2695
        for (CellRangeAddress range : arrayFormulas) {
2696
            if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) {
2697
                return getRow(range.getFirstRow()).getCell(range.getFirstColumn());
2698
            }
2699
        }
2700
        return null;
2701
    }
2702
2703
    public void setArrayFormula(String formula, CellRangeAddress range) {
2704
        XSSFRow row = getRow(range.getFirstRow());
2705
        if (row == null) {
2706
            row = createRow(range.getFirstRow());
2707
        }
2708
        XSSFCell mainArrayFormulaCell = row.getCell(range.getFirstColumn());
2709
        if (mainArrayFormulaCell == null) {
2710
            mainArrayFormulaCell = row.createCell(range.getFirstColumn());
2711
        }
2712
        mainArrayFormulaCell.setCellArrayFormula(formula, range);
2713
        arrayFormulas.add(range);
2714
    }
2715
2716
    public void removeArrayFormula(Cell cell) {
2717
        for (CellRangeAddress range : arrayFormulas) {
2718
            if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) {
2719
                arrayFormulas.remove(range);
2720
                for (int rowIndex = range.getFirstRow(); rowIndex <= range.getLastRow(); rowIndex++) {
2721
                    XSSFRow row = getRow(rowIndex);
2722
                    for (int columnIndex = range.getFirstColumn(); columnIndex <= range.getLastColumn(); columnIndex++) {
2723
                        XSSFCell arrayFormulaCell = row.getCell(columnIndex);
2724
                        if (arrayFormulaCell != null) {
2725
                            arrayFormulaCell.setCellType(Cell.CELL_TYPE_BLANK);
2726
                        }
2727
                    }
2728
                }
2729
                return;
2730
            }
2731
        }
2732
        throw new RuntimeException("Cell does not belong to Array Formula");
2733
    }
2679
}
2734
}
(-)a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/AllXSSFUsermodelTests.java (-2 / +6 lines)
Lines 28-34 import org.apache.poi.xssf.usermodel.helpers.TestHeaderFooterHelper; Link Here
28
28
29
/**
29
/**
30
 * Collects all tests for <tt>org.apache.poi.xssf.usermodel</tt> and sub-packages.
30
 * Collects all tests for <tt>org.apache.poi.xssf.usermodel</tt> and sub-packages.
31
 * 
31
 *
32
 * @author Josh Micich
32
 * @author Josh Micich
33
 */
33
 */
34
public final class AllXSSFUsermodelTests {
34
public final class AllXSSFUsermodelTests {
Lines 37-42 public final class AllXSSFUsermodelTests { Link Here
37
		TestSuite result = new TestSuite(AllXSSFUsermodelTests.class.getName());
37
		TestSuite result = new TestSuite(AllXSSFUsermodelTests.class.getName());
38
		result.addTestSuite(TestFormulaEvaluatorOnXSSF.class);
38
		result.addTestSuite(TestFormulaEvaluatorOnXSSF.class);
39
		result.addTestSuite(TestSheetHiding.class);
39
		result.addTestSuite(TestSheetHiding.class);
40
		result.addTestSuite(TestXSSFAddRemoveArrayFormula.class);
41
		result.addTestSuite(TestXSSFArrayEvaluation.class);
42
		result.addTestSuite(TestXSSFArrayFormulaEvaluation.class);
43
		result.addTestSuite(TestXSSFArrayFormulaFunction.class);
40
		result.addTestSuite(TestXSSFBugs.class);
44
		result.addTestSuite(TestXSSFBugs.class);
41
		result.addTestSuite(TestXSSFDataFormat.class);
45
		result.addTestSuite(TestXSSFDataFormat.class);
42
		result.addTestSuite(TestXSSFCellStyle.class);
46
		result.addTestSuite(TestXSSFCellStyle.class);
Lines 59-65 public final class AllXSSFUsermodelTests { Link Here
59
		result.addTestSuite(TestXSSFBorder.class);
63
		result.addTestSuite(TestXSSFBorder.class);
60
		result.addTestSuite(TestXSSFCellFill.class);
64
		result.addTestSuite(TestXSSFCellFill.class);
61
		result.addTestSuite(TestXSSFSheetComments.class);
65
		result.addTestSuite(TestXSSFSheetComments.class);
62
		
66
63
		result.addTestSuite(TestColumnHelper.class);
67
		result.addTestSuite(TestColumnHelper.class);
64
		result.addTestSuite(TestHeaderFooterHelper.class);
68
		result.addTestSuite(TestHeaderFooterHelper.class);
65
69
(-)a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFAddRemoveArrayFormula.java (+29 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.xssf.usermodel;
19
20
import org.apache.poi.ss.usermodel.BaseTestAddRemoveArrayFormula;
21
import org.apache.poi.xssf.XSSFITestDataProvider;
22
23
24
public final class TestXSSFAddRemoveArrayFormula extends BaseTestAddRemoveArrayFormula {
25
26
	public TestXSSFAddRemoveArrayFormula() {
27
		super(XSSFITestDataProvider.getInstance(), "ArrayFormula.xlsx");
28
	}
29
}
(-)a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFArrayEvaluation.java (+33 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.xssf.usermodel;
19
20
import org.apache.poi.ss.formula.eval.BaseTestArrayEvaluation;
21
import org.apache.poi.ss.usermodel.ArrayFormulaTestHelper;
22
23
public final class TestXSSFArrayEvaluation extends BaseTestArrayEvaluation {
24
25
	private static ArrayFormulaTestHelper th;
26
27
	protected ArrayFormulaTestHelper th() {
28
		if (th == null) {
29
			th = new ArrayFormulaTestHelper("ConstantArray.xlsx");
30
		}
31
		return th;
32
	}
33
}
(-)a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFArrayFormulaEvaluation.java (+33 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.xssf.usermodel;
19
20
import org.apache.poi.ss.formula.eval.BaseTestArrayFormulaEvaluation;
21
import org.apache.poi.ss.usermodel.ArrayFormulaTestHelper;
22
23
public final class TestXSSFArrayFormulaEvaluation extends BaseTestArrayFormulaEvaluation {
24
25
	private static ArrayFormulaTestHelper th;
26
27
	protected ArrayFormulaTestHelper th() {
28
		if (th == null) {
29
			th = new ArrayFormulaTestHelper("ArrayFormula.xlsx");
30
		}
31
		return th;
32
	}
33
}
(-)a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFArrayFormulaFunction.java (+33 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.xssf.usermodel;
19
20
import org.apache.poi.ss.formula.eval.BaseTestArrayFormulaFunction;
21
import org.apache.poi.ss.usermodel.ArrayFormulaTestHelper;
22
23
public final class TestXSSFArrayFormulaFunction extends BaseTestArrayFormulaFunction {
24
25
	private static ArrayFormulaTestHelper th;
26
27
	protected ArrayFormulaTestHelper th() {
28
		if (th == null) {
29
			th = new ArrayFormulaTestHelper("ArrayFormulaFunctions.xlsx");
30
		}
31
		return th;
32
	}
33
}
(-)a/src/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java (+4 lines)
Lines 30-41 public class AllFormulaEvalTests { Link Here
30
	public static Test suite() {
30
	public static Test suite() {
31
		TestSuite result = new TestSuite(AllFormulaEvalTests.class.getName());
31
		TestSuite result = new TestSuite(AllFormulaEvalTests.class.getName());
32
		result.addTestSuite(TestAreaEval.class);
32
		result.addTestSuite(TestAreaEval.class);
33
		result.addTestSuite(TestArrayEval.class);
33
		result.addTestSuite(TestCircularReferences.class);
34
		result.addTestSuite(TestCircularReferences.class);
34
		result.addTestSuite(TestDivideEval.class);
35
		result.addTestSuite(TestDivideEval.class);
35
		result.addTestSuite(TestEqualEval.class);
36
		result.addTestSuite(TestEqualEval.class);
36
		result.addTestSuite(TestExternalFunction.class);
37
		result.addTestSuite(TestExternalFunction.class);
37
		result.addTestSuite(TestFormulaBugs.class);
38
		result.addTestSuite(TestFormulaBugs.class);
38
		result.addTestSuite(TestFormulasFromSpreadsheet.class);
39
		result.addTestSuite(TestFormulasFromSpreadsheet.class);
40
		result.addTestSuite(TestHSSFArrayEvaluation.class);
41
		result.addTestSuite(TestHSSFArrayFormulaEvaluation.class);
42
		result.addTestSuite(TestHSSFArrayFormulaFunction.class);
39
		result.addTestSuite(TestMinusZeroResult.class);
43
		result.addTestSuite(TestMinusZeroResult.class);
40
		result.addTestSuite(TestMissingArgEval.class);
44
		result.addTestSuite(TestMissingArgEval.class);
41
		result.addTestSuite(TestPercentEval.class);
45
		result.addTestSuite(TestPercentEval.class);
(-)a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestArrayEval.java (+47 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.hssf.record.formula.eval;
19
20
import junit.framework.AssertionFailedError;
21
import junit.framework.TestCase;
22
23
import org.apache.poi.ss.formula.ArrayEval;
24
25
/**
26
 * Tests for {@link ArrayEval}
27
 *
28
 * @author Josh Micich
29
 */
30
public final class TestArrayEval extends TestCase {
31
32
	public void testToString() {
33
34
		ValueEval x = NumberEval.ZERO;
35
36
		ValueEval[][] values = {
37
				{ x, x }, { x, x },
38
		};
39
		ArrayEval ae = new ArrayEval(values);
40
41
		try {
42
			ae.toString();
43
		} catch (ArrayIndexOutOfBoundsException e) {
44
			throw new AssertionFailedError("Identified bug in toString");
45
		}
46
	}
47
}
(-)a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestArrayEvaluationExtra.java (+149 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.hssf.record.formula.eval;
19
20
import junit.framework.TestCase;
21
22
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
23
import org.apache.poi.ss.usermodel.Cell;
24
import org.apache.poi.ss.usermodel.CellValue;
25
import org.apache.poi.ss.usermodel.ErrorConstants;
26
import org.apache.poi.ss.usermodel.FormulaEvaluator;
27
import org.apache.poi.ss.usermodel.Row;
28
import org.apache.poi.ss.usermodel.Sheet;
29
import org.apache.poi.ss.usermodel.Workbook;
30
import org.apache.poi.ss.util.CellRangeAddress;
31
32
/**
33
 * @author Josh Micich
34
 */
35
public class TestArrayEvaluationExtra extends TestCase {
36
	private static boolean FIXED = false;
37
38
	// simple case already works
39
	public void testArraysInSimpleNonArrayFormula() {
40
		Workbook wb = new HSSFWorkbook();
41
		Sheet sheet = wb.createSheet("Sheet1");
42
43
		Row row1 = sheet.createRow(0);
44
		Cell cellA1 = row1.createCell(0);
45
		Cell cellB1 = row1.createCell(1);
46
		Cell cellC1 = row1.createCell(2);
47
48
		FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
49
50
		String formula = "{3,4}+{0.2,0.1,0.4}";
51
52
		cellA1.setCellFormula(formula);
53
		assertEquals(formula, cellA1.getCellFormula());
54
		confirmEvaluate(cellA1, fe, 3.2);
55
56
		sheet.setArrayFormula(formula, CellRangeAddress.valueOf("A1:C1"));
57
		confirmEvaluate(cellA1, fe, 3.2);
58
		confirmEvaluate(cellB1, fe, 4.1);
59
60
		CellValue cv;
61
		fe.clearAllCachedResultValues();
62
		cv = fe.evaluate(cellC1);
63
		assertEquals(Cell.CELL_TYPE_ERROR, cv.getCellType());
64
		assertEquals(cv.getErrorValue(), ErrorConstants.ERROR_NA);
65
	}
66
67
68
	public void testArraysInNonArrayFormula() {
69
		Workbook wb = new HSSFWorkbook();
70
		Sheet sheet = wb.createSheet("Sheet1");
71
		Row row149 = sheet.createRow(148);
72
		Row row150 = sheet.createRow(149);
73
		row149.createCell(0).setCellValue(1.0);
74
		row149.createCell(1).setCellValue(2.0);
75
		row150.createCell(0).setCellValue(3.0);
76
		row150.createCell(1).setCellValue(4.0);
77
78
		Row row1 = sheet.createRow(0);
79
		Cell cellD1 = row1.createCell(3);
80
		Cell cellE1 = row1.createCell(4);
81
		Cell cellD2 = sheet.createRow(1).createCell(3);
82
		CellRangeAddress colRange = CellRangeAddress.valueOf("D1:D2");
83
		CellRangeAddress rowRange = CellRangeAddress.valueOf("D1:E2");
84
85
		FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
86
87
		// Note: literal arrays are firstly row arrays, then column arrays
88
		String rowFormula = "SUM(OFFSET(A149:B150,{1,0},{1,0},{1,1},{1,1}))";
89
		String colFormula = "SUM(OFFSET(A149:B150,{1;0},{1;0},{1;1},{1;1}))";
90
91
		cellD1.setCellFormula(rowFormula);
92
		assertEquals(rowFormula, cellD1.getCellFormula());
93
		if (FIXED) {
94
			confirmEvaluate(cellD1, fe, 4.0);
95
		}
96
97
		sheet.setArrayFormula(rowFormula, colRange);
98
		if (FIXED) {
99
			confirmEvaluate(cellD1, fe, 4.0);
100
		}
101
		if (FIXED) {
102
			confirmEvaluate(cellD2, fe, 4.0);
103
		}
104
		sheet.removeArrayFormula(cellD1);
105
106
		sheet.setArrayFormula(rowFormula, rowRange);
107
		if (FIXED) {
108
			confirmEvaluate(cellD1, fe, 4.0);
109
		}
110
		if (FIXED) {
111
			confirmEvaluate(cellE1, fe, 1.0);
112
		}
113
		sheet.removeArrayFormula(cellD1);
114
115
116
		sheet.setArrayFormula(colFormula, colRange);
117
		if (FIXED) {
118
			confirmEvaluate(cellD1, fe, 4.0);
119
		}
120
		if (FIXED) {
121
			confirmEvaluate(cellD2, fe, 1.0);
122
		}
123
		sheet.removeArrayFormula(cellD1);
124
	}
125
126
	public void testSumStringElement() {
127
		Workbook wb = new HSSFWorkbook();
128
		Sheet sheet = wb.createSheet("Sheet1");
129
130
		Row row1 = sheet.createRow(0);
131
		Cell cellA1 = row1.createCell(0);
132
133
		FormulaEvaluator fe = wb.getCreationHelper().createFormulaEvaluator();
134
135
		String formula = "SUM({3,\"4\"})";
136
137
		cellA1.setCellFormula(formula);
138
		assertEquals(formula, cellA1.getCellFormula());
139
		confirmEvaluate(cellA1, fe, 3.0);
140
	}
141
142
	private void confirmEvaluate(Cell cell, FormulaEvaluator fe, double expectedResult) {
143
		CellValue cv;
144
		fe.clearAllCachedResultValues();
145
		cv = fe.evaluate(cell);
146
		assertEquals(Cell.CELL_TYPE_NUMERIC, cv.getCellType());
147
		assertEquals(expectedResult, cv.getNumberValue(), 0.0);
148
	}
149
}
(-)a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestHSSFArrayEvaluation.java (+33 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.hssf.record.formula.eval;
19
20
import org.apache.poi.ss.formula.eval.BaseTestArrayEvaluation;
21
import org.apache.poi.ss.usermodel.ArrayFormulaTestHelper;
22
23
public final class TestHSSFArrayEvaluation extends BaseTestArrayEvaluation{
24
25
	private static ArrayFormulaTestHelper th;
26
27
	protected ArrayFormulaTestHelper th() {
28
		if (th == null) {
29
			th = new ArrayFormulaTestHelper("ConstantArray.xls");
30
		}
31
		return th;
32
	}
33
}
(-)a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestHSSFArrayFormulaEvaluation.java (+33 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.hssf.record.formula.eval;
19
20
import org.apache.poi.ss.formula.eval.BaseTestArrayFormulaEvaluation;
21
import org.apache.poi.ss.usermodel.ArrayFormulaTestHelper;
22
23
public final class TestHSSFArrayFormulaEvaluation extends BaseTestArrayFormulaEvaluation{
24
25
	private static ArrayFormulaTestHelper th;
26
27
	protected ArrayFormulaTestHelper th() {
28
		if (th == null) {
29
			th = new ArrayFormulaTestHelper("ArrayFormula.xls");
30
		}
31
		return th;
32
	}
33
}
(-)a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestHSSFArrayFormulaFunction.java (+33 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.hssf.record.formula.eval;
19
20
import org.apache.poi.ss.formula.eval.BaseTestArrayFormulaFunction;
21
import org.apache.poi.ss.usermodel.ArrayFormulaTestHelper;
22
23
public final class TestHSSFArrayFormulaFunction extends BaseTestArrayFormulaFunction {
24
25
	private static ArrayFormulaTestHelper th;
26
27
	public ArrayFormulaTestHelper th() {
28
		if (th == null) {
29
			th = new ArrayFormulaTestHelper("ArrayFormulaFunctions.xls");
30
		}
31
		return th;
32
	}
33
}
(-)a/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java (-5 / +6 lines)
Lines 22-35 import junit.framework.TestSuite; Link Here
22
22
23
/**
23
/**
24
 * Collects all tests for the <tt>org.apache.poi.hssf.usermodel</tt> package.
24
 * Collects all tests for the <tt>org.apache.poi.hssf.usermodel</tt> package.
25
 * 
25
 *
26
 * @author Josh Micich
26
 * @author Josh Micich
27
 */
27
 */
28
public class AllUserModelTests {
28
public class AllUserModelTests {
29
	
29
30
	public static Test suite() {
30
	public static Test suite() {
31
		TestSuite result = new TestSuite(AllUserModelTests.class.getName());
31
		TestSuite result = new TestSuite(AllUserModelTests.class.getName());
32
		
32
33
		result.addTestSuite(TestBugs.class);
33
		result.addTestSuite(TestBugs.class);
34
		result.addTestSuite(TestCellStyle.class);
34
		result.addTestSuite(TestCellStyle.class);
35
		result.addTestSuite(TestCloneSheet.class);
35
		result.addTestSuite(TestCloneSheet.class);
Lines 40-45 public class AllUserModelTests { Link Here
40
		result.addTestSuite(TestFormulas.class);
40
		result.addTestSuite(TestFormulas.class);
41
		result.addTestSuite(TestFormulaEvaluatorBugs.class);
41
		result.addTestSuite(TestFormulaEvaluatorBugs.class);
42
		result.addTestSuite(TestFormulaEvaluatorDocs.class);
42
		result.addTestSuite(TestFormulaEvaluatorDocs.class);
43
		result.addTestSuite(TestHSSFAddRemoveArrayFormula.class);
43
		result.addTestSuite(TestHSSFCell.class);
44
		result.addTestSuite(TestHSSFCell.class);
44
		result.addTestSuite(TestHSSFClientAnchor.class);
45
		result.addTestSuite(TestHSSFClientAnchor.class);
45
		result.addTestSuite(TestHSSFComment.class);
46
		result.addTestSuite(TestHSSFComment.class);
Lines 71-78 public class AllUserModelTests { Link Here
71
		}
72
		}
72
		result.addTestSuite(TestUnicodeWorkbook.class);
73
		result.addTestSuite(TestUnicodeWorkbook.class);
73
		result.addTestSuite(TestUppercaseWorkbook.class);
74
		result.addTestSuite(TestUppercaseWorkbook.class);
74
		result.addTestSuite(TestWorkbook.class);		
75
		result.addTestSuite(TestWorkbook.class);
75
		
76
76
		return result;
77
		return result;
77
	}
78
	}
78
}
79
}
(-)a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFAddRemoveArrayFormula.java (+28 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.hssf.usermodel;
19
20
import org.apache.poi.hssf.HSSFITestDataProvider;
21
import org.apache.poi.ss.usermodel.BaseTestAddRemoveArrayFormula;
22
23
public final class TestHSSFAddRemoveArrayFormula extends BaseTestAddRemoveArrayFormula {
24
25
	public TestHSSFAddRemoveArrayFormula() {
26
		super(HSSFITestDataProvider.getInstance(), "ArrayFormula.xls");
27
	}
28
}
(-)a/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java (-2 / +2 lines)
Lines 48-54 import org.apache.poi.ss.usermodel.CellValue; Link Here
48
public class TestWorkbookEvaluator extends TestCase {
48
public class TestWorkbookEvaluator extends TestCase {
49
49
50
	private static ValueEval evaluateFormula(Ptg[] ptgs) {
50
	private static ValueEval evaluateFormula(Ptg[] ptgs) {
51
		OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null);
51
		OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null, false);
52
		return new WorkbookEvaluator(null, null, null).evaluateFormula(ec, ptgs);
52
		return new WorkbookEvaluator(null, null, null).evaluateFormula(ec, ptgs);
53
	}
53
	}
54
54
Lines 193-199 public class TestWorkbookEvaluator extends TestCase { Link Here
193
		assertEquals(HSSFCell.CELL_TYPE_STRING, cv.getCellType());
193
		assertEquals(HSSFCell.CELL_TYPE_STRING, cv.getCellType());
194
		// adding blank to "abc" gives "abc"
194
		// adding blank to "abc" gives "abc"
195
		assertEquals("abc", cv.getStringValue());
195
		assertEquals("abc", cv.getStringValue());
196
		
196
197
		// check CHOOSE()
197
		// check CHOOSE()
198
		cell.setCellFormula("\"abc\"&CHOOSE(2,5,,9)");
198
		cell.setCellFormula("\"abc\"&CHOOSE(2,5,,9)");
199
		fe.notifySetFormula(cell);
199
		fe.notifySetFormula(cell);
(-)a/src/testcases/org/apache/poi/ss/formula/eval/BaseTestArrayEvaluation.java (+311 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.ss.formula.eval;
19
20
import junit.framework.TestCase;
21
22
import org.apache.poi.ss.usermodel.ArrayFormulaTestHelper;
23
import org.apache.poi.ss.usermodel.FormulaError;
24
25
public abstract class BaseTestArrayEvaluation extends TestCase {
26
27
	protected abstract ArrayFormulaTestHelper th();
28
29
	public final void testSingleArg() {
30
		confirmNumeric(5);
31
	}
32
33
	public final void testSingleArgOnArray() {
34
		confirmNumeric(6);
35
	}
36
37
	public final void testTwoArgFirstArray() {
38
		confirmNumeric(9);
39
	}
40
41
	public final void testTwoArgTwoArrays() {
42
		confirmNumeric(10);
43
	}
44
45
	public final void testTwoArgFirstArrayResult() {
46
		confirmNumeric(11);
47
	}
48
49
	public final void testAggregateOnArray() {
50
		confirmNumeric(13);
51
	}
52
53
	public final void testAggregateOnTwoArrays() {
54
		confirmNumeric(14);
55
	}
56
57
	public final void testAggregateWithSpecialLast() {
58
		confirmNumeric(15);
59
	}
60
61
	public final void testAggregateWithSpecialLastAsArray() {
62
		confirmNumeric(16);
63
	}
64
65
	public final void testAggregateOnArrayFromSingle() {
66
		confirmNumeric(19);
67
	}
68
69
	public final void testAggregateOnTwoDimArrayFromSingle() {
70
		confirmNumeric(20);
71
	}
72
73
	public final void testAggregateOnArrayFromTwoArg() {
74
		confirmNumeric(21);
75
	}
76
77
	public final void testAggregateOnNonProperArray() {
78
		confirmError(22, FormulaError.NA);
79
	}
80
81
	public final void testAggregateOnTwoArg() {
82
		confirmNumeric(23);
83
	}
84
	public final void testAggregateOnTwoArgWithLastSpecial() {
85
		confirmNumeric(24);
86
	}
87
88
	public final void testAggregateOnNonProperArrayViaSingle() {
89
		confirmError(25,  FormulaError.NA);
90
	}
91
92
	public final void testSimpleNumericOneArg() {
93
		confirmNumeric(27);
94
	}
95
96
	public final void testSimpleNumericTwoArg() {
97
		confirmNumeric(28);
98
	}
99
100
	public final void testSimpleAgregate() {
101
102
		confirmNumeric(29);
103
	}
104
105
	public final void testSimpleAgregateWithLast() {
106
		confirmNumeric(30);
107
	}
108
109
	public final void testSimpleTextOnNumeric() {
110
		confirmNumeric(43);
111
	}
112
113
	public final void testSingleArgTextReturnNum() {
114
		confirmNumeric(44);
115
	}
116
117
	public final void testSingleArgOnArrayFromNum() {
118
		confirmNumeric(45);
119
	}
120
		public void NumericAggregateOnArrayFromTextSingleArg() {
121
122
		confirmNumeric(46);
123
	}
124
	public final void testNumericAggregateSigleArgtTextSingleArgTextArray() {
125
126
		confirmNumeric(47);
127
	}
128
129
	public final void testSimpleMultiArgText() {
130
		confirmString(48);
131
	}
132
133
	public final void testSimpleMultiArgTextOneArray() {
134
		confirmString(49);
135
	}
136
137
	public final void testSimpleMultiArgTextTwoArraysSizeMatch() {
138
		confirmString(50);
139
	}
140
141
	public final void testSimpleMultiArgTextTwoArraysSizeNotMatch() {
142
		confirmString(51);
143
	}
144
145
	public final void testNumericAggregateOnTextArraySizeMatch() {
146
		confirmNumeric(52);
147
	}
148
149
	public final void testAggregateOnTextArraySizenotMatch() {
150
		confirmError(53,  FormulaError.NA);
151
	}
152
153
	public final void testSimpleXY() {
154
		confirmNumeric(60);
155
	}
156
157
	public final void testXYonArray() {
158
		confirmNumeric(61);
159
	}
160
161
	public final void testXYonArrayFromFunction() {
162
		confirmNumeric(62);
163
	}
164
165
	public final void testXYonWrongArray() {
166
		confirmError(63, FormulaError.NA);
167
	}
168
169
	public final void testAndTrue() {
170
		confirmBoolean(73);
171
	}
172
173
	public final void testAndFalse() {
174
		confirmBoolean(74);
175
	}
176
177
	public final void testOrTrue() {
178
		confirmBoolean(75);
179
	}
180
181
	public final void testOrFalse() {
182
		confirmBoolean(76);
183
	}
184
185
	public final void testChoose() {
186
		confirmNumeric(82);
187
	}
188
189
	public final void testChooseAggregate() {
190
		confirmNumeric(83);
191
	}
192
193
	public final void testVlookupArrayAsTable() {
194
		confirmNumeric(88);
195
	}
196
197
	public final void testVlookupAllArrays() {
198
		confirmNumeric(89);
199
	}
200
201
	public final void testHlookupArrayAsTable() {
202
		confirmNumeric(95);
203
	}
204
205
	public final void testHlookupAllArrays() {
206
		confirmNumeric(96);
207
	}
208
209
	public final void testLookupArray() {
210
		confirmNumeric(101);
211
	}
212
213
	public final void testLookupAllArrays() {
214
		confirmNumeric(102);
215
	}
216
217
	public final void testLookupAggregateArrays() {
218
		confirmNumeric(103);
219
	}
220
221
	public final void testCountArray() {
222
		confirmNumeric(108);
223
	}
224
225
	public final void testCountaArray() {
226
		confirmNumeric(112);
227
	}
228
229
	public final void testCountifArray() {
230
		confirmNumeric(117);
231
	}
232
233
	public final void testCountifArrayAggregate() {
234
		confirmNumeric(118);
235
	}
236
237
	public final void testSumifArray() {
238
		confirmNumeric(124);
239
	}
240
241
	public final void testSumifArrayAggregate() {
242
		confirmNumeric(125);
243
	}
244
245
	public final void testSumproductArray() {
246
		confirmNumeric(130);
247
	}
248
249
	public final void testColumnsArray() {
250
		confirmNumeric(136);
251
	}
252
253
	public final void testMatchArray() {
254
		confirmNumeric(140);
255
	}
256
257
	public final void testMatchArrayAggregate() {
258
		confirmNumeric(141);
259
	}
260
261
	public final void testModeArray() {
262
		confirmNumeric(145);
263
	}
264
265
	public final void testOffsetArray() {
266
		confirmNumeric(149);
267
	}
268
269
	public final void testRowsArray() {
270
		confirmNumeric(154);
271
	}
272
273
	public final void testIndexArray() {
274
		confirmNumeric(160);
275
	}
276
277
	public final void testIndexArrayEntireColumn() {
278
		confirmNumeric(161);
279
	}
280
281
	public final void testIndexArrayEntireColumnAggregate() {
282
		confirmNumeric(162);
283
	}
284
	private void confirmError(int rowNum, FormulaError err) {
285
		String formulaCell = "C" + rowNum;
286
		ArrayFormulaTestHelper th = th();
287
		FormulaError fe = th.calculateNumericFormulaWithError(formulaCell);
288
		assertEquals(formulaCell, fe.getCode(), err.getCode());
289
	}
290
	private void confirmNumeric(int rowNum) {
291
		String formulaCell = "C" + rowNum;
292
		String expResCell = "D" + rowNum;
293
		ArrayFormulaTestHelper th = th();
294
		double actResult = th.calculateNumericFormula(formulaCell);
295
		assertEquals(expResCell + "-" + formulaCell, th.getNumericValue(expResCell), actResult, 0.0);
296
	}
297
	private void confirmString(int rowNum) {
298
		String formulaCell = "C" + rowNum;
299
		String expResCell = "D" + rowNum;
300
		ArrayFormulaTestHelper th = th();
301
		String actResult = th.calculateStringFormula(formulaCell);
302
		assertEquals(expResCell + "-" + formulaCell, th.getStringValue(expResCell), actResult);
303
	}
304
	private void confirmBoolean(int rowNum) {
305
		String formulaCell = "C" + rowNum;
306
		String expResCell = "D" + rowNum;
307
		ArrayFormulaTestHelper th = th();
308
		boolean actResult = th.calculateBooleanFormula(formulaCell);
309
		assertEquals(expResCell + "-" + formulaCell, th.getBooleanValue(expResCell), actResult);
310
	}
311
}
(-)a/src/testcases/org/apache/poi/ss/formula/eval/BaseTestArrayFormulaEvaluation.java (+277 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.ss.formula.eval;
19
20
import org.apache.poi.ss.usermodel.ArrayFormulaTestHelper;
21
22
import junit.framework.TestCase;
23
24
public abstract class BaseTestArrayFormulaEvaluation extends TestCase {
25
26
	protected abstract ArrayFormulaTestHelper th();
27
	private ArrayFormulaTestHelper _th;
28
29
	protected void setUp() {
30
		_th = th();
31
	}
32
33
	public final void testNumericSquareArrayFormula() {
34
		// Clean cell's values before calculation
35
		_th.setNumericValue("C4", 0);
36
		_th.setNumericValue("D4", 0);
37
		_th.setNumericValue("C5", 0);
38
		_th.setNumericValue("D5", 0);
39
40
		assertEquals("C4-F4",_th.getNumericValue("F4"), _th.calculateNumericFormula("C4"), 0);
41
		assertEquals("D4-G4",_th.getNumericValue("G4"), _th.calculateNumericFormula("D4"), 0);
42
		assertEquals("C5-F5",_th.getNumericValue("F5"), _th.calculateNumericFormula("C5"), 0);
43
		assertEquals("D5-G5",_th.getNumericValue("G5"), _th.calculateNumericFormula("D5"), 0);
44
	}
45
46
	public final void testNumericArrayFormulaWORange() {
47
48
		// Clean cell's values before calculation
49
		_th.setNumericValue("C7", 0);
50
51
		assertEquals("C7-F7",_th.getNumericValue("F7"), _th.calculateNumericFormula("C7"), 0);
52
	}
53
54
	public final void testNumericArrayFormulaFullRow() {
55
56
		// Clean cell's values before calculation
57
		_th.setNumericValue("A9", 0);
58
		_th.setNumericValue("B9", 0);
59
		_th.setNumericValue("C9", 0);
60
		assertEquals("A9-F9",_th.getNumericValue("F9"), _th.calculateNumericFormula("A9"), 0);
61
		assertEquals("B9-G9",_th.getNumericValue("G9"), _th.calculateNumericFormula("B9"), 0);
62
		assertEquals("C9-H9",_th.getNumericValue("H9"), _th.calculateNumericFormula("C9"), 0);
63
	}
64
65
	public final void testNumericArrayFormulaSmallRow() {
66
67
		// Clean cell's values before calculation
68
		_th.setNumericValue("A11", 0);
69
		_th.setNumericValue("B11", 0);
70
		assertEquals("A11-F11",_th.getNumericValue("F11"), _th.calculateNumericFormula("A11"), 0);
71
		assertEquals("B11-G11",_th.getNumericValue("G11"), _th.calculateNumericFormula("B11"), 0);
72
	}
73
74
	public final void testNumericArrayFormulaBigRow() {
75
76
		// Clean cell's values before calculation
77
		_th.setNumericValue("A13", 0);
78
		_th.setNumericValue("B13", 0);
79
		_th.setNumericValue("C13", 0);
80
81
		assertEquals("A13-F13",_th.getNumericValue("F13"), _th.calculateNumericFormula("A13"), 0);
82
		assertEquals("B13-G13",_th.getNumericValue("G13"), _th.calculateNumericFormula("B13"), 0);
83
		assertEquals("C13-H13",_th.getNumericValue("H13"), _th.calculateNumericFormula("C13"), 0);
84
		assertEquals("D3-I13",_th.getErrorValue("I13"), _th.calculateNumericFormulaWithError("D13").getString());
85
	}
86
87
	public final void testNumericArrayFormulaFewerRows() {
88
89
		// Clean cell's values before calculation
90
		_th.setNumericValue("A16", 0);
91
		_th.setNumericValue("B16", 0);
92
		_th.setNumericValue("A17", 0);
93
		_th.setNumericValue("B17", 0);
94
95
		assertEquals("A16-F16",_th.getNumericValue("F16"), _th.calculateNumericFormula("A16"), 0);
96
		assertEquals("B16-G16",_th.getNumericValue("G16"), _th.calculateNumericFormula("B16"), 0);
97
		assertEquals("A17-F17",_th.getNumericValue("F17"), _th.calculateNumericFormula("A17"), 0);
98
		assertEquals("B17-G17",_th.getNumericValue("G17"), _th.calculateNumericFormula("B17"), 0);
99
	}
100
101
	public final void testNumericArrayFormulaDataExceed() {
102
		// Clean cell's values before calculation
103
		_th.setNumericValue("A19", 0);
104
		_th.setNumericValue("B19", 0);
105
		_th.setNumericValue("A20", 0);
106
		_th.setNumericValue("B20", 0);
107
108
		assertEquals("A19-F19",_th.getNumericValue("F19"), _th.calculateNumericFormula("A19"), 0);
109
		assertEquals("B19-G19",_th.getNumericValue("G19"), _th.calculateNumericFormula("B19"), 0);
110
		assertEquals("A20-F20",_th.getNumericValue("F20"), _th.calculateNumericFormula("A20"), 0);
111
		assertEquals("B20-G20",_th.getNumericValue("G20"), _th.calculateNumericFormula("B20"), 0);
112
	}
113
114
	public final void testNumericArrayFormulaCol4Row() {
115
116
		// Clean cell's values before calculation
117
		_th.setNumericValue("A22", 0);
118
		_th.setNumericValue("A23", 0);
119
		_th.setNumericValue("A24", 0);
120
		_th.setNumericValue("A25", 0);
121
122
		assertEquals("A22-F22",_th.getNumericValue("F22"), _th.calculateNumericFormula("A22"), 0);
123
		assertEquals("A23-F23",_th.getNumericValue("F23"), _th.calculateNumericFormula("A23"), 0);
124
		assertEquals("A24-F24",_th.getNumericValue("F24"), _th.calculateNumericFormula("A24"), 0);
125
		assertEquals("A25-F25",_th.getNumericValue("F25"), _th.calculateNumericFormula("A25"), 0);
126
	}
127
128
	public final void testNumericArrayFormulaRow4Col() {
129
130
		// Clean cell's values before calculation
131
		_th.setNumericValue("A27", 0);
132
		_th.setNumericValue("B27", 0);
133
		_th.setNumericValue("C27", 0);
134
		_th.setNumericValue("D27", 0);
135
136
		assertEquals("A27-F27",_th.getNumericValue("F27"), _th.calculateNumericFormula("A27"), 0);
137
		assertEquals("B27-G27",_th.getNumericValue("G27"), _th.calculateNumericFormula("B27"), 0);
138
		assertEquals("C27-H27",_th.getNumericValue("H27"), _th.calculateNumericFormula("C27"), 0);
139
		assertEquals("D27-I27",_th.getNumericValue("I27"), _th.calculateNumericFormula("D27"), 0);
140
	}
141
142
	public final void testNumericArrayFormulaDataRow4Col() {
143
144
		// Clean cell's values before calculation
145
		_th.setNumericValue("A27", 0);
146
		_th.setNumericValue("B27", 0);
147
		_th.setNumericValue("C27", 0);
148
		_th.setNumericValue("D27", 0);
149
150
		assertEquals("A27-F27",_th.getNumericValue("F27"), _th.calculateNumericFormula("A27"), 0);
151
		assertEquals("B27-G27",_th.getNumericValue("G27"), _th.calculateNumericFormula("B27"), 0);
152
		assertEquals("C27-H27",_th.getNumericValue("H27"), _th.calculateNumericFormula("C27"), 0);
153
		assertEquals("D27-I27",_th.getNumericValue("I27"), _th.calculateNumericFormula("D27"), 0);
154
	}
155
156
	public final void testNumericArrayFormulaDataShortage() {
157
158
		// Clean cell's values before calculation
159
		_th.setNumericValue("B30", 0);
160
		_th.setNumericValue("C30", 0);
161
		_th.setNumericValue("D30", 0);
162
		_th.setNumericValue("B31", 0);
163
		_th.setNumericValue("C31", 0);
164
		_th.setNumericValue("D31", 0);
165
		_th.setNumericValue("B32", 0);
166
		_th.setNumericValue("C32", 0);
167
		_th.setNumericValue("D32", 0);
168
169
		assertEquals("C30-G30",_th.getNumericValue("G30"), _th.calculateNumericFormula("C30"), 0);
170
		assertEquals("B30-F30",_th.getNumericValue("F30"), _th.calculateNumericFormula("B30"), 0);
171
		if (false) { // TODO - fix this
172
			assertEquals("C30-G30", _th.getNumericValue("G30"), _th.getNumericValue("C30"), 0);
173
		}
174
		assertEquals("B31-G30",_th.getNumericValue("F31"), _th.calculateNumericFormula("B31"), 0);
175
		assertEquals("C31-G31",_th.getNumericValue("G31"), _th.calculateNumericFormula("C31"), 0);
176
		assertEquals("D30-H30",_th.getErrorValue("H30"), _th.calculateNumericFormulaWithError("D30").getString());
177
		assertEquals("D31-H31",_th.getErrorValue("H31"), _th.calculateNumericFormulaWithError("D31").getString());
178
179
		assertEquals("B32-F32",_th.getErrorValue("F32"), _th.calculateNumericFormulaWithError("B32").getString());
180
		assertEquals("C32-G32",_th.getErrorValue("G32"), _th.calculateNumericFormulaWithError("C32").getString());
181
		assertEquals("D32-H32",_th.getErrorValue("H32"), _th.calculateNumericFormulaWithError("D32").getString());
182
	}
183
184
	public final void testNumericArrayFormulaRefArguments() {
185
186
		// Clean cell's values before calculation
187
		_th.setNumericValue("A37", 0);
188
		_th.setNumericValue("B37", 0);
189
		_th.setNumericValue("C37", 0);
190
191
		assertEquals("A37-F37",_th.getNumericValue("F37"), _th.calculateNumericFormula("A37"), 0);
192
		assertEquals("B37-G37",_th.getNumericValue("G37"), _th.calculateNumericFormula("B37"), 0);
193
		assertEquals("C37-H37",_th.getNumericValue("H37"), _th.calculateNumericFormula("C37"), 0);
194
	}
195
196
	public final void testNumericArrayFormulasRefArguments() {
197
198
		// Clean cell's values before calculation
199
		_th.setNumericValue("A40", 0);
200
		_th.setNumericValue("B40", 0);
201
		_th.setNumericValue("C40", 0);
202
203
		assertEquals("A40-F40",_th.getNumericValue("F40"), _th.calculateNumericFormula("A40"), 0);
204
		assertEquals("B40-G40",_th.getNumericValue("G40"), _th.calculateNumericFormula("B40"), 0);
205
		assertEquals("C40-H40",_th.getNumericValue("H40"), _th.calculateNumericFormula("C40"), 0);
206
	}
207
208
	public final void testNumericOperation4Range() {
209
210
		// Clean cell's values before calculation
211
		_th.setNumericValue("C43", 0);
212
		_th.setNumericValue("C44", 0);
213
		_th.setNumericValue("C45", 0);
214
		_th.setNumericValue("C46", 0);
215
216
		assertEquals("C43-F43",_th.getNumericValue("F43"), _th.calculateNumericFormula("C43"), 0);
217
		assertEquals("C44-F44",_th.getNumericValue("F44"), _th.calculateNumericFormula("C44"), 0);
218
		assertEquals("C45-F45",_th.getNumericValue("F45"), _th.calculateNumericFormula("C45"), 0);
219
		assertEquals("C46-F46",_th.getNumericValue("F46"), _th.calculateNumericFormula("C46"), 0);
220
	}
221
222
	public final void testNumericOperation4DiffRanges() {
223
224
		// Clean cell's values before calculation
225
		_th.setNumericValue("C48", 0);
226
		_th.setNumericValue("C49", 0);
227
		_th.setNumericValue("C50", 0);
228
		_th.setNumericValue("C51", 0);
229
230
		assertEquals("C48-F48",_th.getNumericValue("F48"), _th.calculateNumericFormula("C48"), 0);
231
		assertEquals("C49-F49",_th.getNumericValue("F49"), _th.calculateNumericFormula("C49"), 0);
232
		assertEquals("C50-F50",_th.getNumericValue("F50"), _th.calculateNumericFormula("C50"), 0);
233
		assertEquals("C51-F51",_th.getNumericValue("F51"), _th.calculateNumericFormula("C51"), 0);
234
		if (false) { // TODO - fix these
235
			assertEquals("C50-F50", _th.getErrorValue("F50"), _th.getErrorValue("C50"));
236
			assertEquals("C51-F51",_th.getErrorValue("F51"), _th.getErrorValue("C51"));
237
		}
238
	}
239
240
	public final void testNumericArrayChangeRefArguments() {
241
242
		// Clean cell's values before calculation
243
		_th.setNumericValue("A40", 0);
244
		_th.setNumericValue("B40", 0);
245
		_th.setNumericValue("C40", 0);
246
247
		assertEquals("A40-F40", _th.getNumericValue("F40"), _th.calculateNumericFormula("A40"), 0);
248
		assertEquals("B40-G40", _th.getNumericValue("G40"), _th.calculateNumericFormula("B40"), 0);
249
		assertEquals("C40-H40", _th.getNumericValue("H40"), _th.calculateNumericFormula("C40"), 0);
250
251
		_th.setNumericValue("A41", 0.4);
252
		_th.setNumericValue("B41", 0.5);
253
		_th.setNumericValue("C41", 0.6);
254
255
		assertEquals("B40-G40", Math.cos(Math.sin(0.5)), _th.calculateNumericFormula("B40"), 0);
256
		assertEquals("A40-F40", Math.cos(Math.sin(0.4)), _th.calculateNumericFormula("A40"), 0);
257
		assertEquals("C40-H40", Math.cos(Math.sin(0.6)), _th.calculateNumericFormula("C40"), 0);
258
	}
259
260
	public final void testNumericArrayDifTypeArguments() {
261
262
		// Clean cell's values before calculation
263
		_th.setNumericValue("A54", 0);
264
		_th.setNumericValue("B54", 0);
265
		_th.setNumericValue("C54", 0);
266
		_th.setNumericValue("A55", 0);
267
		_th.setNumericValue("B55", 0);
268
		_th.setNumericValue("C55", 0);
269
270
		assertEquals("A54-F54",_th.getNumericValue("F54"), _th.calculateNumericFormula("A54"), 0);
271
		assertEquals("B54-G54",_th.getNumericValue("G54"), _th.calculateNumericFormula("B54"), 0);
272
		assertEquals("C54-H54",_th.getNumericValue("H54"), _th.calculateNumericFormula("C54"), 0);
273
		assertEquals("A55-F55",_th.getNumericValue("F55"), _th.calculateNumericFormula("A55"), 0);
274
		assertEquals("B55-G55",_th.getNumericValue("G55"), _th.calculateNumericFormula("B55"), 0);
275
		assertEquals("C55-H55",_th.getNumericValue("H55"), _th.calculateNumericFormula("C55"), 0);
276
	}
277
}
(-)a/src/testcases/org/apache/poi/ss/formula/eval/BaseTestArrayFormulaFunction.java (+149 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.ss.formula.eval;
19
20
import org.apache.poi.ss.usermodel.ArrayFormulaTestHelper;
21
22
import junit.framework.TestCase;
23
24
public abstract class BaseTestArrayFormulaFunction extends TestCase {
25
26
	private ArrayFormulaTestHelper _th;
27
	protected abstract ArrayFormulaTestHelper th();
28
29
	protected void setUp() {
30
		_th = th();
31
	}
32
33
	/**
34
	 *  check if non-calculated array cells does not contain value
35
	 */
36
	public final void testNonCalculated1() {
37
		_th.confirmNotNumeric("C4");
38
	}
39
40
	public final void testHorizontalArray1() {
41
		assertEquals("C4-I4",_th.getNumericValue("I4"), _th.calculateNumericFormula("C4"), 0);
42
	}
43
44
	public final void testNonCalculated2() {
45
		_th.confirmNotNumeric("D4");
46
	}
47
48
	public final void testHorizontalArray2() {
49
		assertEquals("D4-J4",_th.getNumericValue("J4"), _th.calculateNumericFormula("D4"), 0);
50
		assertEquals("E4-K4",_th.getNumericValue("K4"), _th.calculateNumericFormula("E4"), 0);
51
	}
52
53
	public final void testHorizontalArray3() {
54
		assertTrue("C4:E4 - I4:K4",_th.calculateAndCompareNumericArray('C', 4, 'E', 4, 'I', 4, 0));
55
	}
56
57
	public final void testTwoDimArray() {
58
		assertTrue("C6:E7 - I6:K7",_th.calculateAndCompareNumericArray('C', 6, 'E', 7, 'I', 6, 0));
59
	}
60
61
	public final void testVertArray() {
62
		assertTrue("C9:C11 - I9:I11",_th.calculateAndCompareNumericArray('C', 9, 'C', 11, 'I', 9, 0));
63
	}
64
65
	public final void testPowerArray() {
66
		assertTrue("C16:D16 - I16:K16",_th.calculateAndCompareNumericArray('C', 16, 'D', 16, 'I', 16, 0));
67
	}
68
69
	public final void testPowerArrayChanged() {
70
		_th.setNumericValue("A15", 2);
71
		assertEquals("changed array", 4, _th.calculateNumericFormula("C16"),0);
72
	}
73
74
	public final void testIndexArray() {
75
		assertTrue("C23:C25 - I23:I25",_th.calculateAndCompareNumericArray('C', 23, 'C', 25, 'I', 23, 0));
76
	}
77
78
	public final void testMatrixMultiply() {
79
		assertTrue("C33:D34 - I33:K34",_th.calculateAndCompareNumericArray('C', 33, 'D', 34, 'I', 33, 0));
80
	}
81
82
	public final void testSumOnMultiply() {
83
		assertEquals("C41",_th.getNumericValue("I41"), _th.calculateNumericFormula("C41"), 0);
84
	}
85
86
	public final void testSumOnRangeArray() {
87
		assertEquals("C48",_th.getNumericValue("I48"), _th.calculateNumericFormula("C48"), 0);
88
	}
89
90
	public final void testSumOnTwoArraya() {
91
		assertEquals("C50",_th.getNumericValue("I50"), _th.calculateNumericFormula("C50"), 0);
92
	}
93
94
	public final void testArrayMultiply() {
95
		assertTrue("C55:F57 - I55:L57",_th.calculateAndCompareNumericArray('C', 55, 'F', 57, 'I', 55, 0));
96
	}
97
98
99
	public final void testSumOnLen() {
100
		assertEquals("C63",_th.getNumericValue("I63"), _th.calculateNumericFormula("C63"), 0);
101
	}
102
103
	public final void testSmallRangeArray() {
104
		assertTrue("C69:D69",_th.calculateAndCompareNumericArray('C', 69, 'D', 69, 'I', 69, 0));
105
	}
106
107
	public final void testAverageOnSmall() {
108
		assertEquals("C72",_th.getNumericValue("I72"), _th.calculateNumericFormula("C72"), 0);
109
	}
110
111
	public final void testSumOnIf() {
112
		assertEquals("C75",_th.getNumericValue("I75"), _th.calculateNumericFormula("C75"), 0);
113
	}
114
115
	public final void testSumOnComplexIf() {
116
		assertEquals("C78",_th.getNumericValue("I78"), _th.calculateNumericFormula("C78"), 0);
117
	}
118
119
	public final void testSumOnComplexIf2() {
120
		assertEquals("C84",_th.getNumericValue("I84"), _th.calculateNumericFormula("C84"), 0);
121
	}
122
123
	public final void testColumn() {
124
		assertTrue("C87:F87",_th.calculateAndCompareNumericArray('C', 87, 'F', 87, 'I', 87, 0));
125
	}
126
127
	public final void testRow() {
128
		assertTrue("C90:C94",_th.calculateAndCompareNumericArray('C', 90, 'C', 94, 'I', 90, 0));
129
	}
130
131
	public final void testColumnNonArray() {
132
		assertEquals("C88",_th.getNumericValue("I88"), _th.calculateNumericFormula("C88"), 0);
133
	}
134
135
	public final void testColumnArray() {
136
		assertEquals("C89",_th.getNumericValue("I89"), _th.calculateNumericFormula("C89"), 0);
137
	}
138
139
	public final void testReferenceToArray() {
140
		assertEquals("C99", _th.getNumericValue("i99"), _th.calculateNumericFormula("C99"),0);
141
	}
142
143
	public final void testReferenceArrayToArray() {
144
		assertEquals("B102", _th.getNumericValue("i102"), _th.calculateNumericFormula("b102"),0.001);
145
		assertEquals("B103", _th.getNumericValue("i103"), _th.calculateNumericFormula("b103"),0.001);
146
		assertEquals("C102", _th.getNumericValue("j102"), _th.calculateNumericFormula("c102"),0.001);
147
		assertEquals("C103", _th.getNumericValue("j103"), _th.calculateNumericFormula("C103"),0.001);
148
	}
149
}
(-)a/src/testcases/org/apache/poi/ss/usermodel/ArrayFormulaTestHelper.java (+258 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.ss.usermodel;
19
20
import junit.framework.AssertionFailedError;
21
22
import org.apache.commons.logging.Log;
23
import org.apache.commons.logging.LogFactory;
24
import org.apache.poi.hssf.HSSFTestDataSamples;
25
import org.apache.poi.ss.usermodel.Cell;
26
import org.apache.poi.ss.usermodel.ErrorConstants;
27
import org.apache.poi.ss.usermodel.FormulaError;
28
import org.apache.poi.ss.usermodel.FormulaEvaluator;
29
import org.apache.poi.ss.usermodel.Row;
30
import org.apache.poi.ss.usermodel.Sheet;
31
import org.apache.poi.ss.usermodel.Workbook;
32
import org.apache.poi.ss.util.CellRangeAddress;
33
import org.apache.poi.ss.util.CellReference;
34
import org.apache.poi.xssf.XSSFTestDataSamples;
35
36
public final class ArrayFormulaTestHelper {
37
	private static final Log log = LogFactory.getLog(ArrayFormulaTestHelper.class);
38
39
	private final Workbook _wb;
40
41
	public ArrayFormulaTestHelper(String sampleFileName) {
42
43
		boolean isXssf = sampleFileName.endsWith(".xlsx");
44
		if (isXssf) {
45
			_wb = XSSFTestDataSamples.openSampleWorkbook(sampleFileName);
46
		} else {
47
			_wb = HSSFTestDataSamples.openSampleWorkbook(sampleFileName);
48
		}
49
	}
50
51
	public Cell getCell(String cellRef) {
52
53
		log.debug("Access to Cell:" + cellRef);
54
		Sheet sheet = _wb.getSheetAt(0);
55
		CellReference cellReference = new CellReference(cellRef);
56
		Row row = sheet.getRow(cellReference.getRow());
57
		if (row==null) {
58
			throw new IllegalArgumentException("Illegal access to cell:" + cellRef);
59
		}
60
		Cell cell = row.getCell(cellReference.getCol());
61
		if (cell==null) {
62
			throw new IllegalArgumentException("Illegal access to cell:" + cellRef);
63
		}
64
		return cell;
65
	}
66
67
68
	public FormulaError calculateNumericFormulaWithError(String cellRef) {
69
		Cell cell = getCell(cellRef);
70
		if (cell.getCellType() != Cell.CELL_TYPE_FORMULA) {
71
			log.error("Not formula in cell: " + cell.toString());
72
			throw new IllegalArgumentException("Not formula" + cell.toString());
73
		}
74
75
		log.debug("Formula: " + cell.getCellFormula());
76
		FormulaEvaluator evaluator = _wb.getCreationHelper().createFormulaEvaluator();
77
		int type = evaluator.evaluateFormulaCell(cell);
78
		if (type != Cell.CELL_TYPE_ERROR) {
79
			log.error("not error result");
80
			throw new IllegalArgumentException("Not error type: " + type);
81
		}
82
83
		byte result = cell.getErrorCellValue();
84
		log.debug("Error value:" + result);
85
		return FormulaError.forInt(result);
86
	}
87
88
	public String calculateStringFormula(String cellRef) {
89
90
		Cell cell = getCell(cellRef);
91
		if (cell.getCellType() != Cell.CELL_TYPE_FORMULA) {
92
			log.error("Not formula in cell: " + cell.toString());
93
			throw new IllegalArgumentException("Not formula" + cell.toString());
94
		}
95
96
		log.debug("Formula: " + cell.getCellFormula());
97
		FormulaEvaluator evaluator = _wb.getCreationHelper().createFormulaEvaluator();
98
		int type = evaluator.evaluateFormulaCell(cell);
99
		if (type != Cell.CELL_TYPE_STRING) {
100
			log.error("not string result: " + type);
101
			throw new IllegalArgumentException("Not string type" + type);
102
		}
103
104
		String result = cell.getStringCellValue();
105
		log.debug("Calculated:" + result);
106
		return result;
107
	}
108
109
	public double calculateNumericFormula(String cellRef) {
110
111
		Cell cell = getCell(cellRef);
112
		if (cell.getCellType() != Cell.CELL_TYPE_FORMULA) {
113
			log.error("Not formula in cell: " + cell.toString());
114
			throw new IllegalArgumentException("Not formula" + cell.toString());
115
		}
116
117
		log.debug("Formula: " + cell.getCellFormula());
118
		FormulaEvaluator evaluator = _wb.getCreationHelper().createFormulaEvaluator();
119
		int type = evaluator.evaluateFormulaCell(cell);
120
		if (type != Cell.CELL_TYPE_NUMERIC) {
121
			log.error("not numeric result: " + type);
122
			throw new IllegalArgumentException("Not numeric type" + type);
123
		}
124
125
		double result = cell.getNumericCellValue();
126
		log.debug("Calculated:" + result);
127
		return result;
128
	}
129
130
	public boolean calculateBooleanFormula(String cellRef) {
131
132
		Cell cell = getCell(cellRef);
133
		if (cell.getCellType() != Cell.CELL_TYPE_FORMULA) {
134
			log.error("Not formula in cell: " + cell.toString());
135
			throw new IllegalArgumentException("Not formula" + cell.toString());
136
		}
137
138
		log.debug("Formula: " + cell.getCellFormula());
139
		FormulaEvaluator evaluator = _wb.getCreationHelper().createFormulaEvaluator();
140
		int type = evaluator.evaluateFormulaCell(cell);
141
		if (type != Cell.CELL_TYPE_BOOLEAN) {
142
			log.error("not boolean result: " + type);
143
			throw new IllegalArgumentException("Not boolean type" + type);
144
		}
145
146
		boolean result = cell.getBooleanCellValue();
147
		log.debug("Calculated:" + result);
148
		return result;
149
	}
150
151
	public void confirmNotNumeric(String cellRef) {
152
		Cell cell = getCell(cellRef);
153
		if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
154
			throw new AssertionFailedError("Cell type was unexpectedly numeric:" + cell.toString());
155
		}
156
	}
157
158
	public void setNumericValue(String cellRef, double value) {
159
		getCell(cellRef).setCellValue(value);
160
	}
161
162
	public double getNumericValue(String cellRef) {
163
164
		Cell cell = getCell(cellRef);
165
		if (cell.getCellType() != Cell.CELL_TYPE_NUMERIC) {
166
			log.error("Not numeric in cell:" + cell.toString());
167
			throw new IllegalArgumentException("Not numeric:" + cell.toString());
168
		}
169
		double result = cell.getNumericCellValue();
170
		log.debug("value: " + result );
171
		return result;
172
	}
173
174
	public String getStringValue(String cellRef) {
175
176
		Cell cell = getCell(cellRef);
177
		if (cell.getCellType() != Cell.CELL_TYPE_STRING) {
178
			log.error("Not string in cell:" + cell.toString());
179
			throw new IllegalArgumentException("Not string:" + cell.toString());
180
		}
181
		String result = cell.getStringCellValue();
182
		log.debug("value: " + result );
183
		return result;
184
	}
185
186
	public boolean getBooleanValue(String cellRef) {
187
188
		Cell cell = getCell(cellRef);
189
		if (cell.getCellType() != Cell.CELL_TYPE_BOOLEAN) {
190
			log.error("Not boolean in cell:" + cell.toString());
191
			throw new IllegalArgumentException("Not boolean:" + cell.toString());
192
		}
193
		boolean result = cell.getBooleanCellValue();
194
		log.debug("value: " + result );
195
		return result;
196
	}
197
198
	public boolean calculateAndCompareNumericArray(char calculateFromColumn, int calculateFromRow, char calculateToColumn,
199
			int calculateToRow, char compareFromColumn, int compareFromRow, double precision) {
200
201
		if (calculateFromColumn> calculateToColumn) {
202
			throw new IllegalArgumentException("columns: " + calculateFromColumn + "   " + calculateToColumn);
203
		}
204
205
		if (calculateFromRow > calculateToRow) {
206
			throw new IllegalArgumentException("rows: " + calculateFromRow + "   " + calculateToRow);
207
		}
208
209
		for(int c=0; calculateFromColumn + c <= calculateToColumn; c++ ) {
210
			for(int r=0; calculateFromRow +r <= calculateToRow; r++) {
211
				char ch[] = new char[1];
212
				ch[0] = (char)(calculateFromColumn+c);
213
				String calculateRef = (new String(ch)) + (calculateFromRow+r);
214
				double calcResult = calculateNumericFormula(calculateRef);
215
216
				ch[0]=(char)(compareFromColumn+c);
217
				String compareRef = (new String(ch)) + (compareFromRow + r);
218
				double compareResult = getNumericValue(compareRef);
219
220
				if (Math.abs(compareResult - calcResult) > precision) {
221
					// comparison failed
222
					log.debug("Array comparison failed. " + "Calculated cell:" + calculateRef + " Compared cell: " + compareRef +
223
					"   calculated result: " + calcResult + "  compared result: " + compareResult);
224
					return false;
225
				}
226
			}
227
		}
228
		return true;
229
	}
230
231
	public void setArrayFormula(String cellRef, String formula, String range) {
232
		Sheet sheet = getCell(cellRef).getSheet();
233
		sheet.setArrayFormula(formula, CellRangeAddress.valueOf(range));
234
	}
235
236
	public void removeArrayFormula(String cellRef) {
237
		Cell cell = getCell(cellRef);
238
		Sheet sheet = cell.getSheet();
239
		sheet.removeArrayFormula(cell);
240
	}
241
242
	public String getErrorValue(String cellRef) {
243
244
		Cell cell = getCell(cellRef);
245
		if (cell.getCellType() != Cell.CELL_TYPE_ERROR) {
246
			log.error("Not numeric in cell:" + cell.toString());
247
			throw new IllegalArgumentException("Not error:" + cell.toString());
248
		}
249
		byte result = cell.getErrorCellValue();
250
		log.debug("value: " + result );
251
		return ErrorConstants.getText(result);
252
	}
253
254
	public int getCellType(String cellRef) {
255
		Cell cell = getCell(cellRef);
256
		return cell.getCellType();
257
	}
258
}
(-)a/src/testcases/org/apache/poi/ss/usermodel/BaseTestAddRemoveArrayFormula.java (+121 lines)
Line 0 Link Here
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
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
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.
16
==================================================================== */
17
18
package org.apache.poi.ss.usermodel;
19
20
import junit.framework.TestCase;
21
22
import org.apache.poi.ss.ITestDataProvider;
23
import org.apache.poi.ss.usermodel.Cell;
24
import org.apache.poi.ss.usermodel.FormulaEvaluator;
25
import org.apache.poi.ss.usermodel.Row;
26
import org.apache.poi.ss.usermodel.Sheet;
27
import org.apache.poi.ss.usermodel.Workbook;
28
import org.apache.poi.ss.util.CellRangeAddress;
29
30
/**
31
 * Array Formula Set/Remove test class
32
 *
33
 * Workbook contains formulas and tests
34
 */
35
public abstract class BaseTestAddRemoveArrayFormula extends TestCase {
36
37
	protected final ITestDataProvider _testDataProvider;
38
	private final String _sampleSpreadsheetName;
39
40
	private ArrayFormulaTestHelper _th;
41
42
	protected BaseTestAddRemoveArrayFormula(ITestDataProvider testDataProvider, String sampleSpreadsheetName) {
43
		_testDataProvider = testDataProvider;
44
		_sampleSpreadsheetName = sampleSpreadsheetName;
45
	}
46
47
	protected void setUp() {
48
		_th = new ArrayFormulaTestHelper(_sampleSpreadsheetName);
49
	}
50
51
52
	public final void testNewNumericArrayFormula() {
53
54
		_th.setArrayFormula("A35", "SIN({0.1,0.2,0.3})", "A35:B35");
55
56
		assertEquals("A35-F35", _th.getNumericValue("F35"), _th.calculateNumericFormula("A35"), 0);
57
		assertEquals("B35-G35", _th.getNumericValue("G35"), _th.calculateNumericFormula("B35"), 0);
58
	}
59
60
	public final void testNewNumericArrayFormulaOut() {
61
62
		// create empty workbook
63
		Workbook workbook = _testDataProvider.createWorkbook();
64
		Sheet sheet = workbook.createSheet("Sheet1");
65
66
		Row rowd = sheet.createRow((short) (0));
67
		Cell cd = rowd.createCell((short) 0);
68
		CellRangeAddress range = new CellRangeAddress(0, 1, 0, 1);
69
		sheet.setArrayFormula("SQRT({1,4;9,16})", range);
70
71
		// Calculate formula
72
		FormulaEvaluator eval = workbook.getCreationHelper().createFormulaEvaluator();
73
		eval.evaluateFormulaCell(cd);
74
75
		// Set tested values (copy from 5 rows above)
76
		for (int rowIn = range.getFirstRow(); rowIn <= range.getLastRow(); rowIn++) {
77
			for (int colIn = range.getFirstColumn(); colIn <= range.getLastColumn(); colIn++) {
78
				Cell cell = sheet.getRow(rowIn).getCell(colIn);
79
				int lowerRowIx = rowIn + 5;
80
				double value = cell.getNumericCellValue();
81
				Row row = sheet.getRow(lowerRowIx);
82
				if (row == null) {
83
					row = sheet.createRow(lowerRowIx);
84
				}
85
				row.createCell(colIn).setCellValue(value);
86
			}
87
		}
88
89
		// serialize/deserialize file
90
		workbook = _testDataProvider.writeOutAndReadBack(workbook);
91
		sheet = workbook.getSheetAt(0);
92
		// Set 0 values before calculation
93
		for (int rowIn = range.getFirstRow(); rowIn <= range.getLastRow(); rowIn++) {
94
			for (int colIn = range.getFirstColumn(); colIn <= range.getLastColumn(); colIn++) {
95
				sheet.getRow(rowIn).getCell(colIn).setCellValue(0.0);
96
			}
97
		}
98
		// Calculate formula (we use cell from firstRow and firstColumn)
99
		eval = workbook.getCreationHelper().createFormulaEvaluator();
100
		eval.evaluateFormulaCell(sheet.getRow(range.getFirstRow()).getCell(range.getFirstColumn()));
101
		// Check calculated values
102
		for (int rowIn = range.getFirstRow(); rowIn <= range.getLastRow(); rowIn++) {
103
			for (int colIn = range.getFirstColumn(); colIn <= range.getLastColumn(); colIn++) {
104
				Cell cell = sheet.getRow(rowIn).getCell(colIn);
105
				double value = cell.getNumericCellValue();
106
				cell = sheet.getRow(rowIn + 5).getCell(colIn);
107
				assertEquals("ArrayFormula:" + rowIn + "," + colIn, cell.getNumericCellValue(),
108
						value, 0);
109
			}
110
		}
111
	}
112
113
	public final void testRemoveArrayFormula() {
114
115
		_th.removeArrayFormula("A40");
116
117
		assertEquals("A40-F40", Cell.CELL_TYPE_BLANK, _th.getCellType("A40"), 0);
118
		assertEquals("B40-G40", Cell.CELL_TYPE_BLANK, _th.getCellType("B40"), 0);
119
		assertEquals("C40-H40", Cell.CELL_TYPE_BLANK, _th.getCellType("C40"), 0);
120
	}
121
}

Return to bug 48292