Patch from Josh from bug #44371 - support for OFFSET function, and various tweaks to the formula evaluator to support this
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@629821 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
acd787dcb8
commit
173f63cd0a
@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
<!-- Don't forget to update status.xml too! -->
|
<!-- Don't forget to update status.xml too! -->
|
||||||
<release version="3.1-beta1" date="2008-??-??">
|
<release version="3.1-beta1" date="2008-??-??">
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">44371 - Support for the Offset function</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">38921 - Have HSSFPalette.findSimilar() work properly</action>
|
<action dev="POI-DEVELOPERS" type="fix">38921 - Have HSSFPalette.findSimilar() work properly</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">44456 - Fix the contrib SViewer / SViewerPanel to not fail on sheets with missing rows</action>
|
<action dev="POI-DEVELOPERS" type="fix">44456 - Fix the contrib SViewer / SViewerPanel to not fail on sheets with missing rows</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">44403 - Further support for unusual, but valid, arguments to the Mid function</action>
|
<action dev="POI-DEVELOPERS" type="fix">44403 - Further support for unusual, but valid, arguments to the Mid function</action>
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
<!-- Don't forget to update changes.xml too! -->
|
<!-- Don't forget to update changes.xml too! -->
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.1-beta1" date="2008-??-??">
|
<release version="3.1-beta1" date="2008-??-??">
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">44371 - Support for the Offset function</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">38921 - Have HSSFPalette.findSimilar() work properly</action>
|
<action dev="POI-DEVELOPERS" type="fix">38921 - Have HSSFPalette.findSimilar() work properly</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">44456 - Fix the contrib SViewer / SViewerPanel to not fail on sheets with missing rows</action>
|
<action dev="POI-DEVELOPERS" type="fix">44456 - Fix the contrib SViewer / SViewerPanel to not fail on sheets with missing rows</action>
|
||||||
<action dev="POI-DEVELOPERS" type="fix">44403 - Further support for unusual, but valid, arguments to the Mid function</action>
|
<action dev="POI-DEVELOPERS" type="fix">44403 - Further support for unusual, but valid, arguments to the Mid function</action>
|
||||||
|
@ -70,7 +70,21 @@ public class Area3DPtg extends Ptg implements AreaI
|
|||||||
field_5_last_column = in.readShort();
|
field_5_last_column = in.readShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public Area3DPtg(short firstRow, short lastRow, short firstColumn, short lastColumn,
|
||||||
|
boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative,
|
||||||
|
short externalSheetIndex) {
|
||||||
|
setFirstRow(firstRow);
|
||||||
|
setLastRow(lastRow);
|
||||||
|
setFirstColumn(firstColumn);
|
||||||
|
setLastColumn(lastColumn);
|
||||||
|
setFirstRowRelative(firstRowRelative);
|
||||||
|
setLastRowRelative(lastRowRelative);
|
||||||
|
setFirstColRelative(firstColRelative);
|
||||||
|
setLastColRelative(lastColRelative);
|
||||||
|
setExternSheetIndex(externalSheetIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString()
|
||||||
{
|
{
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ import org.apache.poi.hssf.record.formula.Ptg;
|
|||||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Area3DEval implements AreaEval {
|
public final class Area3DEval implements AreaEval {
|
||||||
|
|
||||||
private Area3DPtg delegate;
|
private Area3DPtg delegate;
|
||||||
|
|
||||||
@ -90,4 +90,8 @@ public class Area3DEval implements AreaEval {
|
|||||||
return delegate.getFirstRow() == delegate.getLastRow();
|
return delegate.getFirstRow() == delegate.getLastRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getExternSheetIndex() {
|
||||||
|
return delegate.getExternSheetIndex();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.poi.hssf.record.formula.eval;
|
package org.apache.poi.hssf.record.formula.eval;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.poi.hssf.record.formula.functions.*;
|
import org.apache.poi.hssf.record.formula.functions.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,12 +30,47 @@ import org.apache.poi.hssf.record.formula.functions.*;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class FunctionEval implements OperationEval {
|
public abstract class FunctionEval implements OperationEval {
|
||||||
|
/**
|
||||||
|
* Some function IDs that require special treatment
|
||||||
|
*/
|
||||||
|
private static final class FunctionID {
|
||||||
|
/** 78 */
|
||||||
|
public static final int OFFSET = 78;
|
||||||
|
/** 148 */
|
||||||
|
public static final int INDIRECT = 148;
|
||||||
|
|
||||||
|
}
|
||||||
|
// convenient access to namespace
|
||||||
|
private static final FunctionID ID = null;
|
||||||
|
|
||||||
protected static Function[] functions = produceFunctions();
|
protected static Function[] functions = produceFunctions();
|
||||||
|
|
||||||
|
private static Map freeRefFunctionsByIdMap;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Map m = new HashMap();
|
||||||
|
addMapping(m, ID.OFFSET, new Offset());
|
||||||
|
addMapping(m, ID.INDIRECT, new Indirect());
|
||||||
|
freeRefFunctionsByIdMap = m;
|
||||||
|
}
|
||||||
|
private static void addMapping(Map m, int offset, FreeRefFunction frf) {
|
||||||
|
m.put(createFRFKey(offset), frf);
|
||||||
|
}
|
||||||
|
private static Integer createFRFKey(int functionIndex) {
|
||||||
|
return new Integer(functionIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public Function getFunction() {
|
public Function getFunction() {
|
||||||
short fidx = getFunctionIndex();
|
short fidx = getFunctionIndex();
|
||||||
return functions[fidx];
|
return functions[fidx];
|
||||||
}
|
}
|
||||||
|
public boolean isFreeRefFunction() {
|
||||||
|
return freeRefFunctionsByIdMap.containsKey(createFRFKey(getFunctionIndex()));
|
||||||
|
}
|
||||||
|
public FreeRefFunction getFreeRefFunction() {
|
||||||
|
return (FreeRefFunction) freeRefFunctionsByIdMap.get(createFRFKey(getFunctionIndex()));
|
||||||
|
}
|
||||||
|
|
||||||
public abstract short getFunctionIndex();
|
public abstract short getFunctionIndex();
|
||||||
|
|
||||||
@ -115,7 +153,7 @@ public abstract class FunctionEval implements OperationEval {
|
|||||||
retval[75] = new Areas(); // AREAS
|
retval[75] = new Areas(); // AREAS
|
||||||
retval[76] = new Rows(); // ROWS
|
retval[76] = new Rows(); // ROWS
|
||||||
retval[77] = new Columns(); // COLUMNS
|
retval[77] = new Columns(); // COLUMNS
|
||||||
retval[78] = new Offset(); // OFFSET
|
retval[ID.OFFSET] = null; // Offset.evaluate has a different signature
|
||||||
retval[79] = new Absref(); // ABSREF
|
retval[79] = new Absref(); // ABSREF
|
||||||
retval[80] = new Relref(); // RELREF
|
retval[80] = new Relref(); // RELREF
|
||||||
retval[81] = new Argument(); // ARGUMENT
|
retval[81] = new Argument(); // ARGUMENT
|
||||||
@ -185,7 +223,7 @@ public abstract class FunctionEval implements OperationEval {
|
|||||||
retval[145] = new NotImplementedFunction(); // GETDEF
|
retval[145] = new NotImplementedFunction(); // GETDEF
|
||||||
retval[146] = new Reftext(); // REFTEXT
|
retval[146] = new Reftext(); // REFTEXT
|
||||||
retval[147] = new Textref(); // TEXTREF
|
retval[147] = new Textref(); // TEXTREF
|
||||||
retval[148] = new Indirect(); // INDIRECT
|
retval[ID.INDIRECT] = null; // Indirect.evaluate has different signature
|
||||||
retval[149] = new NotImplementedFunction(); // REGISTER
|
retval[149] = new NotImplementedFunction(); // REGISTER
|
||||||
retval[150] = new Call(); // CALL
|
retval[150] = new Call(); // CALL
|
||||||
retval[151] = new NotImplementedFunction(); // ADDBAR
|
retval[151] = new NotImplementedFunction(); // ADDBAR
|
||||||
|
@ -27,7 +27,7 @@ import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
|||||||
* @author Amol S. Deshmukh
|
* @author Amol S. Deshmukh
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Ref3DEval implements RefEval {
|
public final class Ref3DEval implements RefEval {
|
||||||
|
|
||||||
private ValueEval value;
|
private ValueEval value;
|
||||||
|
|
||||||
@ -57,4 +57,8 @@ public class Ref3DEval implements RefEval {
|
|||||||
return evaluated;
|
return evaluated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getExternSheetIndex() {
|
||||||
|
return delegate.getExternSheetIndex();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
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.Eval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For most Excel functions, involving references ((cell, area), (2d, 3d)), the references are
|
||||||
|
* passed in as arguments, and the exact location remains fixed. However, a select few Excel
|
||||||
|
* functions have the ability to access cells that were not part of any reference passed as an
|
||||||
|
* argument.<br/>
|
||||||
|
* Two important functions with this feature are <b>INDIRECT</b> and <b>OFFSET</b><p/>
|
||||||
|
*
|
||||||
|
* In POI, the <tt>HSSFFormulaEvaluator</tt> evaluates every cell in each reference argument before
|
||||||
|
* calling the function. This means that functions using fixed references do not need access to
|
||||||
|
* the rest of the workbook to execute. Hence the <tt>evaluate()</tt> method on the common
|
||||||
|
* interface <tt>Function</tt> does not take a workbook parameter.<p>
|
||||||
|
*
|
||||||
|
* This interface recognises the requirement of some functions to freely create and evaluate
|
||||||
|
* references beyond those passed in as arguments.
|
||||||
|
*
|
||||||
|
* @author Josh Micich
|
||||||
|
*/
|
||||||
|
public interface FreeRefFunction {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param args the pre-evaluated arguments for this function. args is never <code>null</code>,
|
||||||
|
* nor are any of its elements.
|
||||||
|
* @param srcCellRow zero based row index of the cell containing the currently evaluating formula
|
||||||
|
* @param srcCellCol zero based column index of the cell containing the currently evaluating formula
|
||||||
|
* @param workbook is the workbook containing the formula/cell being evaluated
|
||||||
|
* @param sheet is the sheet containing the formula/cell being evaluated
|
||||||
|
* @return never <code>null</code>. Possibly an instance of <tt>ErrorEval</tt> in the case of
|
||||||
|
* a specified Excel error (Exceptions are never thrown to represent Excel errors).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet);
|
||||||
|
}
|
@ -14,12 +14,36 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
* Created on May 15, 2005
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package org.apache.poi.hssf.record.formula.functions;
|
package org.apache.poi.hssf.record.formula.functions;
|
||||||
|
|
||||||
public class Indirect extends NotImplementedFunction {
|
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.ValueEval;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for Excel function INDIRECT<p/>
|
||||||
|
*
|
||||||
|
* INDIRECT() returns the cell or area reference denoted by the text argument.<p/>
|
||||||
|
*
|
||||||
|
* <b>Syntax</b>:</br>
|
||||||
|
* <b>INDIRECT</b>(<b>ref_text</b>,isA1Style)<p/>
|
||||||
|
*
|
||||||
|
* <b>ref_text</b> a string representation of the desired reference as it would normally be written
|
||||||
|
* in a cell formula.<br/>
|
||||||
|
* <b>isA1Style</b> (default TRUE) specifies whether the ref_text should be interpreted as A1-style
|
||||||
|
* or R1C1-style.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Josh Micich
|
||||||
|
*/
|
||||||
|
public final class Indirect implements FreeRefFunction {
|
||||||
|
|
||||||
|
public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet) {
|
||||||
|
// TODO - implement INDIRECT()
|
||||||
|
return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,349 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
* Created on May 15, 2005
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package org.apache.poi.hssf.record.formula.functions;
|
package org.apache.poi.hssf.record.formula.functions;
|
||||||
|
|
||||||
public class Offset extends NotImplementedFunction {
|
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;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
|
||||||
|
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/>
|
||||||
|
*
|
||||||
|
* OFFSET returns an area reference that is a specified number of rows and columns from a
|
||||||
|
* reference cell or area.<p/>
|
||||||
|
*
|
||||||
|
* <b>Syntax</b>:<br/>
|
||||||
|
* <b>OFFSET</b>(<b>reference</b>, <b>rows</b>, <b>cols</b>, height, width)<p/>
|
||||||
|
* <b>reference</b> is the base reference.<br/>
|
||||||
|
* <b>rows</b> is the number of rows up or down from the base reference.<br/>
|
||||||
|
* <b>cols</b> is the number of columns left or right from the base reference.<br/>
|
||||||
|
* <b>height</b> (default same height as base reference) is the row count for the returned area reference.<br/>
|
||||||
|
* <b>width</b> (default same width as base reference) is the column count for the returned area reference.<br/>
|
||||||
|
*
|
||||||
|
* @author Josh Micich
|
||||||
|
*/
|
||||||
|
public final class Offset implements FreeRefFunction {
|
||||||
|
// These values are specific to BIFF8
|
||||||
|
private static final int LAST_VALID_ROW_INDEX = 0xFFFF;
|
||||||
|
private static final int LAST_VALID_COLUMN_INDEX = 0xFF;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exceptions are used within this class to help simplify flow control when error conditions
|
||||||
|
* are encountered
|
||||||
|
*/
|
||||||
|
private static final class EvalEx extends Exception {
|
||||||
|
private final ErrorEval _error;
|
||||||
|
|
||||||
|
public EvalEx(ErrorEval error) {
|
||||||
|
_error = error;
|
||||||
|
}
|
||||||
|
public ErrorEval getError() {
|
||||||
|
return _error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A one dimensional base + offset. Represents either a row range or a column range.
|
||||||
|
* Two instances of this class together specify an area range.
|
||||||
|
*/
|
||||||
|
/* package */ static final class LinearOffsetRange {
|
||||||
|
|
||||||
|
private final int _offset;
|
||||||
|
private final int _length;
|
||||||
|
|
||||||
|
public LinearOffsetRange(int offset, int length) {
|
||||||
|
if(length == 0) {
|
||||||
|
// handled that condition much earlier
|
||||||
|
throw new RuntimeException("length may not be zero");
|
||||||
|
}
|
||||||
|
_offset = offset;
|
||||||
|
_length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getFirstIndex() {
|
||||||
|
return (short) _offset;
|
||||||
|
}
|
||||||
|
public short getLastIndex() {
|
||||||
|
return (short) (_offset + _length - 1);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Moves the range by the specified translation amount.<p/>
|
||||||
|
*
|
||||||
|
* This method also 'normalises' the range: Excel specifies that the width and height
|
||||||
|
* parameters (length field here) cannot be negative. However, OFFSET() does produce
|
||||||
|
* sensible results in these cases. That behavior is replicated here. <p/>
|
||||||
|
*
|
||||||
|
* @param translationAmount may be zero negative or positive
|
||||||
|
*
|
||||||
|
* @return the equivalent <tt>LinearOffsetRange</tt> with a positive length, moved by the
|
||||||
|
* specified translationAmount.
|
||||||
|
*/
|
||||||
|
public LinearOffsetRange normaliseAndTranslate(int translationAmount) {
|
||||||
|
if (_length > 0) {
|
||||||
|
if(translationAmount == 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new LinearOffsetRange(translationAmount + _offset, _length);
|
||||||
|
}
|
||||||
|
return new LinearOffsetRange(translationAmount + _offset + _length + 1, -_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOutOfBounds(int lowValidIx, int highValidIx) {
|
||||||
|
if(_offset < lowValidIx) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(getLastIndex() > highValidIx) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer sb = new StringBuffer(64);
|
||||||
|
sb.append(getClass().getName()).append(" [");
|
||||||
|
sb.append(_offset).append("...").append(getLastIndex());
|
||||||
|
sb.append("]");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public BaseRef(RefEval re) {
|
||||||
|
_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) {
|
||||||
|
_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() {
|
||||||
|
return _width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFirstRowIndex() {
|
||||||
|
return _firstRowIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
return (short) _externalSheetIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet) {
|
||||||
|
|
||||||
|
if(args.length < 3 || args.length > 5) {
|
||||||
|
return ErrorEval.VALUE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
BaseRef baseRef = evaluateBaseRef(args[0]);
|
||||||
|
int rowOffset = evaluateIntArg(args[1], srcCellRow, srcCellCol);
|
||||||
|
int columnOffset = evaluateIntArg(args[2], srcCellRow, srcCellCol);
|
||||||
|
int height = baseRef.getHeight();
|
||||||
|
int width = baseRef.getWidth();
|
||||||
|
switch(args.length) {
|
||||||
|
case 5:
|
||||||
|
width = evaluateIntArg(args[4], srcCellRow, srcCellCol);
|
||||||
|
case 4:
|
||||||
|
height = evaluateIntArg(args[3], srcCellRow, srcCellCol);
|
||||||
|
}
|
||||||
|
// Zero height or width raises #REF! error
|
||||||
|
if(height == 0 || width == 0) {
|
||||||
|
return ErrorEval.REF_INVALID;
|
||||||
|
}
|
||||||
|
LinearOffsetRange rowOffsetRange = new LinearOffsetRange(rowOffset, height);
|
||||||
|
LinearOffsetRange colOffsetRange = new LinearOffsetRange(columnOffset, width);
|
||||||
|
return createOffset(baseRef, rowOffsetRange, colOffsetRange, workbook, sheet);
|
||||||
|
} catch (EvalEx e) {
|
||||||
|
return e.getError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static AreaEval createOffset(BaseRef baseRef,
|
||||||
|
LinearOffsetRange rowOffsetRange, LinearOffsetRange colOffsetRange,
|
||||||
|
HSSFWorkbook workbook, HSSFSheet sheet) throws EvalEx {
|
||||||
|
|
||||||
|
LinearOffsetRange rows = rowOffsetRange.normaliseAndTranslate(baseRef.getFirstRowIndex());
|
||||||
|
LinearOffsetRange cols = colOffsetRange.normaliseAndTranslate(baseRef.getFirstColumnIndex());
|
||||||
|
|
||||||
|
if(rows.isOutOfBounds(0, LAST_VALID_ROW_INDEX)) {
|
||||||
|
throw new EvalEx(ErrorEval.REF_INVALID);
|
||||||
|
}
|
||||||
|
if(cols.isOutOfBounds(0, LAST_VALID_COLUMN_INDEX)) {
|
||||||
|
throw new EvalEx(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static BaseRef evaluateBaseRef(Eval eval) throws EvalEx {
|
||||||
|
|
||||||
|
if(eval instanceof RefEval) {
|
||||||
|
return new BaseRef((RefEval)eval);
|
||||||
|
}
|
||||||
|
if(eval instanceof AreaEval) {
|
||||||
|
return new BaseRef((AreaEval)eval);
|
||||||
|
}
|
||||||
|
if (eval instanceof ErrorEval) {
|
||||||
|
throw new EvalEx((ErrorEval) eval);
|
||||||
|
}
|
||||||
|
throw new EvalEx(ErrorEval.VALUE_INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OFFSET's numeric arguments (2..5) have similar processing rules
|
||||||
|
*/
|
||||||
|
private static int evaluateIntArg(Eval eval, int srcCellRow, short srcCellCol) throws EvalEx {
|
||||||
|
|
||||||
|
double d = evaluateDoubleArg(eval, srcCellRow, srcCellCol);
|
||||||
|
return convertDoubleToInt(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fractional values are silently truncated by Excel.
|
||||||
|
* Truncation is toward negative infinity.
|
||||||
|
*/
|
||||||
|
/* package */ static int convertDoubleToInt(double d) {
|
||||||
|
// Note - the standard java type conversion from double to int truncates toward zero.
|
||||||
|
// but Math.floor() truncates toward negative infinity
|
||||||
|
return (int)Math.floor(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static double evaluateDoubleArg(Eval eval, int srcCellRow, short srcCellCol) throws EvalEx {
|
||||||
|
ValueEval ve = evaluateSingleValue(eval, srcCellRow, srcCellCol);
|
||||||
|
|
||||||
|
if (ve instanceof NumericValueEval) {
|
||||||
|
return ((NumericValueEval) ve).getNumberValue();
|
||||||
|
}
|
||||||
|
if (ve instanceof StringEval) {
|
||||||
|
StringEval se = (StringEval) ve;
|
||||||
|
Double d = parseDouble(se.getStringValue());
|
||||||
|
if(d == null) {
|
||||||
|
throw new EvalEx(ErrorEval.VALUE_INVALID);
|
||||||
|
}
|
||||||
|
return d.doubleValue();
|
||||||
|
}
|
||||||
|
if (ve instanceof BoolEval) {
|
||||||
|
// in the context of OFFSET, booleans resolve to 0 and 1.
|
||||||
|
if(((BoolEval) ve).getBooleanValue()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unexpected eval type (" + ve.getClass().getName() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Double parseDouble(String s) {
|
||||||
|
// TODO - find a home for this method
|
||||||
|
// TODO - support various number formats: sign char, dollars, commas
|
||||||
|
// OFFSET and COUNTIF seem to handle these
|
||||||
|
return Countif.parseDouble(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ValueEval evaluateSingleValue(Eval eval, int srcCellRow, short srcCellCol) throws EvalEx {
|
||||||
|
if(eval instanceof RefEval) {
|
||||||
|
return ((RefEval)eval).getInnerValueEval();
|
||||||
|
}
|
||||||
|
if(eval instanceof AreaEval) {
|
||||||
|
return chooseSingleElementFromArea((AreaEval)eval, srcCellRow, srcCellCol);
|
||||||
|
}
|
||||||
|
if (eval instanceof ValueEval) {
|
||||||
|
return (ValueEval) eval;
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unexpected eval type (" + eval.getClass().getName() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - this code seems to get repeated a bit
|
||||||
|
private static ValueEval chooseSingleElementFromArea(AreaEval ae, int srcCellRow, short srcCellCol) throws EvalEx {
|
||||||
|
if (ae.isColumn()) {
|
||||||
|
if (ae.isRow()) {
|
||||||
|
return ae.getValues()[0];
|
||||||
|
}
|
||||||
|
if (!ae.containsRow(srcCellRow)) {
|
||||||
|
throw new EvalEx(ErrorEval.VALUE_INVALID);
|
||||||
|
}
|
||||||
|
return ae.getValueAt(srcCellRow, ae.getFirstColumn());
|
||||||
|
}
|
||||||
|
if (!ae.isRow()) {
|
||||||
|
throw new EvalEx(ErrorEval.VALUE_INVALID);
|
||||||
|
}
|
||||||
|
if (!ae.containsColumn(srcCellCol)) {
|
||||||
|
throw new EvalEx(ErrorEval.VALUE_INVALID);
|
||||||
|
}
|
||||||
|
return ae.getValueAt(ae.getFirstRow(), srcCellCol);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
|||||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation for Excel COLUMNS function.
|
* Implementation for Excel ROWS function.
|
||||||
*
|
*
|
||||||
* @author Josh Micich
|
* @author Josh Micich
|
||||||
*/
|
*/
|
||||||
|
@ -74,6 +74,7 @@ import org.apache.poi.hssf.record.formula.eval.EqualEval;
|
|||||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
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.Eval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.FuncVarEval;
|
import org.apache.poi.hssf.record.formula.eval.FuncVarEval;
|
||||||
|
import org.apache.poi.hssf.record.formula.eval.FunctionEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.GreaterEqualEval;
|
import org.apache.poi.hssf.record.formula.eval.GreaterEqualEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.GreaterThanEval;
|
import org.apache.poi.hssf.record.formula.eval.GreaterThanEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.LessEqualEval;
|
import org.apache.poi.hssf.record.formula.eval.LessEqualEval;
|
||||||
@ -91,7 +92,6 @@ import org.apache.poi.hssf.record.formula.eval.SubtractEval;
|
|||||||
import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval;
|
import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
|
import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
|
||||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||||
@ -379,7 +379,7 @@ public class HSSFFormulaEvaluator {
|
|||||||
Stack stack = new Stack();
|
Stack stack = new Stack();
|
||||||
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
|
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
|
||||||
|
|
||||||
// since we dont know how to handle these yet :(
|
// since we don't know how to handle these yet :(
|
||||||
if (ptgs[i] instanceof ControlPtg) { continue; }
|
if (ptgs[i] instanceof ControlPtg) { continue; }
|
||||||
if (ptgs[i] instanceof MemErrPtg) { continue; }
|
if (ptgs[i] instanceof MemErrPtg) { continue; }
|
||||||
if (ptgs[i] instanceof MissingArgPtg) { continue; }
|
if (ptgs[i] instanceof MissingArgPtg) { continue; }
|
||||||
@ -405,7 +405,7 @@ public class HSSFFormulaEvaluator {
|
|||||||
Eval p = (Eval) stack.pop();
|
Eval p = (Eval) stack.pop();
|
||||||
ops[j] = p;
|
ops[j] = p;
|
||||||
}
|
}
|
||||||
Eval opresult = operation.evaluate(ops, srcRowNum, srcColNum);
|
Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
|
||||||
stack.push(opresult);
|
stack.push(opresult);
|
||||||
}
|
}
|
||||||
else if (ptgs[i] instanceof ReferencePtg) {
|
else if (ptgs[i] instanceof ReferencePtg) {
|
||||||
@ -428,56 +428,12 @@ public class HSSFFormulaEvaluator {
|
|||||||
}
|
}
|
||||||
else if (ptgs[i] instanceof AreaPtg) {
|
else if (ptgs[i] instanceof AreaPtg) {
|
||||||
AreaPtg ap = (AreaPtg) ptgs[i];
|
AreaPtg ap = (AreaPtg) ptgs[i];
|
||||||
short row0 = ap.getFirstRow();
|
AreaEval ae = evaluateAreaPtg(sheet, workbook, ap);
|
||||||
short col0 = ap.getFirstColumn();
|
|
||||||
short row1 = ap.getLastRow();
|
|
||||||
short 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 = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
|
|
||||||
for (short x = row0; sheet != null && x < row1 + 1; x++) {
|
|
||||||
HSSFRow row = sheet.getRow(x);
|
|
||||||
for (short y = col0; row != null && y < col1 + 1; y++) {
|
|
||||||
values[(x - row0) * (col1 - col0 + 1) + (y - col0)] =
|
|
||||||
getEvalForCell(row.getCell(y), row, sheet, workbook);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AreaEval ae = new Area2DEval(ap, values);
|
|
||||||
stack.push(ae);
|
stack.push(ae);
|
||||||
}
|
}
|
||||||
else if (ptgs[i] instanceof Area3DPtg) {
|
else if (ptgs[i] instanceof Area3DPtg) {
|
||||||
Area3DPtg a3dp = (Area3DPtg) ptgs[i];
|
Area3DPtg a3dp = (Area3DPtg) ptgs[i];
|
||||||
short row0 = a3dp.getFirstRow();
|
AreaEval ae = evaluateArea3dPtg(workbook, a3dp);
|
||||||
short col0 = a3dp.getFirstColumn();
|
|
||||||
short row1 = a3dp.getLastRow();
|
|
||||||
short 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 = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
|
|
||||||
for (short x = row0; xsheet != null && x < row1 + 1; x++) {
|
|
||||||
HSSFRow row = xsheet.getRow(x);
|
|
||||||
for (short y = col0; row != null && y < col1 + 1; y++) {
|
|
||||||
values[(x - row0) * (col1 - col0 + 1) + (y - col0)] =
|
|
||||||
getEvalForCell(row.getCell(y), row, xsheet, workbook);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AreaEval ae = new Area3DEval(a3dp, values);
|
|
||||||
stack.push(ae);
|
stack.push(ae);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -485,23 +441,95 @@ public class HSSFFormulaEvaluator {
|
|||||||
stack.push(ptgEval);
|
stack.push(ptgEval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueEval value = ((ValueEval) stack.pop());
|
ValueEval value = ((ValueEval) stack.pop());
|
||||||
if (value instanceof RefEval) {
|
if (value instanceof RefEval) {
|
||||||
RefEval rv = (RefEval) value;
|
RefEval rv = (RefEval) value;
|
||||||
value = rv.getInnerValueEval();
|
return rv.getInnerValueEval();
|
||||||
}
|
}
|
||||||
else if (value instanceof AreaEval) {
|
if (value instanceof AreaEval) {
|
||||||
AreaEval ae = (AreaEval) value;
|
AreaEval ae = (AreaEval) value;
|
||||||
if (ae.isRow())
|
if (ae.isRow()) {
|
||||||
value = ae.getValueAt(ae.getFirstRow(), srcColNum);
|
if(ae.isColumn()) {
|
||||||
else if (ae.isColumn())
|
return ae.getValues()[0];
|
||||||
value = ae.getValueAt(srcRowNum, ae.getFirstColumn());
|
}
|
||||||
else
|
return ae.getValueAt(ae.getFirstRow(), srcColNum);
|
||||||
value = ErrorEval.VALUE_INVALID;
|
}
|
||||||
|
if (ae.isColumn()) {
|
||||||
|
return ae.getValueAt(srcRowNum, ae.getFirstColumn());
|
||||||
|
}
|
||||||
|
return ErrorEval.VALUE_INVALID;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum,
|
||||||
|
HSSFWorkbook workbook, HSSFSheet sheet) {
|
||||||
|
|
||||||
|
if(operation instanceof FunctionEval) {
|
||||||
|
FunctionEval fe = (FunctionEval) operation;
|
||||||
|
if(fe.isFreeRefFunction()) {
|
||||||
|
return fe.getFreeRefFunction().evaluate(ops, srcRowNum, srcColNum, workbook, sheet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return operation.evaluate(ops, srcRowNum, srcColNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AreaEval evaluateAreaPtg(HSSFSheet sheet, HSSFWorkbook workbook, AreaPtg ap) {
|
||||||
|
short row0 = ap.getFirstRow();
|
||||||
|
short col0 = ap.getFirstColumn();
|
||||||
|
short row1 = ap.getLastRow();
|
||||||
|
short 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 = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
|
||||||
|
for (short x = row0; sheet != null && x < row1 + 1; x++) {
|
||||||
|
HSSFRow row = sheet.getRow(x);
|
||||||
|
for (short y = col0; row != null && y < col1 + 1; y++) {
|
||||||
|
values[(x - row0) * (col1 - col0 + 1) + (y - col0)] =
|
||||||
|
getEvalForCell(row.getCell(y), row, sheet, workbook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AreaEval ae = new Area2DEval(ap, values);
|
||||||
|
return ae;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AreaEval evaluateArea3dPtg(HSSFWorkbook workbook, Area3DPtg a3dp) {
|
||||||
|
short row0 = a3dp.getFirstRow();
|
||||||
|
short col0 = a3dp.getFirstColumn();
|
||||||
|
short row1 = a3dp.getLastRow();
|
||||||
|
short 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 = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
|
||||||
|
for (short x = row0; xsheet != null && x < row1 + 1; x++) {
|
||||||
|
HSSFRow row = xsheet.getRow(x);
|
||||||
|
for (short y = col0; row != null && y < col1 + 1; y++) {
|
||||||
|
values[(x - row0) * (col1 - col0 + 1) + (y - col0)] =
|
||||||
|
getEvalForCell(row.getCell(y), row, xsheet, workbook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AreaEval ae = new Area3DEval(a3dp, values);
|
||||||
|
return ae;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the OperationEval concrete impl instance corresponding
|
* returns the OperationEval concrete impl instance corresponding
|
||||||
* to the suplied operationPtg
|
* to the suplied operationPtg
|
||||||
|
@ -38,6 +38,7 @@ public final class AllIndividualFunctionEvaluationTests {
|
|||||||
result.addTestSuite(TestMid.class);
|
result.addTestSuite(TestMid.class);
|
||||||
result.addTestSuite(TestMathX.class);
|
result.addTestSuite(TestMathX.class);
|
||||||
result.addTestSuite(TestMatch.class);
|
result.addTestSuite(TestMatch.class);
|
||||||
|
result.addTestSuite(TestOffset.class);
|
||||||
result.addTestSuite(TestRowCol.class);
|
result.addTestSuite(TestRowCol.class);
|
||||||
result.addTestSuite(TestSumproduct.class);
|
result.addTestSuite(TestSumproduct.class);
|
||||||
result.addTestSuite(TestStatsLib.class);
|
result.addTestSuite(TestStatsLib.class);
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
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 junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.apache.poi.hssf.record.formula.functions.Offset.LinearOffsetRange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for OFFSET function implementation
|
||||||
|
*
|
||||||
|
* @author Josh Micich
|
||||||
|
*/
|
||||||
|
public final class TestOffset extends TestCase {
|
||||||
|
|
||||||
|
|
||||||
|
private static void confirmDoubleConvert(double doubleVal, int expected) {
|
||||||
|
assertEquals(expected, Offset.convertDoubleToInt(doubleVal));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Excel's double to int conversion (for function 'OFFSET()') behaves more like Math.floor().
|
||||||
|
* Note - negative values are not symmetrical
|
||||||
|
*/
|
||||||
|
public void testDoubleConversion() {
|
||||||
|
|
||||||
|
confirmDoubleConvert(100.09, 100);
|
||||||
|
confirmDoubleConvert(100.01, 100);
|
||||||
|
confirmDoubleConvert(100.00, 100);
|
||||||
|
confirmDoubleConvert(99.99, 99);
|
||||||
|
|
||||||
|
confirmDoubleConvert(+2.01, +2);
|
||||||
|
confirmDoubleConvert(+2.00, +2);
|
||||||
|
confirmDoubleConvert(+1.99, +1);
|
||||||
|
confirmDoubleConvert(+1.01, +1);
|
||||||
|
confirmDoubleConvert(+1.00, +1);
|
||||||
|
confirmDoubleConvert(+0.99, 0);
|
||||||
|
confirmDoubleConvert(+0.01, 0);
|
||||||
|
confirmDoubleConvert( 0.00, 0);
|
||||||
|
confirmDoubleConvert(-0.01, -1);
|
||||||
|
confirmDoubleConvert(-0.99, -1);
|
||||||
|
confirmDoubleConvert(-1.00, -1);
|
||||||
|
confirmDoubleConvert(-1.01, -2);
|
||||||
|
confirmDoubleConvert(-1.99, -2);
|
||||||
|
confirmDoubleConvert(-2.00, -2);
|
||||||
|
confirmDoubleConvert(-2.01, -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLinearOffsetRange() {
|
||||||
|
LinearOffsetRange lor;
|
||||||
|
|
||||||
|
lor = new LinearOffsetRange(3, 2);
|
||||||
|
assertEquals(3, lor.getFirstIndex());
|
||||||
|
assertEquals(4, lor.getLastIndex());
|
||||||
|
lor = lor.normaliseAndTranslate(0); // expected no change
|
||||||
|
assertEquals(3, lor.getFirstIndex());
|
||||||
|
assertEquals(4, lor.getLastIndex());
|
||||||
|
|
||||||
|
lor = lor.normaliseAndTranslate(5);
|
||||||
|
assertEquals(8, lor.getFirstIndex());
|
||||||
|
assertEquals(9, lor.getLastIndex());
|
||||||
|
|
||||||
|
// negative length
|
||||||
|
|
||||||
|
lor = new LinearOffsetRange(6, -4).normaliseAndTranslate(0);
|
||||||
|
assertEquals(3, lor.getFirstIndex());
|
||||||
|
assertEquals(6, lor.getLastIndex());
|
||||||
|
|
||||||
|
|
||||||
|
// bounds checking
|
||||||
|
lor = new LinearOffsetRange(0, 100);
|
||||||
|
assertFalse(lor.isOutOfBounds(0, 16383));
|
||||||
|
lor = lor.normaliseAndTranslate(16300);
|
||||||
|
assertTrue(lor.isOutOfBounds(0, 16383));
|
||||||
|
assertFalse(lor.isOutOfBounds(0, 65535));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user