Converted AreEval and RefEval to be lazy (part of fix for bug 45358)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@690835 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-09-01 01:48:45 +00:00
parent 020bde0d66
commit 30819e2f9b
30 changed files with 595 additions and 824 deletions

View File

@ -14,11 +14,11 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula;
/**
* Common interface for AreaPtg and Area3DPtg, and their
* child classes.
* Common interface for AreaPtg and Area3DPtg, and their child classes.
*/
public interface AreaI {
/**
@ -40,4 +40,37 @@ public interface AreaI {
* @return lastcolumn in the area
*/
public int getLastColumn();
class OffsetArea implements AreaI {
private final int _firstColumn;
private final int _firstRow;
private final int _lastColumn;
private final int _lastRow;
public OffsetArea(int baseRow, int baseColumn, int relFirstRowIx, int relLastRowIx,
int relFirstColIx, int relLastColIx) {
_firstRow = baseRow + relFirstRowIx;
_lastRow = baseRow + relLastRowIx;
_firstColumn = baseColumn + relFirstColIx;
_lastColumn = baseColumn + relLastColIx;
}
public int getFirstColumn() {
return _firstColumn;
}
public int getFirstRow() {
return _firstRow;
}
public int getLastColumn() {
return _lastColumn;
}
public int getLastRow() {
return _lastRow;
}
}
}

View File

@ -131,14 +131,6 @@ public abstract class RefPtgBase extends OperandPtg {
field_2_col=colRelative.setBoolean(field_2_col,rel);
}
public final void setColumnRawX(int col) { // TODO
field_2_col = col;
}
public int getColumnRawX() { // TODO
return field_2_col;
}
public final void setColumn(int col) {
if(col < 0 || col >= 0x100) {
throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range");

View File

@ -1,32 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.Ptg;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class Area2DEval extends AreaEvalBase {
public Area2DEval(Ptg ptg, ValueEval[] values) {
super((AreaPtg) ptg, values);
}
}

View File

@ -1,39 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.Ptg;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class Area3DEval extends AreaEvalBase {
private final int _externSheetIndex;
public Area3DEval(Ptg ptg, ValueEval[] values) {
super((Area3DPtg) ptg, values);
_externSheetIndex = ((Area3DPtg) ptg).getExternSheetIndex();
}
public int getExternSheetIndex() {
return _externSheetIndex;
}
}

View File

@ -97,4 +97,10 @@ public interface AreaEval extends ValueEval {
* specified indexes should relative to the top left corner of this area.
*/
ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex);
/**
* Creates an {@link AreaEval} offset by a relative amount from from the upper left cell
* of this area
*/
AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx);
}

View File

@ -22,20 +22,16 @@ import org.apache.poi.hssf.record.formula.AreaI;
/**
* @author Josh Micich
*/
abstract class AreaEvalBase implements AreaEval {
public abstract class AreaEvalBase implements AreaEval {
private final int _firstColumn;
private final int _firstRow;
private final int _lastColumn;
private final int _lastRow;
private final ValueEval[] _values;
private final int _nColumns;
private final int _nRows;
protected AreaEvalBase(AreaI ptg, ValueEval[] values) {
if (values == null) {
throw new IllegalArgumentException("values must not be null");
}
protected AreaEvalBase(AreaI ptg) {
_firstRow = ptg.getFirstRow();
_firstColumn = ptg.getFirstColumn();
_lastRow = ptg.getLastRow();
@ -43,22 +39,6 @@ abstract class AreaEvalBase implements AreaEval {
_nColumns = _lastColumn - _firstColumn + 1;
_nRows = _lastRow - _firstRow + 1;
int expectedItemCount = _nRows * _nColumns;
if ((values.length != expectedItemCount)) {
// Note - this math may need alteration when POI starts to support full column or full row refs
throw new IllegalArgumentException("Array size should be (" + expectedItemCount
+ ") but was (" + values.length + ")");
}
for (int i = values.length - 1; i >= 0; i--) {
if (values[i] == null) {
throw new IllegalArgumentException("value array elements must not be null");
}
}
_values = values;
}
public final int getFirstColumn() {
@ -116,14 +96,7 @@ abstract class AreaEvalBase implements AreaEval {
return _lastRow-_firstRow+1;
}
public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
int index = relativeRowIndex * _nColumns + relativeColumnIndex;
ValueEval result = _values[index];
if (result == null) {
return BlankEval.INSTANCE;
}
return result;
}
public abstract ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex);
public int getWidth() {
return _lastColumn-_firstColumn+1;

View File

@ -50,7 +50,6 @@ public abstract class FunctionEval implements OperationEval {
static {
Map m = new HashMap();
addMapping(m, ID.OFFSET, new Offset());
addMapping(m, ID.INDIRECT, new Indirect());
addMapping(m, ID.EXTERNAL_FUNC, new ExternalFunction());
freeRefFunctionsByIdMap = m;
@ -155,7 +154,7 @@ public abstract class FunctionEval implements OperationEval {
retval[75] = new Areas(); // AREAS
retval[76] = new Rows(); // ROWS
retval[77] = new Columns(); // COLUMNS
retval[ID.OFFSET] = null; // Offset.evaluate has a different signature
retval[ID.OFFSET] = new Offset(); // OFFSET
retval[79] = new Absref(); // ABSREF
retval[80] = new Relref(); // RELREF
retval[81] = new Argument(); // ARGUMENT

View File

@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.AreaI;
import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class LazyAreaEval extends AreaEvalBase {
private final HSSFSheet _sheet;
private HSSFWorkbook _workbook;
public LazyAreaEval(AreaI ptg, HSSFSheet sheet, HSSFWorkbook workbook) {
super(ptg);
_sheet = sheet;
_workbook = workbook;
}
public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
int rowIx = (relativeRowIndex + getFirstRow() ) & 0xFFFF;
int colIx = (relativeColumnIndex + getFirstColumn() ) & 0x00FF;
HSSFRow row = _sheet.getRow(rowIx);
if (row == null) {
return BlankEval.INSTANCE;
}
HSSFCell cell = row.getCell(colIx);
if (cell == null) {
return BlankEval.INSTANCE;
}
return HSSFFormulaEvaluator.getEvalForCell(cell, _sheet, _workbook);
}
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
return new LazyAreaEval(area, _sheet, _workbook);
}
}

View File

@ -0,0 +1,52 @@
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.AreaI;
import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
public final class LazyRefEval extends RefEvalBase {
private final HSSFSheet _sheet;
private final HSSFWorkbook _workbook;
public LazyRefEval(RefPtg ptg, HSSFSheet sheet, HSSFWorkbook workbook) {
super(ptg.getRow(), ptg.getColumn());
_sheet = sheet;
_workbook = workbook;
}
public LazyRefEval(Ref3DPtg ptg, HSSFSheet sheet, HSSFWorkbook workbook) {
super(ptg.getRow(), ptg.getColumn());
_sheet = sheet;
_workbook = workbook;
}
public ValueEval getInnerValueEval() {
int rowIx = getRow();
int colIx = getColumn();
HSSFRow row = _sheet.getRow(rowIx);
if (row == null) {
return BlankEval.INSTANCE;
}
HSSFCell cell = row.getCell(colIx);
if (cell == null) {
return BlankEval.INSTANCE;
}
return HSSFFormulaEvaluator.getEvalForCell(cell, _sheet, _workbook);
}
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
AreaI area = new OffsetArea(getRow(), getColumn(),
relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
return new LazyAreaEval(area, _sheet, _workbook);
}
}

View File

@ -47,4 +47,7 @@ public final class Ref2DEval implements RefEval {
public int getColumn() {
return delegate.getColumn();
}
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
throw new RuntimeException("should not be called"); // TODO - delete this whole class
}
}

View File

@ -1,53 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
/**
* @author Amol S. Deshmukh
*
*/
public final class Ref3DEval implements RefEval {
private final ValueEval value;
private final Ref3DPtg delegate;
public Ref3DEval(Ref3DPtg ptg, ValueEval ve) {
if(ve == null) {
throw new IllegalArgumentException("ve must not be null");
}
if(ptg == null) {
throw new IllegalArgumentException("ptg must not be null");
}
value = ve;
delegate = ptg;
}
public ValueEval getInnerValueEval() {
return value;
}
public int getRow() {
return delegate.getRow();
}
public int getColumn() {
return delegate.getColumn();
}
public int getExternSheetIndex() {
return delegate.getExternSheetIndex();
}
}

View File

@ -1,19 +1,19 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.eval;
@ -30,22 +30,22 @@ package org.apache.poi.hssf.record.formula.eval;
public interface RefEval extends ValueEval {
/**
* The (possibly evaluated) ValueEval contained
* in this RefEval. eg. if cell A1 contains "test"
* then in a formula referring to cell A1
* the RefEval representing
* A1 will return as the getInnerValueEval() the
* object of concrete type StringEval
* @return the evaluated value of the cell referred to by this RefEval.
*/
public ValueEval getInnerValueEval();
ValueEval getInnerValueEval();
/**
* returns the zero based column index.
*/
public int getColumn();
int getColumn();
/**
* returns the zero based row index.
*/
public int getRow();
int getRow();
/**
* Creates an {@link AreaEval} offset by a relative amount from this RefEval
*/
AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx);
}

View File

@ -0,0 +1,18 @@
package org.apache.poi.hssf.record.formula.eval;
public abstract class RefEvalBase implements RefEval {
private final int _rowIndex;
private final int _columnIndex;
protected RefEvalBase(int rowIndex, int columnIndex) {
_rowIndex = rowIndex;
_columnIndex = columnIndex;
}
public final int getRow() {
return _rowIndex;
}
public final int getColumn() {
return _columnIndex;
}
}

View File

@ -67,6 +67,9 @@ final class CountUtils {
return 0;
}
public static int countArg(Eval eval, I_MatchPredicate criteriaPredicate) {
if (eval == null) {
throw new IllegalArgumentException("eval must not be null");
}
if (eval instanceof AreaEval) {
return CountUtils.countMatchingCellsInArea((AreaEval) eval, criteriaPredicate);
}

View File

@ -1,28 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on Nov 25, 2006
*
*/
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
@ -37,8 +37,13 @@ public final class If implements Function {
case 3:
evalWhenFalse = args[2];
case 2:
BoolEval beval = (BoolEval) args[0]; // TODO - class cast exception
if (beval.getBooleanValue()) {
boolean b;
try {
b = evaluateFirstArg(args[0], srcCellRow, srcCellCol);
} catch (EvaluationException e) {
return e.getErrorEval();
}
if (b) {
return args[1];
}
return evalWhenFalse;
@ -46,4 +51,13 @@ public final class If implements Function {
return ErrorEval.VALUE_INVALID;
}
}
private static boolean evaluateFirstArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
Boolean b = OperandResolver.coerceValueToBoolean(ve, false);
if (b == null) {
return false;
}
return b.booleanValue();
}
}

View File

@ -17,8 +17,6 @@
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
@ -366,13 +364,7 @@ final class LookupUtils {
// Make this cell ref look like a 1x1 area ref.
// It doesn't matter if eval is a 2D or 3D ref, because that detail is never asked of AreaEval.
// This code only requires the value array item.
// anything would be ok for rowIx and colIx, but may as well get it right.
int rowIx = refEval.getRow();
int colIx = refEval.getColumn();
AreaPtg ap = new AreaPtg(rowIx, rowIx, colIx, colIx, false, false, false, false);
ValueEval value = refEval.getInnerValueEval();
return new Area2DEval(ap, new ValueEval[] { value, });
return refEval.offset(0, 0, 0, 0);
}
throw EvaluationException.invalidValue();
}

View File

@ -455,85 +455,6 @@ public final class MathX {
return d;
}
/**
* returns the sum of difference of squares of corresponding double
* value in each subarray: ie. sigma (xarr[i]^2-yarr[i]^2)
* <br/>
* It is the responsibility of the caller
* to ensure that the two subarrays are of equal length. If the
* subarrays are not of equal length, the return value can be
* unpredictable.
* @param xarr
* @param yarr
*/
public static double sumx2my2(double[] xarr, double[] yarr) {
double d = 0;
try {
for (int i=0, iSize=xarr.length; i<iSize; i++) {
d += (xarr[i] + yarr[i]) * (xarr[i] - yarr[i]);
}
}
catch (ArrayIndexOutOfBoundsException ae) {
d = Double.NaN;
}
return d;
}
/**
* returns the sum of sum of squares of corresponding double
* value in each subarray: ie. sigma (xarr[i]^2 + yarr[i]^2)
* <br/>
* It is the responsibility of the caller
* to ensure that the two subarrays are of equal length. If the
* subarrays are not of equal length, the return value can be
* unpredictable.
* @param xarr
* @param yarr
*/
public static double sumx2py2(double[] xarr, double[] yarr) {
double d = 0;
try {
for (int i=0, iSize=xarr.length; i<iSize; i++) {
d += (xarr[i] * xarr[i]) + (yarr[i] * yarr[i]);
}
}
catch (ArrayIndexOutOfBoundsException ae) {
d = Double.NaN;
}
return d;
}
/**
* returns the sum of squares of difference of corresponding double
* value in each subarray: ie. sigma ( (xarr[i]-yarr[i])^2 )
* <br/>
* It is the responsibility of the caller
* to ensure that the two subarrays are of equal length. If the
* subarrays are not of equal length, the return value can be
* unpredictable.
* @param xarr
* @param yarr
*/
public static double sumxmy2(double[] xarr, double[] yarr) {
double d = 0;
try {
for (int i=0, iSize=xarr.length; i<iSize; i++) {
double t = (xarr[i] - yarr[i]);
d += t * t;
}
}
catch (ArrayIndexOutOfBoundsException ae) {
d = Double.NaN;
}
return d;
}
/**
* returns the total number of combinations possible when

View File

@ -1,26 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.eval.Area3DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
@ -28,13 +24,9 @@ import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.Ref3DEval;
import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* Implementation for Excel function OFFSET()<p/>
*
@ -51,7 +43,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
*
* @author Josh Micich
*/
public final class Offset implements FreeRefFunction {
public final class Offset implements Function {
// These values are specific to BIFF8
private static final int LAST_VALID_ROW_INDEX = 0xFFFF;
private static final int LAST_VALID_COLUMN_INDEX = 0xFF;
@ -125,37 +117,29 @@ public final class Offset implements FreeRefFunction {
* Encapsulates either an area or cell reference which may be 2d or 3d.
*/
private static final class BaseRef {
private static final int INVALID_SHEET_INDEX = -1;
private final int _firstRowIndex;
private final int _firstColumnIndex;
private final int _width;
private final int _height;
private final int _externalSheetIndex;
private final RefEval _refEval;
private final AreaEval _areaEval;
public BaseRef(RefEval re) {
_refEval = re;
_areaEval = null;
_firstRowIndex = re.getRow();
_firstColumnIndex = re.getColumn();
_height = 1;
_width = 1;
if (re instanceof Ref3DEval) {
Ref3DEval r3e = (Ref3DEval) re;
_externalSheetIndex = r3e.getExternSheetIndex();
} else {
_externalSheetIndex = INVALID_SHEET_INDEX;
}
}
public BaseRef(AreaEval ae) {
_refEval = null;
_areaEval = ae;
_firstRowIndex = ae.getFirstRow();
_firstColumnIndex = ae.getFirstColumn();
_height = ae.getLastRow() - ae.getFirstRow() + 1;
_width = ae.getLastColumn() - ae.getFirstColumn() + 1;
if (ae instanceof Area3DEval) {
Area3DEval a3e = (Area3DEval) ae;
_externalSheetIndex = a3e.getExternSheetIndex();
} else {
_externalSheetIndex = INVALID_SHEET_INDEX;
}
}
public int getWidth() {
@ -170,19 +154,17 @@ public final class Offset implements FreeRefFunction {
public int getFirstColumnIndex() {
return _firstColumnIndex;
}
public boolean isIs3d() {
return _externalSheetIndex > 0;
}
public short getExternalSheetIndex() {
if(_externalSheetIndex < 0) {
throw new IllegalStateException("external sheet index only available for 3d refs");
public AreaEval offset(int relFirstRowIx, int relLastRowIx,
int relFirstColIx, int relLastColIx) {
if (_refEval == null) {
return _areaEval.offset(relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
}
return (short) _externalSheetIndex;
return _refEval.offset(relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
}
}
public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet) {
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
if(args.length < 3 || args.length > 5) {
return ErrorEval.VALUE_INVALID;
@ -206,37 +188,25 @@ public final class Offset implements FreeRefFunction {
}
LinearOffsetRange rowOffsetRange = new LinearOffsetRange(rowOffset, height);
LinearOffsetRange colOffsetRange = new LinearOffsetRange(columnOffset, width);
return createOffset(baseRef, rowOffsetRange, colOffsetRange, workbook, sheet);
return createOffset(baseRef, rowOffsetRange, colOffsetRange);
} catch (EvaluationException e) {
return e.getErrorEval();
}
}
private static AreaEval createOffset(BaseRef baseRef,
LinearOffsetRange rowOffsetRange, LinearOffsetRange colOffsetRange,
HSSFWorkbook workbook, HSSFSheet sheet) throws EvaluationException {
LinearOffsetRange orRow, LinearOffsetRange orCol) throws EvaluationException {
LinearOffsetRange rows = rowOffsetRange.normaliseAndTranslate(baseRef.getFirstRowIndex());
LinearOffsetRange cols = colOffsetRange.normaliseAndTranslate(baseRef.getFirstColumnIndex());
LinearOffsetRange absRows = orRow.normaliseAndTranslate(baseRef.getFirstRowIndex());
LinearOffsetRange absCols = orCol.normaliseAndTranslate(baseRef.getFirstColumnIndex());
if(rows.isOutOfBounds(0, LAST_VALID_ROW_INDEX)) {
if(absRows.isOutOfBounds(0, LAST_VALID_ROW_INDEX)) {
throw new EvaluationException(ErrorEval.REF_INVALID);
}
if(cols.isOutOfBounds(0, LAST_VALID_COLUMN_INDEX)) {
if(absCols.isOutOfBounds(0, LAST_VALID_COLUMN_INDEX)) {
throw new EvaluationException(ErrorEval.REF_INVALID);
}
if(baseRef.isIs3d()) {
Area3DPtg a3dp = new Area3DPtg(rows.getFirstIndex(), rows.getLastIndex(),
cols.getFirstIndex(), cols.getLastIndex(),
false, false, false, false,
baseRef.getExternalSheetIndex());
return HSSFFormulaEvaluator.evaluateArea3dPtg(workbook, a3dp);
}
AreaPtg ap = new AreaPtg(rows.getFirstIndex(), rows.getLastIndex(),
cols.getFirstIndex(), cols.getLastIndex(),
false, false, false, false);
return HSSFFormulaEvaluator.evaluateAreaPtg(sheet, workbook, ap);
return baseRef.offset(orRow.getFirstIndex(), orRow.getLastIndex(), orCol.getFirstIndex(), orCol.getLastIndex());
}
private static BaseRef evaluateBaseRef(Eval eval) throws EvaluationException {

View File

@ -17,10 +17,7 @@
package org.apache.poi.hssf.usermodel;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;
import org.apache.poi.hssf.model.FormulaParser;
@ -29,6 +26,7 @@ import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.BoolPtg;
import org.apache.poi.hssf.record.formula.ControlPtg;
import org.apache.poi.hssf.record.formula.ErrPtg;
import org.apache.poi.hssf.record.formula.IntPtg;
import org.apache.poi.hssf.record.formula.MemErrPtg;
import org.apache.poi.hssf.record.formula.MissingArgPtg;
@ -42,20 +40,18 @@ import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.record.formula.StringPtg;
import org.apache.poi.hssf.record.formula.UnionPtg;
import org.apache.poi.hssf.record.formula.UnknownPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.Area3DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.FunctionEval;
import org.apache.poi.hssf.record.formula.eval.LazyAreaEval;
import org.apache.poi.hssf.record.formula.eval.LazyRefEval;
import org.apache.poi.hssf.record.formula.eval.NameEval;
import org.apache.poi.hssf.record.formula.eval.NameXEval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.OperationEval;
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
import org.apache.poi.hssf.record.formula.eval.Ref3DEval;
import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
@ -66,32 +62,6 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
*/
public class HSSFFormulaEvaluator {
// params to lookup the right constructor using reflection
private static final Class[] VALUE_CONTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
private static final Class[] AREA3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval[].class };
private static final Class[] REFERENCE_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
private static final Class[] REF3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
// Maps for mapping *Eval to *Ptg
private static final Map VALUE_EVALS_MAP = new HashMap();
/*
* Following is the mapping between the Ptg tokens returned
* by the FormulaParser and the *Eval classes that are used
* by the FormulaEvaluator
*/
static {
VALUE_EVALS_MAP.put(BoolPtg.class, BoolEval.class);
VALUE_EVALS_MAP.put(IntPtg.class, NumberEval.class);
VALUE_EVALS_MAP.put(NumberPtg.class, NumberEval.class);
VALUE_EVALS_MAP.put(StringPtg.class, StringEval.class);
}
protected HSSFSheet _sheet;
protected HSSFWorkbook _workbook;
@ -287,7 +257,7 @@ public class HSSFFormulaEvaluator {
* Returns a CellValue wrapper around the supplied ValueEval instance.
* @param eval
*/
protected static CellValue getCellValueForEval(ValueEval eval) {
private static CellValue getCellValueForEval(ValueEval eval) {
CellValue retval = null;
if (eval != null) {
if (eval instanceof NumberEval) {
@ -369,7 +339,7 @@ public class HSSFFormulaEvaluator {
continue;
}
if (ptg instanceof UnknownPtg) { continue; }
Eval opResult;
if (ptg instanceof OperationPtg) {
OperationPtg optg = (OperationPtg) ptg;
@ -385,41 +355,11 @@ public class HSSFFormulaEvaluator {
Eval p = (Eval) stack.pop();
ops[j] = p;
}
Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
stack.push(opresult);
}
else if (ptg instanceof RefPtg) {
RefPtg refPtg = (RefPtg) ptg;
int colIx = refPtg.getColumn();
int rowIx = refPtg.getRow();
HSSFRow row = sheet.getRow(rowIx);
HSSFCell cell = (row != null) ? row.getCell(colIx) : null;
stack.push(createRef2DEval(refPtg, cell, sheet, workbook));
}
else if (ptg instanceof Ref3DPtg) {
Ref3DPtg refPtg = (Ref3DPtg) ptg;
int colIx = refPtg.getColumn();
int rowIx = refPtg.getRow();
Workbook wb = workbook.getWorkbook();
HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex()));
HSSFRow row = xsheet.getRow(rowIx);
HSSFCell cell = (row != null) ? row.getCell(colIx) : null;
stack.push(createRef3DEval(refPtg, cell, xsheet, workbook));
}
else if (ptg instanceof AreaPtg) {
AreaPtg ap = (AreaPtg) ptg;
AreaEval ae = evaluateAreaPtg(sheet, workbook, ap);
stack.push(ae);
}
else if (ptg instanceof Area3DPtg) {
Area3DPtg a3dp = (Area3DPtg) ptg;
AreaEval ae = evaluateArea3dPtg(workbook, a3dp);
stack.push(ae);
}
else {
Eval ptgEval = getEvalForPtg(ptg);
stack.push(ptgEval);
opResult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
} else {
opResult = getEvalForPtg(ptg, sheet, workbook);
}
stack.push(opResult);
}
ValueEval value = ((ValueEval) stack.pop());
@ -475,105 +415,49 @@ public class HSSFFormulaEvaluator {
return operation.evaluate(ops, srcRowNum, srcColNum);
}
public static AreaEval evaluateAreaPtg(HSSFSheet sheet, HSSFWorkbook workbook, AreaPtg ap) {
int row0 = ap.getFirstRow();
int col0 = ap.getFirstColumn();
int row1 = ap.getLastRow();
int col1 = ap.getLastColumn();
// If the last row is -1, then the
// reference is for the rest of the column
// (eg C:C)
// TODO: Handle whole column ranges properly
if(row1 == -1 && row0 >= 0) {
row1 = (short)sheet.getLastRowNum();
}
ValueEval[] values = evalArea(workbook, sheet, row0, col0, row1, col1);
return new Area2DEval(ap, values);
}
public static AreaEval evaluateArea3dPtg(HSSFWorkbook workbook, Area3DPtg a3dp) {
int row0 = a3dp.getFirstRow();
int col0 = a3dp.getFirstColumn();
int row1 = a3dp.getLastRow();
int col1 = a3dp.getLastColumn();
Workbook wb = workbook.getWorkbook();
HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex()));
// If the last row is -1, then the
// reference is for the rest of the column
// (eg C:C)
// TODO: Handle whole column ranges properly
if(row1 == -1 && row0 >= 0) {
row1 = (short)xsheet.getLastRowNum();
}
ValueEval[] values = evalArea(workbook, xsheet, row0, col0, row1, col1);
return new Area3DEval(a3dp, values);
}
private static ValueEval[] evalArea(HSSFWorkbook workbook, HSSFSheet sheet,
int row0, int col0, int row1, int col1) {
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
for (int x = row0; sheet != null && x < row1 + 1; x++) {
HSSFRow row = sheet.getRow(x);
for (int y = col0; y < col1 + 1; y++) {
ValueEval cellEval;
if(row == null) {
cellEval = BlankEval.INSTANCE;
} else {
cellEval = getEvalForCell(row.getCell(y), row, sheet, workbook);
}
values[(x - row0) * (col1 - col0 + 1) + (y - col0)] = cellEval;
}
}
return values;
}
/**
* returns an appropriate Eval impl instance for the Ptg. The Ptg must be
* one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,
* StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
* passed here!
*
* @param ptg
*/
protected static Eval getEvalForPtg(Ptg ptg) {
Eval retval = null;
Class clazz = (Class) VALUE_EVALS_MAP.get(ptg.getClass());
try {
private static Eval getEvalForPtg(Ptg ptg, HSSFSheet sheet, HSSFWorkbook workbook) {
if (ptg instanceof RefPtg) {
return new LazyRefEval(((RefPtg) ptg), sheet, workbook);
}
if (ptg instanceof Ref3DPtg) {
Ref3DPtg refPtg = (Ref3DPtg) ptg;
Workbook wb = workbook.getWorkbook();
HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex()));
return new LazyRefEval(refPtg, xsheet, workbook);
}
if (ptg instanceof AreaPtg) {
return new LazyAreaEval(((AreaPtg) ptg), sheet, workbook);
}
if (ptg instanceof Area3DPtg) {
Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
}
else if (ptg instanceof AreaPtg) {
Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
}
else if (ptg instanceof RefPtg) {
Constructor constructor = clazz.getConstructor(REFERENCE_CONSTRUCTOR_CLASS_ARRAY);
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
}
else if (ptg instanceof Ref3DPtg) {
Constructor constructor = clazz.getConstructor(REF3D_CONSTRUCTOR_CLASS_ARRAY);
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
}
else {
if (ptg instanceof IntPtg || ptg instanceof NumberPtg || ptg instanceof StringPtg
|| ptg instanceof BoolPtg) {
Constructor constructor = clazz.getConstructor(VALUE_CONTRUCTOR_CLASS_ARRAY);
retval = (ValueEval) constructor.newInstance(new Ptg[] { ptg });
}
}
}
catch (Exception e) {
throw new RuntimeException("Fatal Error: ", e);
}
return retval;
Area3DPtg a3dp = (Area3DPtg) ptg;
Workbook wb = workbook.getWorkbook();
HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex()));
return new LazyAreaEval(a3dp, xsheet, workbook);
}
if (ptg instanceof IntPtg) {
return new NumberEval(((IntPtg)ptg).getValue());
}
if (ptg instanceof NumberPtg) {
return new NumberEval(((NumberPtg)ptg).getValue());
}
if (ptg instanceof StringPtg) {
return new StringEval(((StringPtg) ptg).getValue());
}
if (ptg instanceof BoolPtg) {
return BoolEval.valueOf(((BoolPtg) ptg).getValue());
}
if (ptg instanceof ErrPtg) {
return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
}
throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")");
}
/**
* Given a cell, find its type and from that create an appropriate ValueEval
* impl instance and return that. Since the cell could be an external
@ -583,7 +467,7 @@ public class HSSFFormulaEvaluator {
* @param sheet
* @param workbook
*/
protected static ValueEval getEvalForCell(HSSFCell cell, HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
public static ValueEval getEvalForCell(HSSFCell cell, HSSFSheet sheet, HSSFWorkbook workbook) {
if (cell == null) {
return BlankEval.INSTANCE;
@ -605,58 +489,6 @@ public class HSSFFormulaEvaluator {
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
}
/**
* Creates a Ref2DEval for ReferencePtg.
* Non existent cells are treated as RefEvals containing BlankEval.
*/
private static Ref2DEval createRef2DEval(RefPtg ptg, HSSFCell cell,
HSSFSheet sheet, HSSFWorkbook workbook) {
if (cell == null) {
return new Ref2DEval(ptg, BlankEval.INSTANCE);
}
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_NUMERIC:
return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()));
case HSSFCell.CELL_TYPE_STRING:
return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
case HSSFCell.CELL_TYPE_FORMULA:
return new Ref2DEval(ptg, internalEvaluate(cell, sheet, workbook));
case HSSFCell.CELL_TYPE_BOOLEAN:
return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
case HSSFCell.CELL_TYPE_BLANK:
return new Ref2DEval(ptg, BlankEval.INSTANCE);
case HSSFCell.CELL_TYPE_ERROR:
return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
}
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
}
/**
* create a Ref3DEval for Ref3DPtg.
*/
private static Ref3DEval createRef3DEval(Ref3DPtg ptg, HSSFCell cell,
HSSFSheet sheet, HSSFWorkbook workbook) {
if (cell == null) {
return new Ref3DEval(ptg, BlankEval.INSTANCE);
}
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_NUMERIC:
return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()));
case HSSFCell.CELL_TYPE_STRING:
return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
case HSSFCell.CELL_TYPE_FORMULA:
return new Ref3DEval(ptg, internalEvaluate(cell, sheet, workbook));
case HSSFCell.CELL_TYPE_BOOLEAN:
return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
case HSSFCell.CELL_TYPE_BLANK:
return new Ref3DEval(ptg, BlankEval.INSTANCE);
case HSSFCell.CELL_TYPE_ERROR:
return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
}
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
}
/**
* Mimics the 'data view' of a cell. This allows formula evaluator
* to return a CellValue instead of precasting the value to String
@ -752,15 +584,9 @@ public class HSSFFormulaEvaluator {
/**
* debug method
*
* @param formula
* @param sheet
* @param workbook
*/
void inspectPtgs(String formula) {
FormulaParser fp = new FormulaParser(formula, _workbook);
fp.parse();
Ptg[] ptgs = fp.getRPNPtg();
Ptg[] ptgs = FormulaParser.parse(formula, _workbook);
System.out.println("<ptg-group>");
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
System.out.println("<ptg>");

View File

@ -20,7 +20,8 @@ package org.apache.poi.hssf.record.formula.eval;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.functions.EvalFactory;
/**
* Tests for <tt>AreaEval</tt>
@ -30,8 +31,8 @@ import org.apache.poi.hssf.record.formula.Area3DPtg;
public final class TestAreaEval extends TestCase {
public void testGetValue_bug44950() {
Area3DPtg ptg = new Area3DPtg("B2:D3", (short)0);
// TODO - this test probably isn't testing much anymore
AreaPtg ptg = new AreaPtg("B2:D3");
NumberEval one = new NumberEval(1);
ValueEval[] values = {
one,
@ -41,7 +42,7 @@ public final class TestAreaEval extends TestCase {
new NumberEval(5),
new NumberEval(6),
};
AreaEval ae = new Area3DEval(ptg, values);
AreaEval ae = EvalFactory.createAreaEval(ptg, values);
if (one == ae.getValueAt(1, 2)) {
throw new AssertionFailedError("Identified bug 44950 a");
}

View File

@ -18,12 +18,13 @@
package org.apache.poi.hssf.record.formula.eval;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
import org.apache.poi.hssf.record.formula.functions.EvalFactory;
import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker;
import junit.framework.TestCase;
/**
* Test for unary plus operator evaluator.
*
@ -48,9 +49,8 @@ public final class TestUnaryPlusEval extends TestCase {
new NumberEval(37),
new NumberEval(38),
};
Eval areaEval = new Area2DEval(areaPtg, values);
Eval[] args = {
areaEval,
EvalFactory.createAreaEval(areaPtg, values),
};
double result = NumericFunctionInvoker.invoke(new UnaryPlusEval(UnaryPlusPtg.instance), args, 10, (short)20);

View File

@ -15,16 +15,16 @@
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.AreaEvalBase;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.RefEvalBase;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
@ -32,7 +32,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
*
* @author Josh Micich
*/
final class EvalFactory {
public final class EvalFactory {
private EvalFactory() {
// no instances of this class
@ -44,6 +44,14 @@ final class EvalFactory {
*/
public static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
AreaPtg areaPtg = new AreaPtg(areaRefStr);
return createAreaEval(areaPtg, values);
}
/**
* Creates a dummy AreaEval
* @param values empty (<code>null</code>) entries in this array will be converted to NumberEval.ZERO
*/
public static AreaEval createAreaEval(AreaPtg areaPtg, ValueEval[] values) {
int nCols = areaPtg.getLastColumn() - areaPtg.getFirstColumn() + 1;
int nRows = areaPtg.getLastRow() - areaPtg.getFirstRow() + 1;
int nExpected = nRows * nCols;
@ -55,13 +63,57 @@ final class EvalFactory {
values[i] = NumberEval.ZERO;
}
}
return new Area2DEval(areaPtg, values);
return new MockAreaEval(areaPtg, values);
}
/**
* Creates a single RefEval (with value zero)
*/
public static RefEval createRefEval(String refStr) {
return new Ref2DEval(new RefPtg(refStr), NumberEval.ZERO);
return createRefEval(refStr, NumberEval.ZERO);
}
public static RefEval createRefEval(String refStr, ValueEval value) {
return new MockRefEval(new RefPtg(refStr), value);
}
private static final class MockAreaEval extends AreaEvalBase {
private final ValueEval[] _values;
public MockAreaEval(AreaPtg areaPtg, ValueEval[] values) {
super(areaPtg);
_values = values;
}
public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
if (relativeRowIndex < 0 || relativeRowIndex >=getHeight()) {
throw new IllegalArgumentException("row index out of range");
}
int width = getWidth();
if (relativeColumnIndex < 0 || relativeColumnIndex >=width) {
throw new IllegalArgumentException("column index out of range");
}
int oneDimensionalIndex = relativeRowIndex * width + relativeColumnIndex;
return _values[oneDimensionalIndex];
}
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
throw new RuntimeException("Operation not implemented on this mock object");
}
}
private static final class MockRefEval extends RefEvalBase {
private final ValueEval _value;
public MockRefEval(RefPtg ptg, ValueEval value) {
super(ptg.getRow(), ptg.getColumn());
_value = value;
}
public MockRefEval(Ref3DPtg ptg, ValueEval value) {
super(ptg.getRow(), ptg.getColumn());
_value = value;
}
public ValueEval getInnerValueEval() {
return _value;
}
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
throw new RuntimeException("Operation not implemented on this mock object");
}
}
}

View File

@ -21,9 +21,7 @@ import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
@ -91,7 +89,7 @@ public final class TestCountFuncs extends TestCase {
BoolEval.TRUE,
BlankEval.INSTANCE,
};
range = createAreaEval("A1:B3", values);
range = EvalFactory.createAreaEval("A1:B3", values);
confirmCountIf(2, range, BoolEval.TRUE);
// when criteria is numeric
@ -103,7 +101,7 @@ public final class TestCountFuncs extends TestCase {
new NumberEval(2),
BoolEval.TRUE,
};
range = createAreaEval("A1:B3", values);
range = EvalFactory.createAreaEval("A1:B3", values);
confirmCountIf(3, range, new NumberEval(2));
// note - same results when criteria is a string that parses as the number with the same value
confirmCountIf(3, range, new StringEval("2.00"));
@ -126,20 +124,15 @@ public final class TestCountFuncs extends TestCase {
new NumberEval(25),
new NumberEval(25),
};
Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values);
AreaEval arg0 = EvalFactory.createAreaEval("C1:C6", values);
Ref2DEval criteriaArg = new Ref2DEval(new RefPtg("A1"), new NumberEval(25));
ValueEval criteriaArg = EvalFactory.createRefEval("A1", new NumberEval(25));
Eval[] args= { arg0, criteriaArg, };
double actual = NumericFunctionInvoker.invoke(new Countif(), args);
assertEquals(4, actual, 0D);
}
private static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
return new Area2DEval(new AreaPtg(areaRefStr), values);
}
private static void confirmCountA(int expected, Eval[] args) {
double result = NumericFunctionInvoker.invoke(new Counta(), args);
assertEquals(expected, result, 0);

View File

@ -19,8 +19,7 @@ package org.apache.poi.hssf.record.formula.functions;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
@ -68,7 +67,7 @@ public final class TestIndex extends TestCase {
for (int i = 0; i < values.length; i++) {
values[i] = new NumberEval(dValues[i]);
}
Area2DEval arg0 = new Area2DEval(new AreaPtg(areaRefString), values);
AreaEval arg0 = EvalFactory.createAreaEval(areaRefString, values);
Eval[] args;
if (colNum > 0) {

View File

@ -19,8 +19,6 @@ package org.apache.poi.hssf.record.formula.functions;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
@ -54,13 +52,6 @@ public final class TestMatch extends TestCase {
NumericValueEval nve = (NumericValueEval)actualEval;
assertEquals(expected, nve.getNumberValue(), 0);
}
/**
* Convenience method
* @return <code>new Area2DEval(new AreaPtg(ref), values)</code>
*/
private static AreaEval createAreaEval(String ref, ValueEval[] values) {
return new Area2DEval(new AreaPtg(ref), values);
}
public void testSimpleNumber() {
@ -72,7 +63,7 @@ public final class TestMatch extends TestCase {
new NumberEval(25),
};
AreaEval ae = createAreaEval("A1:A5", values);
AreaEval ae = EvalFactory.createAreaEval("A1:A5", values);
confirmInt(2, invokeMatch(new NumberEval(5), ae, MATCH_LARGEST_LTE));
confirmInt(2, invokeMatch(new NumberEval(5), ae, MATCH_EXACT));
@ -92,7 +83,7 @@ public final class TestMatch extends TestCase {
new NumberEval(4),
};
AreaEval ae = createAreaEval("A1:A5", values);
AreaEval ae = EvalFactory.createAreaEval("A1:A5", values);
confirmInt(2, invokeMatch(new NumberEval(10), ae, MATCH_SMALLEST_GTE));
confirmInt(2, invokeMatch(new NumberEval(10), ae, MATCH_EXACT));
@ -112,7 +103,7 @@ public final class TestMatch extends TestCase {
new StringEval("Ian"),
};
AreaEval ae = createAreaEval("A1:A5", values);
AreaEval ae = EvalFactory.createAreaEval("A1:A5", values);
// Note String comparisons are case insensitive
confirmInt(3, invokeMatch(new StringEval("Ed"), ae, MATCH_LARGEST_LTE));
@ -132,7 +123,7 @@ public final class TestMatch extends TestCase {
BoolEval.TRUE,
};
AreaEval ae = createAreaEval("A1:A4", values);
AreaEval ae = EvalFactory.createAreaEval("A1:A4", values);
// Note String comparisons are case insensitive
confirmInt(2, invokeMatch(BoolEval.FALSE, ae, MATCH_LARGEST_LTE));
@ -159,7 +150,7 @@ public final class TestMatch extends TestCase {
new StringEval("Ed"),
};
AreaEval ae = createAreaEval("A1:A13", values);
AreaEval ae = EvalFactory.createAreaEval("A1:A13", values);
assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Aaron"), ae, MATCH_LARGEST_LTE));
@ -197,9 +188,9 @@ public final class TestMatch extends TestCase {
new NumberEval(25),
};
AreaEval ae = createAreaEval("A1:A5", values);
AreaEval ae = EvalFactory.createAreaEval("A1:A5", values);
AreaEval matchAE = createAreaEval("C1:C1", new ValueEval[] { MATCH_LARGEST_LTE, });
AreaEval matchAE = EvalFactory.createAreaEval("C1:C1", new ValueEval[] { MATCH_LARGEST_LTE, });
try {
confirmInt(4, invokeMatch(new NumberEval(10), ae, matchAE));

View File

@ -20,6 +20,8 @@
*/
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.functions.XYNumericFunction.Accumulator;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
@ -566,97 +568,97 @@ public class TestMathX extends AbstractNumericTestCase {
}
public void testSumx2my2() {
double d = 0;
double[] xarr = null;
double[] yarr = null;
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumx2my2(xarr, yarr);
assertEquals("sumx2my2 ", 100, d);
confirmSumx2my2(xarr, yarr, 100);
xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumx2my2(xarr, yarr);
assertEquals("sumx2my2 ", 100, d);
confirmSumx2my2(xarr, yarr, 100);
xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumx2my2(xarr, yarr);
assertEquals("sumx2my2 ", -100, d);
confirmSumx2my2(xarr, yarr, -100);
xarr = new double[]{10};
yarr = new double[]{9};
d = MathX.sumx2my2(xarr, yarr);
assertEquals("sumx2my2 ", 19, d);
confirmSumx2my2(xarr, yarr, 19);
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumx2my2(xarr, yarr);
assertEquals("sumx2my2 ", 0, d);
confirmSumx2my2(xarr, yarr, 0);
}
public void testSumx2py2() {
double d = 0;
double[] xarr = null;
double[] yarr = null;
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumx2py2(xarr, yarr);
assertEquals("sumx2py2 ", 670, d);
confirmSumx2py2(xarr, yarr, 670);
xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumx2py2(xarr, yarr);
assertEquals("sumx2py2 ", 670, d);
confirmSumx2py2(xarr, yarr, 670);
xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumx2py2(xarr, yarr);
assertEquals("sumx2py2 ", 670, d);
confirmSumx2py2(xarr, yarr, 670);
xarr = new double[]{10};
yarr = new double[]{9};
d = MathX.sumx2py2(xarr, yarr);
assertEquals("sumx2py2 ", 181, d);
confirmSumx2py2(xarr, yarr, 181);
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumx2py2(xarr, yarr);
assertEquals("sumx2py2 ", 770, d);
confirmSumx2py2(xarr, yarr, 770);
}
public void testSumxmy2() {
double d = 0;
double[] xarr = null;
double[] yarr = null;
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumxmy2(xarr, yarr);
assertEquals("sumxmy2 ", 10, d);
confirmSumxmy2(xarr, yarr, 10);
xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumxmy2(xarr, yarr);
assertEquals("sumxmy2 ", 1330, d);
confirmSumxmy2(xarr, yarr, 1330);
xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumxmy2(xarr, yarr);
assertEquals("sumxmy2 ", 10, d);
confirmSumxmy2(xarr, yarr, 10);
xarr = new double[]{10};
yarr = new double[]{9};
d = MathX.sumxmy2(xarr, yarr);
assertEquals("sumxmy2 ", 1, d);
confirmSumxmy2(xarr, yarr, 1);
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumxmy2(xarr, yarr);
assertEquals("sumxmy2 ", 0, d);
confirmSumxmy2(xarr, yarr, 0);
}
private static void confirmSumx2my2(double[] xarr, double[] yarr, double expectedResult) {
confirmXY(new Sumx2my2().createAccumulator(), xarr, yarr, expectedResult);
}
private static void confirmSumx2py2(double[] xarr, double[] yarr, double expectedResult) {
confirmXY(new Sumx2py2().createAccumulator(), xarr, yarr, expectedResult);
}
private static void confirmSumxmy2(double[] xarr, double[] yarr, double expectedResult) {
confirmXY(new Sumxmy2().createAccumulator(), xarr, yarr, expectedResult);
}
private static void confirmXY(Accumulator acc, double[] xarr, double[] yarr,
double expectedResult) {
double result = 0.0;
for (int i = 0; i < xarr.length; i++) {
result += acc.accumulate(xarr[i], yarr[i]);
}
assertEquals(expectedResult, result, 0.0);
}
public void testRound() {

View File

@ -17,21 +17,17 @@
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
import junit.framework.TestCase;
/**
* Tests for Excel function MID()
*
@ -76,8 +72,8 @@ public final class TestMid extends TestCase {
confirmMid(new NumberEval(123456), new StringEval("3.1"), new StringEval("2.9"), "34");
// startPos is 1x1 area ref, numChars is cell ref
AreaEval aeStart = new Area2DEval(new AreaPtg("A1:A1"), new ValueEval[] { new NumberEval(2), } );
RefEval reNumChars = new Ref2DEval(new RefPtg("B1"), new NumberEval(3));
AreaEval aeStart = EvalFactory.createAreaEval("A1:A1", new ValueEval[] { new NumberEval(2), } );
RefEval reNumChars = EvalFactory.createRefEval("B1", new NumberEval(3));
confirmMid(new StringEval("galactic"), aeStart, reNumChars, "ala");
confirmMid(new StringEval("galactic"), new NumberEval(3.1), BlankEval.INSTANCE, "");

View File

@ -17,18 +17,16 @@
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.RefPtg;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
import junit.framework.TestCase;
/**
* Test cases for SUMPRODUCT()
*
@ -50,7 +48,7 @@ public final class TestSumproduct extends TestCase {
public void testScalarSimple() {
RefEval refEval = new Ref2DEval(new RefPtg("A1"), new NumberEval(3));
RefEval refEval = EvalFactory.createRefEval("A1", new NumberEval(3));
Eval[] args = {
refEval,
new NumberEval(2),

View File

@ -17,18 +17,16 @@
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.RefPtg;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
import junit.framework.TestCase;
/**
* Test cases for Excel function T()
*
@ -50,7 +48,7 @@ public final class TestTFunc extends TestCase {
* where cell A1 has the specified innerValue
*/
private Eval invokeTWithReference(ValueEval innerValue) {
Eval arg = new Ref2DEval(new RefPtg((short)1, (short)1, false, false), innerValue);
Eval arg = EvalFactory.createRefEval("$B$2", innerValue);
return invokeT(arg);
}

View File

@ -19,8 +19,6 @@ package org.apache.poi.hssf.record.formula.functions;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
@ -100,7 +98,7 @@ public final class TestXYNumericFunction extends TestCase {
private static ValueEval createAreaEval(ValueEval[] values) {
String refStr = "A1:A" + values.length;
return new Area2DEval(new AreaPtg(refStr), values);
return EvalFactory.createAreaEval(refStr, values);
}
public void testErrors() {