moved common formula-related code to org.apache.poi.ss.formula, eliminated dependencies on HSSF, reduced the number of eclipse warnings
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1037432 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
533f3a4f4a
commit
619e612817
@ -24,7 +24,7 @@ import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.apache.poi.POIOLE2TextExtractor;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
|
||||
import org.apache.poi.hssf.usermodel.HSSFComment;
|
||||
|
@ -69,7 +69,7 @@ import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate;
|
||||
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
|
||||
import org.apache.poi.hssf.record.aggregates.RecordAggregate.PositionTrackingVisitor;
|
||||
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
|
||||
import org.apache.poi.hssf.record.formula.FormulaShifter;
|
||||
import org.apache.poi.ss.formula.FormulaShifter;
|
||||
import org.apache.poi.hssf.util.PaneInformation;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
import org.apache.poi.util.Internal;
|
||||
|
@ -82,7 +82,7 @@ import org.apache.poi.hssf.record.WriteAccessRecord;
|
||||
import org.apache.poi.hssf.record.WriteProtectRecord;
|
||||
import org.apache.poi.hssf.record.common.UnicodeString;
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
import org.apache.poi.hssf.record.formula.FormulaShifter;
|
||||
import org.apache.poi.ss.formula.FormulaShifter;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.util.HSSFColor;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalName;
|
||||
|
@ -18,7 +18,7 @@
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.ss.formula.Formula;
|
||||
import org.apache.poi.util.BitField;
|
||||
|
@ -21,6 +21,7 @@ import org.apache.poi.hssf.record.formula.*;
|
||||
import org.apache.poi.hssf.util.CellRangeAddress8Bit;
|
||||
import org.apache.poi.ss.formula.Formula;
|
||||
import org.apache.poi.ss.SpreadsheetVersion;
|
||||
import org.apache.poi.ss.formula.SharedFormula;
|
||||
import org.apache.poi.util.HexDump;
|
||||
import org.apache.poi.util.LittleEndianOutput;
|
||||
|
||||
|
@ -26,7 +26,7 @@ import org.apache.poi.hssf.record.CFRuleRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.formula.AreaErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.FormulaShifter;
|
||||
import org.apache.poi.ss.formula.FormulaShifter;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
|
||||
|
@ -22,7 +22,7 @@ import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.model.RecordStream;
|
||||
import org.apache.poi.hssf.record.CFHeaderRecord;
|
||||
import org.apache.poi.hssf.record.formula.FormulaShifter;
|
||||
import org.apache.poi.ss.formula.FormulaShifter;
|
||||
|
||||
/**
|
||||
* Holds all the conditional formatting for a workbook sheet.<p/>
|
||||
|
@ -38,7 +38,7 @@ import org.apache.poi.hssf.record.RowRecord;
|
||||
import org.apache.poi.hssf.record.SharedFormulaRecord;
|
||||
import org.apache.poi.hssf.record.TableRecord;
|
||||
import org.apache.poi.hssf.record.UnknownRecord;
|
||||
import org.apache.poi.hssf.record.formula.FormulaShifter;
|
||||
import org.apache.poi.ss.formula.FormulaShifter;
|
||||
import org.apache.poi.ss.SpreadsheetVersion;
|
||||
|
||||
/**
|
||||
|
@ -29,7 +29,7 @@ import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.RecordBase;
|
||||
import org.apache.poi.hssf.record.StringRecord;
|
||||
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
|
||||
import org.apache.poi.hssf.record.formula.FormulaShifter;
|
||||
import org.apache.poi.ss.formula.FormulaShifter;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
|
||||
/**
|
||||
|
@ -17,8 +17,8 @@
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
|
||||
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
|
||||
import org.apache.poi.ss.formula.function.FunctionMetadata;
|
||||
import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.RecordFormatException;
|
||||
import org.apache.poi.util.LittleEndianInput;
|
||||
import org.apache.poi.util.LittleEndianOutput;
|
||||
|
||||
@ -62,7 +61,7 @@ public final class ExpPtg extends ControlPtg {
|
||||
}
|
||||
|
||||
public String toFormulaString() {
|
||||
throw new RecordFormatException("Coding Error: Expected ExpPtg to be converted from Shared to Non-Shared Formula by ValueRecordsAggregate, but it wasn't");
|
||||
throw new RuntimeException("Coding Error: Expected ExpPtg to be converted from Shared to Non-Shared Formula by ValueRecordsAggregate, but it wasn't");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
@ -19,6 +19,7 @@ package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
|
||||
import org.apache.poi.ss.formula.SheetNameFormatter;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
|
@ -1,294 +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;
|
||||
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class FormulaShifter {
|
||||
|
||||
/**
|
||||
* Extern sheet index of sheet where moving is occurring
|
||||
*/
|
||||
private final int _externSheetIndex;
|
||||
private final int _firstMovedIndex;
|
||||
private final int _lastMovedIndex;
|
||||
private final int _amountToMove;
|
||||
|
||||
private FormulaShifter(int externSheetIndex, int firstMovedIndex, int lastMovedIndex, int amountToMove) {
|
||||
if (amountToMove == 0) {
|
||||
throw new IllegalArgumentException("amountToMove must not be zero");
|
||||
}
|
||||
if (firstMovedIndex > lastMovedIndex) {
|
||||
throw new IllegalArgumentException("firstMovedIndex, lastMovedIndex out of order");
|
||||
}
|
||||
_externSheetIndex = externSheetIndex;
|
||||
_firstMovedIndex = firstMovedIndex;
|
||||
_lastMovedIndex = lastMovedIndex;
|
||||
_amountToMove = amountToMove;
|
||||
}
|
||||
|
||||
public static FormulaShifter createForRowShift(int externSheetIndex, int firstMovedRowIndex, int lastMovedRowIndex, int numberOfRowsToMove) {
|
||||
return new FormulaShifter(externSheetIndex, firstMovedRowIndex, lastMovedRowIndex, numberOfRowsToMove);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
sb.append(getClass().getName());
|
||||
sb.append(" [");
|
||||
sb.append(_firstMovedIndex);
|
||||
sb.append(_lastMovedIndex);
|
||||
sb.append(_amountToMove);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ptgs - if necessary, will get modified by this method
|
||||
* @param currentExternSheetIx - the extern sheet index of the sheet that contains the formula being adjusted
|
||||
* @return <code>true</code> if a change was made to the formula tokens
|
||||
*/
|
||||
public boolean adjustFormula(Ptg[] ptgs, int currentExternSheetIx) {
|
||||
boolean refsWereChanged = false;
|
||||
for(int i=0; i<ptgs.length; i++) {
|
||||
Ptg newPtg = adjustPtg(ptgs[i], currentExternSheetIx);
|
||||
if (newPtg != null) {
|
||||
refsWereChanged = true;
|
||||
ptgs[i] = newPtg;
|
||||
}
|
||||
}
|
||||
return refsWereChanged;
|
||||
}
|
||||
|
||||
private Ptg adjustPtg(Ptg ptg, int currentExternSheetIx) {
|
||||
return adjustPtgDueToRowMove(ptg, currentExternSheetIx);
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if this Ptg needed to be changed
|
||||
*/
|
||||
private Ptg adjustPtgDueToRowMove(Ptg ptg, int currentExternSheetIx) {
|
||||
if(ptg instanceof RefPtg) {
|
||||
if (currentExternSheetIx != _externSheetIndex) {
|
||||
// local refs on other sheets are unaffected
|
||||
return null;
|
||||
}
|
||||
RefPtg rptg = (RefPtg)ptg;
|
||||
return rowMoveRefPtg(rptg);
|
||||
}
|
||||
if(ptg instanceof Ref3DPtg) {
|
||||
Ref3DPtg rptg = (Ref3DPtg)ptg;
|
||||
if (_externSheetIndex != rptg.getExternSheetIndex()) {
|
||||
// only move 3D refs that refer to the sheet with cells being moved
|
||||
// (currentExternSheetIx is irrelevant)
|
||||
return null;
|
||||
}
|
||||
return rowMoveRefPtg(rptg);
|
||||
}
|
||||
if(ptg instanceof Area2DPtgBase) {
|
||||
if (currentExternSheetIx != _externSheetIndex) {
|
||||
// local refs on other sheets are unaffected
|
||||
return ptg;
|
||||
}
|
||||
return rowMoveAreaPtg((Area2DPtgBase)ptg);
|
||||
}
|
||||
if(ptg instanceof Area3DPtg) {
|
||||
Area3DPtg aptg = (Area3DPtg)ptg;
|
||||
if (_externSheetIndex != aptg.getExternSheetIndex()) {
|
||||
// only move 3D refs that refer to the sheet with cells being moved
|
||||
// (currentExternSheetIx is irrelevant)
|
||||
return null;
|
||||
}
|
||||
return rowMoveAreaPtg(aptg);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Ptg rowMoveRefPtg(RefPtgBase rptg) {
|
||||
int refRow = rptg.getRow();
|
||||
if (_firstMovedIndex <= refRow && refRow <= _lastMovedIndex) {
|
||||
// Rows being moved completely enclose the ref.
|
||||
// - move the area ref along with the rows regardless of destination
|
||||
rptg.setRow(refRow + _amountToMove);
|
||||
return rptg;
|
||||
}
|
||||
// else rules for adjusting area may also depend on the destination of the moved rows
|
||||
|
||||
int destFirstRowIndex = _firstMovedIndex + _amountToMove;
|
||||
int destLastRowIndex = _lastMovedIndex + _amountToMove;
|
||||
|
||||
// ref is outside source rows
|
||||
// check for clashes with destination
|
||||
|
||||
if (destLastRowIndex < refRow || refRow < destFirstRowIndex) {
|
||||
// destination rows are completely outside ref
|
||||
return null;
|
||||
}
|
||||
|
||||
if (destFirstRowIndex <= refRow && refRow <= destLastRowIndex) {
|
||||
// destination rows enclose the area (possibly exactly)
|
||||
return createDeletedRef(rptg);
|
||||
}
|
||||
throw new IllegalStateException("Situation not covered: (" + _firstMovedIndex + ", " +
|
||||
_lastMovedIndex + ", " + _amountToMove + ", " + refRow + ", " + refRow + ")");
|
||||
}
|
||||
|
||||
private Ptg rowMoveAreaPtg(AreaPtgBase aptg) {
|
||||
int aFirstRow = aptg.getFirstRow();
|
||||
int aLastRow = aptg.getLastRow();
|
||||
if (_firstMovedIndex <= aFirstRow && aLastRow <= _lastMovedIndex) {
|
||||
// Rows being moved completely enclose the area ref.
|
||||
// - move the area ref along with the rows regardless of destination
|
||||
aptg.setFirstRow(aFirstRow + _amountToMove);
|
||||
aptg.setLastRow(aLastRow + _amountToMove);
|
||||
return aptg;
|
||||
}
|
||||
// else rules for adjusting area may also depend on the destination of the moved rows
|
||||
|
||||
int destFirstRowIndex = _firstMovedIndex + _amountToMove;
|
||||
int destLastRowIndex = _lastMovedIndex + _amountToMove;
|
||||
|
||||
if (aFirstRow < _firstMovedIndex && _lastMovedIndex < aLastRow) {
|
||||
// Rows moved were originally *completely* within the area ref
|
||||
|
||||
// If the destination of the rows overlaps either the top
|
||||
// or bottom of the area ref there will be a change
|
||||
if (destFirstRowIndex < aFirstRow && aFirstRow <= destLastRowIndex) {
|
||||
// truncate the top of the area by the moved rows
|
||||
aptg.setFirstRow(destLastRowIndex+1);
|
||||
return aptg;
|
||||
} else if (destFirstRowIndex <= aLastRow && aLastRow < destLastRowIndex) {
|
||||
// truncate the bottom of the area by the moved rows
|
||||
aptg.setLastRow(destFirstRowIndex-1);
|
||||
return aptg;
|
||||
}
|
||||
// else - rows have moved completely outside the area ref,
|
||||
// or still remain completely within the area ref
|
||||
return null; // - no change to the area
|
||||
}
|
||||
if (_firstMovedIndex <= aFirstRow && aFirstRow <= _lastMovedIndex) {
|
||||
// Rows moved include the first row of the area ref, but not the last row
|
||||
// btw: (aLastRow > _lastMovedIndex)
|
||||
if (_amountToMove < 0) {
|
||||
// simple case - expand area by shifting top upward
|
||||
aptg.setFirstRow(aFirstRow + _amountToMove);
|
||||
return aptg;
|
||||
}
|
||||
if (destFirstRowIndex > aLastRow) {
|
||||
// in this case, excel ignores the row move
|
||||
return null;
|
||||
}
|
||||
int newFirstRowIx = aFirstRow + _amountToMove;
|
||||
if (destLastRowIndex < aLastRow) {
|
||||
// end of area is preserved (will remain exact same row)
|
||||
// the top area row is moved simply
|
||||
aptg.setFirstRow(newFirstRowIx);
|
||||
return aptg;
|
||||
}
|
||||
// else - bottom area row has been replaced - both area top and bottom may move now
|
||||
int areaRemainingTopRowIx = _lastMovedIndex + 1;
|
||||
if (destFirstRowIndex > areaRemainingTopRowIx) {
|
||||
// old top row of area has moved deep within the area, and exposed a new top row
|
||||
newFirstRowIx = areaRemainingTopRowIx;
|
||||
}
|
||||
aptg.setFirstRow(newFirstRowIx);
|
||||
aptg.setLastRow(Math.max(aLastRow, destLastRowIndex));
|
||||
return aptg;
|
||||
}
|
||||
if (_firstMovedIndex <= aLastRow && aLastRow <= _lastMovedIndex) {
|
||||
// Rows moved include the last row of the area ref, but not the first
|
||||
// btw: (aFirstRow < _firstMovedIndex)
|
||||
if (_amountToMove > 0) {
|
||||
// simple case - expand area by shifting bottom downward
|
||||
aptg.setLastRow(aLastRow + _amountToMove);
|
||||
return aptg;
|
||||
}
|
||||
if (destLastRowIndex < aFirstRow) {
|
||||
// in this case, excel ignores the row move
|
||||
return null;
|
||||
}
|
||||
int newLastRowIx = aLastRow + _amountToMove;
|
||||
if (destFirstRowIndex > aFirstRow) {
|
||||
// top of area is preserved (will remain exact same row)
|
||||
// the bottom area row is moved simply
|
||||
aptg.setLastRow(newLastRowIx);
|
||||
return aptg;
|
||||
}
|
||||
// else - top area row has been replaced - both area top and bottom may move now
|
||||
int areaRemainingBottomRowIx = _firstMovedIndex - 1;
|
||||
if (destLastRowIndex < areaRemainingBottomRowIx) {
|
||||
// old bottom row of area has moved up deep within the area, and exposed a new bottom row
|
||||
newLastRowIx = areaRemainingBottomRowIx;
|
||||
}
|
||||
aptg.setFirstRow(Math.min(aFirstRow, destFirstRowIndex));
|
||||
aptg.setLastRow(newLastRowIx);
|
||||
return aptg;
|
||||
}
|
||||
// else source rows include none of the rows of the area ref
|
||||
// check for clashes with destination
|
||||
|
||||
if (destLastRowIndex < aFirstRow || aLastRow < destFirstRowIndex) {
|
||||
// destination rows are completely outside area ref
|
||||
return null;
|
||||
}
|
||||
|
||||
if (destFirstRowIndex <= aFirstRow && aLastRow <= destLastRowIndex) {
|
||||
// destination rows enclose the area (possibly exactly)
|
||||
return createDeletedRef(aptg);
|
||||
}
|
||||
|
||||
if (aFirstRow <= destFirstRowIndex && destLastRowIndex <= aLastRow) {
|
||||
// destination rows are within area ref (possibly exact on top or bottom, but not both)
|
||||
return null; // - no change to area
|
||||
}
|
||||
|
||||
if (destFirstRowIndex < aFirstRow && aFirstRow <= destLastRowIndex) {
|
||||
// dest rows overlap top of area
|
||||
// - truncate the top
|
||||
aptg.setFirstRow(destLastRowIndex+1);
|
||||
return aptg;
|
||||
}
|
||||
if (destFirstRowIndex < aLastRow && aLastRow <= destLastRowIndex) {
|
||||
// dest rows overlap bottom of area
|
||||
// - truncate the bottom
|
||||
aptg.setLastRow(destFirstRowIndex-1);
|
||||
return aptg;
|
||||
}
|
||||
throw new IllegalStateException("Situation not covered: (" + _firstMovedIndex + ", " +
|
||||
_lastMovedIndex + ", " + _amountToMove + ", " + aFirstRow + ", " + aLastRow + ")");
|
||||
}
|
||||
|
||||
private static Ptg createDeletedRef(Ptg ptg) {
|
||||
if (ptg instanceof RefPtg) {
|
||||
return new RefErrorPtg();
|
||||
}
|
||||
if (ptg instanceof Ref3DPtg) {
|
||||
Ref3DPtg rptg = (Ref3DPtg) ptg;
|
||||
return new DeletedRef3DPtg(rptg.getExternSheetIndex());
|
||||
}
|
||||
if (ptg instanceof AreaPtg) {
|
||||
return new AreaErrPtg();
|
||||
}
|
||||
if (ptg instanceof Area3DPtg) {
|
||||
Area3DPtg area3DPtg = (Area3DPtg) ptg;
|
||||
return new DeletedArea3DPtg(area3DPtg.getExternSheetIndex());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unexpected ref ptg class (" + ptg.getClass().getName() + ")");
|
||||
}
|
||||
}
|
@ -17,8 +17,8 @@
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
|
||||
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
|
||||
import org.apache.poi.ss.formula.function.FunctionMetadata;
|
||||
import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
|
||||
import org.apache.poi.util.LittleEndianInput;
|
||||
import org.apache.poi.util.LittleEndianOutput;
|
||||
|
||||
|
@ -16,8 +16,8 @@
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
|
||||
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
|
||||
import org.apache.poi.ss.formula.function.FunctionMetadata;
|
||||
import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
|
||||
import org.apache.poi.util.LittleEndianInput;
|
||||
import org.apache.poi.util.LittleEndianOutput;
|
||||
|
||||
|
@ -1,97 +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;
|
||||
|
||||
import org.apache.poi.ss.SpreadsheetVersion;
|
||||
|
||||
/**
|
||||
* Encapsulates logic to convert shared formulaa into non shared equivalent
|
||||
*/
|
||||
public class SharedFormula {
|
||||
|
||||
private final int _columnWrappingMask;
|
||||
private final int _rowWrappingMask;
|
||||
|
||||
public SharedFormula(SpreadsheetVersion ssVersion){
|
||||
_columnWrappingMask = ssVersion.getLastColumnIndex(); //"IV" for .xls and "XFD" for .xlsx
|
||||
_rowWrappingMask = ssVersion.getLastRowIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a non shared formula from the shared formula counterpart, i.e.
|
||||
* Converts the shared formula into the equivalent {@link Ptg} array that it would have,
|
||||
* were it not shared.
|
||||
*
|
||||
* @param ptgs parsed tokens of the shared formula
|
||||
* @param formulaRow
|
||||
* @param formulaColumn
|
||||
*/
|
||||
public Ptg[] convertSharedFormulas(Ptg[] ptgs, int formulaRow, int formulaColumn) {
|
||||
|
||||
Ptg[] newPtgStack = new Ptg[ptgs.length];
|
||||
|
||||
for (int k = 0; k < ptgs.length; k++) {
|
||||
Ptg ptg = ptgs[k];
|
||||
byte originalOperandClass = -1;
|
||||
if (!ptg.isBaseToken()) {
|
||||
originalOperandClass = ptg.getPtgClass();
|
||||
}
|
||||
if (ptg instanceof RefPtgBase) {
|
||||
RefPtgBase refNPtg = (RefPtgBase)ptg;
|
||||
ptg = new RefPtg(fixupRelativeRow(formulaRow,refNPtg.getRow(),refNPtg.isRowRelative()),
|
||||
fixupRelativeColumn(formulaColumn,refNPtg.getColumn(),refNPtg.isColRelative()),
|
||||
refNPtg.isRowRelative(),
|
||||
refNPtg.isColRelative());
|
||||
ptg.setClass(originalOperandClass);
|
||||
} else if (ptg instanceof AreaPtgBase) {
|
||||
AreaPtgBase areaNPtg = (AreaPtgBase)ptg;
|
||||
ptg = new AreaPtg(fixupRelativeRow(formulaRow,areaNPtg.getFirstRow(),areaNPtg.isFirstRowRelative()),
|
||||
fixupRelativeRow(formulaRow,areaNPtg.getLastRow(),areaNPtg.isLastRowRelative()),
|
||||
fixupRelativeColumn(formulaColumn,areaNPtg.getFirstColumn(),areaNPtg.isFirstColRelative()),
|
||||
fixupRelativeColumn(formulaColumn,areaNPtg.getLastColumn(),areaNPtg.isLastColRelative()),
|
||||
areaNPtg.isFirstRowRelative(),
|
||||
areaNPtg.isLastRowRelative(),
|
||||
areaNPtg.isFirstColRelative(),
|
||||
areaNPtg.isLastColRelative());
|
||||
ptg.setClass(originalOperandClass);
|
||||
} else if (ptg instanceof OperandPtg) {
|
||||
// Any subclass of OperandPtg is mutable, so it's safest to not share these instances.
|
||||
ptg = ((OperandPtg) ptg).copy();
|
||||
} else {
|
||||
// all other Ptgs are immutable and can be shared
|
||||
}
|
||||
newPtgStack[k] = ptg;
|
||||
}
|
||||
return newPtgStack;
|
||||
}
|
||||
|
||||
private int fixupRelativeColumn(int currentcolumn, int column, boolean relative) {
|
||||
if(relative) {
|
||||
// mask out upper bits to produce 'wrapping' at the maximum column ("IV" for .xls and "XFD" for .xlsx)
|
||||
return (column + currentcolumn) & _columnWrappingMask;
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
private int fixupRelativeRow(int currentrow, int row, boolean relative) {
|
||||
if(relative) {
|
||||
return (row+currentrow) & _rowWrappingMask;
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
}
|
@ -1,223 +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;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
import org.apache.poi.ss.SpreadsheetVersion;
|
||||
|
||||
/**
|
||||
* Formats sheet names for use in formula expressions.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class SheetNameFormatter {
|
||||
|
||||
private static final char DELIMITER = '\'';
|
||||
|
||||
/**
|
||||
* Matches a single cell ref with no absolute ('$') markers
|
||||
*/
|
||||
private static final Pattern CELL_REF_PATTERN = Pattern.compile("([A-Za-z]+)([0-9]+)");
|
||||
|
||||
private SheetNameFormatter() {
|
||||
// no instances of this class
|
||||
}
|
||||
/**
|
||||
* Used to format sheet names as they would appear in cell formula expressions.
|
||||
* @return the sheet name unchanged if there is no need for delimiting. Otherwise the sheet
|
||||
* name is enclosed in single quotes ('). Any single quotes which were already present in the
|
||||
* sheet name will be converted to double single quotes ('').
|
||||
*/
|
||||
public static String format(String rawSheetName) {
|
||||
StringBuffer sb = new StringBuffer(rawSheetName.length() + 2);
|
||||
appendFormat(sb, rawSheetName);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for ({@link #format(String)}) when a StringBuffer is already available.
|
||||
*
|
||||
* @param out - sheet name will be appended here possibly with delimiting quotes
|
||||
*/
|
||||
public static void appendFormat(StringBuffer out, String rawSheetName) {
|
||||
boolean needsQuotes = needsDelimiting(rawSheetName);
|
||||
if(needsQuotes) {
|
||||
out.append(DELIMITER);
|
||||
appendAndEscape(out, rawSheetName);
|
||||
out.append(DELIMITER);
|
||||
} else {
|
||||
out.append(rawSheetName);
|
||||
}
|
||||
}
|
||||
public static void appendFormat(StringBuffer out, String workbookName, String rawSheetName) {
|
||||
boolean needsQuotes = needsDelimiting(workbookName) || needsDelimiting(rawSheetName);
|
||||
if(needsQuotes) {
|
||||
out.append(DELIMITER);
|
||||
out.append('[');
|
||||
appendAndEscape(out, workbookName.replace('[', '(').replace(']', ')'));
|
||||
out.append(']');
|
||||
appendAndEscape(out, rawSheetName);
|
||||
out.append(DELIMITER);
|
||||
} else {
|
||||
out.append('[');
|
||||
out.append(workbookName);
|
||||
out.append(']');
|
||||
out.append(rawSheetName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendAndEscape(StringBuffer sb, String rawSheetName) {
|
||||
int len = rawSheetName.length();
|
||||
for(int i=0; i<len; i++) {
|
||||
char ch = rawSheetName.charAt(i);
|
||||
if(ch == DELIMITER) {
|
||||
// single quotes (') are encoded as ('')
|
||||
sb.append(DELIMITER);
|
||||
}
|
||||
sb.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean needsDelimiting(String rawSheetName) {
|
||||
int len = rawSheetName.length();
|
||||
if(len < 1) {
|
||||
throw new RuntimeException("Zero length string is an invalid sheet name");
|
||||
}
|
||||
if(Character.isDigit(rawSheetName.charAt(0))) {
|
||||
// sheet name with digit in the first position always requires delimiting
|
||||
return true;
|
||||
}
|
||||
for(int i=0; i<len; i++) {
|
||||
char ch = rawSheetName.charAt(i);
|
||||
if(isSpecialChar(ch)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(Character.isLetter(rawSheetName.charAt(0))
|
||||
&& Character.isDigit(rawSheetName.charAt(len-1))) {
|
||||
// note - values like "A$1:$C$20" don't get this far
|
||||
if(nameLooksLikePlainCellReference(rawSheetName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (nameLooksLikeBooleanLiteral(rawSheetName)) {
|
||||
return true;
|
||||
}
|
||||
// Error constant literals all contain '#' and other special characters
|
||||
// so they don't get this far
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean nameLooksLikeBooleanLiteral(String rawSheetName) {
|
||||
switch(rawSheetName.charAt(0)) {
|
||||
case 'T': case 't':
|
||||
return "TRUE".equalsIgnoreCase(rawSheetName);
|
||||
case 'F': case 'f':
|
||||
return "FALSE".equalsIgnoreCase(rawSheetName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @return <code>true</code> if the presence of the specified character in a sheet name would
|
||||
* require the sheet name to be delimited in formulas. This includes every non-alphanumeric
|
||||
* character besides underscore '_' and dot '.'.
|
||||
*/
|
||||
/* package */ static boolean isSpecialChar(char ch) {
|
||||
// note - Character.isJavaIdentifierPart() would allow dollars '$'
|
||||
if(Character.isLetterOrDigit(ch)) {
|
||||
return false;
|
||||
}
|
||||
switch(ch) {
|
||||
case '.': // dot is OK
|
||||
case '_': // underscore is OK
|
||||
return false;
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
throw new RuntimeException("Illegal character (0x"
|
||||
+ Integer.toHexString(ch) + ") found in sheet name");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to decide whether sheet names like 'AB123' need delimiting due to the fact that they
|
||||
* look like cell references.
|
||||
* <p/>
|
||||
* This code is currently being used for translating formulas represented with <code>Ptg</code>
|
||||
* tokens into human readable text form. In formula expressions, a sheet name always has a
|
||||
* trailing '!' so there is little chance for ambiguity. It doesn't matter too much what this
|
||||
* method returns but it is worth noting the likely consumers of these formula text strings:
|
||||
* <ol>
|
||||
* <li>POI's own formula parser</li>
|
||||
* <li>Visual reading by human</li>
|
||||
* <li>VBA automation entry into Excel cell contents e.g. ActiveCell.Formula = "=c64!A1"</li>
|
||||
* <li>Manual entry into Excel cell contents</li>
|
||||
* <li>Some third party formula parser</li>
|
||||
* </ol>
|
||||
*
|
||||
* At the time of writing, POI's formula parser tolerates cell-like sheet names in formulas
|
||||
* with or without delimiters. The same goes for Excel(2007), both manual and automated entry.
|
||||
* <p/>
|
||||
* For better or worse this implementation attempts to replicate Excel's formula renderer.
|
||||
* Excel uses range checking on the apparent 'row' and 'column' components. Note however that
|
||||
* the maximum sheet size varies across versions.
|
||||
* @see org.apache.poi.hssf.util.CellReference
|
||||
*/
|
||||
/* package */ static boolean cellReferenceIsWithinRange(String lettersPrefix, String numbersSuffix) {
|
||||
return CellReference.cellReferenceIsWithinRange(lettersPrefix, numbersSuffix, SpreadsheetVersion.EXCEL97);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note - this method assumes the specified rawSheetName has only letters and digits. It
|
||||
* cannot be used to match absolute or range references (using the dollar or colon char).
|
||||
* <p/>
|
||||
* Some notable cases:
|
||||
* <blockquote><table border="0" cellpadding="1" cellspacing="0"
|
||||
* summary="Notable cases.">
|
||||
* <tr><th>Input </th><th>Result </th><th>Comments</th></tr>
|
||||
* <tr><td>"A1" </td><td>true</td><td> </td></tr>
|
||||
* <tr><td>"a111" </td><td>true</td><td> </td></tr>
|
||||
* <tr><td>"AA" </td><td>false</td><td> </td></tr>
|
||||
* <tr><td>"aa1" </td><td>true</td><td> </td></tr>
|
||||
* <tr><td>"A1A" </td><td>false</td><td> </td></tr>
|
||||
* <tr><td>"A1A1" </td><td>false</td><td> </td></tr>
|
||||
* <tr><td>"A$1:$C$20" </td><td>false</td><td>Not a plain cell reference</td></tr>
|
||||
* <tr><td>"SALES20080101" </td><td>true</td>
|
||||
* <td>Still needs delimiting even though well out of range</td></tr>
|
||||
* </table></blockquote>
|
||||
*
|
||||
* @return <code>true</code> if there is any possible ambiguity that the specified rawSheetName
|
||||
* could be interpreted as a valid cell name.
|
||||
*/
|
||||
/* package */ static boolean nameLooksLikePlainCellReference(String rawSheetName) {
|
||||
Matcher matcher = CELL_REF_PATTERN.matcher(rawSheetName);
|
||||
if(!matcher.matches()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// rawSheetName == "Sheet1" gets this far.
|
||||
String lettersPrefix = matcher.group(1);
|
||||
String numbersSuffix = matcher.group(2);
|
||||
return cellReferenceIsWithinRange(lettersPrefix, numbersSuffix);
|
||||
}
|
||||
}
|
@ -17,7 +17,6 @@
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.RecordFormatException;
|
||||
import org.apache.poi.util.LittleEndianInput;
|
||||
import org.apache.poi.util.LittleEndianOutput;
|
||||
|
||||
@ -69,7 +68,7 @@ public final class TblPtg extends ControlPtg {
|
||||
public String toFormulaString()
|
||||
{
|
||||
// table(....)[][]
|
||||
throw new RecordFormatException("Table and Arrays are not yet supported");
|
||||
throw new RuntimeException("Table and Arrays are not yet supported");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
@ -1,164 +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.atp;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
|
||||
import org.apache.poi.hssf.record.formula.udf.UDFFinder;
|
||||
import org.apache.poi.ss.formula.OperationEvaluationContext;
|
||||
import org.apache.poi.ss.formula.eval.NotImplementedException;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
* @author Petr Udalau - systematized work of add-in libraries and user defined functions.
|
||||
*/
|
||||
public final class AnalysisToolPak implements UDFFinder {
|
||||
|
||||
public static final UDFFinder instance = new AnalysisToolPak();
|
||||
|
||||
private static final class NotImplemented implements FreeRefFunction {
|
||||
private final String _functionName;
|
||||
|
||||
public NotImplemented(String functionName) {
|
||||
_functionName = functionName;
|
||||
}
|
||||
|
||||
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
|
||||
throw new NotImplementedException(_functionName);
|
||||
}
|
||||
};
|
||||
|
||||
private final Map<String, FreeRefFunction> _functionsByName = createFunctionsMap();
|
||||
|
||||
|
||||
private AnalysisToolPak() {
|
||||
// enforce singleton
|
||||
}
|
||||
|
||||
public FreeRefFunction findFunction(String name) {
|
||||
return _functionsByName.get(name);
|
||||
}
|
||||
|
||||
private Map<String, FreeRefFunction> createFunctionsMap() {
|
||||
Map<String, FreeRefFunction> m = new HashMap<String, FreeRefFunction>(100);
|
||||
|
||||
r(m, "ACCRINT", null);
|
||||
r(m, "ACCRINTM", null);
|
||||
r(m, "AMORDEGRC", null);
|
||||
r(m, "AMORLINC", null);
|
||||
r(m, "BESSELI", null);
|
||||
r(m, "BESSELJ", null);
|
||||
r(m, "BESSELK", null);
|
||||
r(m, "BESSELY", null);
|
||||
r(m, "BIN2DEC", null);
|
||||
r(m, "BIN2HEX", null);
|
||||
r(m, "BIN2OCT", null);
|
||||
r(m, "CO MPLEX", null);
|
||||
r(m, "CONVERT", null);
|
||||
r(m, "COUPDAYBS", null);
|
||||
r(m, "COUPDAYS", null);
|
||||
r(m, "COUPDAYSNC", null);
|
||||
r(m, "COUPNCD", null);
|
||||
r(m, "COUPNUM", null);
|
||||
r(m, "COUPPCD", null);
|
||||
r(m, "CUMIPMT", null);
|
||||
r(m, "CUMPRINC", null);
|
||||
r(m, "DEC2BIN", null);
|
||||
r(m, "DEC2HEX", null);
|
||||
r(m, "DEC2OCT", null);
|
||||
r(m, "DELTA", null);
|
||||
r(m, "DISC", null);
|
||||
r(m, "DOLLARDE", null);
|
||||
r(m, "DOLLARFR", null);
|
||||
r(m, "DURATION", null);
|
||||
r(m, "EDATE", null);
|
||||
r(m, "EFFECT", null);
|
||||
r(m, "EOMONTH", null);
|
||||
r(m, "ERF", null);
|
||||
r(m, "ERFC", null);
|
||||
r(m, "FACTDOUBLE", null);
|
||||
r(m, "FVSCHEDULE", null);
|
||||
r(m, "GCD", null);
|
||||
r(m, "GESTEP", null);
|
||||
r(m, "HEX2BIN", null);
|
||||
r(m, "HEX2DEC", null);
|
||||
r(m, "HEX2OCT", null);
|
||||
r(m, "IMABS", null);
|
||||
r(m, "IMAGINARY", null);
|
||||
r(m, "IMARGUMENT", null);
|
||||
r(m, "IMCONJUGATE", null);
|
||||
r(m, "IMCOS", null);
|
||||
r(m, "IMDIV", null);
|
||||
r(m, "IMEXP", null);
|
||||
r(m, "IMLN", null);
|
||||
r(m, "IMLOG10", null);
|
||||
r(m, "IMLOG2", null);
|
||||
r(m, "IMPOWER", null);
|
||||
r(m, "IMPRODUCT", null);
|
||||
r(m, "IMREAL", null);
|
||||
r(m, "IMSIN", null);
|
||||
r(m, "IMSQRT", null);
|
||||
r(m, "IMSUB", null);
|
||||
r(m, "IMSUM", null);
|
||||
r(m, "INTRATE", null);
|
||||
r(m, "ISEVEN", ParityFunction.IS_EVEN);
|
||||
r(m, "ISODD", ParityFunction.IS_ODD);
|
||||
r(m, "LCM", null);
|
||||
r(m, "MDURATION", null);
|
||||
r(m, "MROUND", null);
|
||||
r(m, "MULTINOMIAL", null);
|
||||
r(m, "NETWORKDAYS", null);
|
||||
r(m, "NOMINAL", null);
|
||||
r(m, "OCT2BIN", null);
|
||||
r(m, "OCT2DEC", null);
|
||||
r(m, "OCT2HEX", null);
|
||||
r(m, "ODDFPRICE", null);
|
||||
r(m, "ODDFYIELD", null);
|
||||
r(m, "ODDLPRICE", null);
|
||||
r(m, "ODDLYIELD", null);
|
||||
r(m, "PRICE", null);
|
||||
r(m, "PRICEDISC", null);
|
||||
r(m, "PRICEMAT", null);
|
||||
r(m, "QUOTIENT", null);
|
||||
r(m, "RANDBETWEEN", RandBetween.instance);
|
||||
r(m, "RECEIVED", null);
|
||||
r(m, "SERIESSUM", null);
|
||||
r(m, "SQRTPI", null);
|
||||
r(m, "TBILLEQ", null);
|
||||
r(m, "TBILLPRICE", null);
|
||||
r(m, "TBILLYIELD", null);
|
||||
r(m, "WEEKNUM", null);
|
||||
r(m, "WORKDAY", null);
|
||||
r(m, "XIRR", null);
|
||||
r(m, "XNPV", null);
|
||||
r(m, "YEARFRAC", YearFrac.instance);
|
||||
r(m, "YIELD", null);
|
||||
r(m, "YIELDDISC", null);
|
||||
r(m, "YIELDMAT", null);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private static void r(Map<String, FreeRefFunction> m, String functionName, FreeRefFunction pFunc) {
|
||||
FreeRefFunction func = pFunc == null ? new NotImplemented(functionName) : pFunc;
|
||||
m.put(functionName, func);
|
||||
}
|
||||
}
|
@ -1,67 +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.atp;
|
||||
|
||||
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.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
|
||||
import org.apache.poi.ss.formula.OperationEvaluationContext;
|
||||
/**
|
||||
* Implementation of Excel 'Analysis ToolPak' function ISEVEN() ISODD()<br/>
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class ParityFunction implements FreeRefFunction {
|
||||
|
||||
public static final FreeRefFunction IS_EVEN = new ParityFunction(0);
|
||||
public static final FreeRefFunction IS_ODD = new ParityFunction(1);
|
||||
private final int _desiredParity;
|
||||
|
||||
private ParityFunction(int desiredParity) {
|
||||
_desiredParity = desiredParity;
|
||||
}
|
||||
|
||||
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
|
||||
if (args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
int val;
|
||||
try {
|
||||
val = evaluateArgParity(args[0], ec.getRowIndex(), ec.getColumnIndex());
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
|
||||
return BoolEval.valueOf(val == _desiredParity);
|
||||
}
|
||||
|
||||
private static int evaluateArgParity(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short)srcCellCol);
|
||||
|
||||
double d = OperandResolver.coerceValueToDouble(ve);
|
||||
if (d < 0) {
|
||||
d = -d;
|
||||
}
|
||||
long v = (long) Math.floor(d);
|
||||
return (int) (v & 0x0001);
|
||||
}
|
||||
}
|
@ -1,84 +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.atp;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
|
||||
import org.apache.poi.ss.formula.OperationEvaluationContext;
|
||||
|
||||
/**
|
||||
* Implementation of Excel 'Analysis ToolPak' function RANDBETWEEN()<br/>
|
||||
*
|
||||
* Returns a random integer number between the numbers you specify.<p/>
|
||||
*
|
||||
* <b>Syntax</b><br/>
|
||||
* <b>RANDBETWEEN</b>(<b>bottom</b>, <b>top</b>)<p/>
|
||||
*
|
||||
* <b>bottom</b> is the smallest integer RANDBETWEEN will return.<br/>
|
||||
* <b>top</b> is the largest integer RANDBETWEEN will return.<br/>
|
||||
|
||||
* @author Brendan Nolan
|
||||
*/
|
||||
final class RandBetween implements FreeRefFunction{
|
||||
|
||||
public static final FreeRefFunction instance = new RandBetween();
|
||||
|
||||
private RandBetween() {
|
||||
//enforces singleton
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate for RANDBETWEEN(). Must be given two arguments. Bottom must be greater than top.
|
||||
* Bottom is rounded up and top value is rounded down. After rounding top has to be set greater
|
||||
* than top.
|
||||
*
|
||||
* @see org.apache.poi.hssf.record.formula.functions.FreeRefFunction#evaluate(org.apache.poi.hssf.record.formula.eval.ValueEval[], org.apache.poi.ss.formula.OperationEvaluationContext)
|
||||
*/
|
||||
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
|
||||
|
||||
double bottom, top;
|
||||
|
||||
if (args.length != 2) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
bottom = OperandResolver.coerceValueToDouble(OperandResolver.getSingleValue(args[0], ec.getRowIndex(), ec.getColumnIndex()));
|
||||
top = OperandResolver.coerceValueToDouble(OperandResolver.getSingleValue(args[1], ec.getRowIndex(), ec.getColumnIndex()));
|
||||
if(bottom > top) {
|
||||
return ErrorEval.NUM_ERROR;
|
||||
}
|
||||
} catch (EvaluationException e) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
bottom = Math.ceil(bottom);
|
||||
top = Math.floor(top);
|
||||
|
||||
if(bottom > top) {
|
||||
top = bottom;
|
||||
}
|
||||
|
||||
return new NumberEval((bottom + (int)(Math.random() * ((top - bottom) + 1))));
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,159 +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.atp;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
|
||||
import org.apache.poi.ss.formula.OperationEvaluationContext;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
/**
|
||||
* Implementation of Excel 'Analysis ToolPak' function YEARFRAC()<br/>
|
||||
*
|
||||
* Returns the fraction of the year spanned by two dates.<p/>
|
||||
*
|
||||
* <b>Syntax</b><br/>
|
||||
* <b>YEARFRAC</b>(<b>startDate</b>, <b>endDate</b>, basis)<p/>
|
||||
*
|
||||
* The <b>basis</b> optionally specifies the behaviour of YEARFRAC as follows:
|
||||
*
|
||||
* <table border="0" cellpadding="1" cellspacing="0" summary="basis parameter description">
|
||||
* <tr><th>Value</th><th>Days per Month</th><th>Days per Year</th></tr>
|
||||
* <tr align='center'><td>0 (default)</td><td>30</td><td>360</td></tr>
|
||||
* <tr align='center'><td>1</td><td>actual</td><td>actual</td></tr>
|
||||
* <tr align='center'><td>2</td><td>actual</td><td>360</td></tr>
|
||||
* <tr align='center'><td>3</td><td>actual</td><td>365</td></tr>
|
||||
* <tr align='center'><td>4</td><td>30</td><td>360</td></tr>
|
||||
* </table>
|
||||
*
|
||||
*/
|
||||
final class YearFrac implements FreeRefFunction {
|
||||
|
||||
public static final FreeRefFunction instance = new YearFrac();
|
||||
|
||||
private YearFrac() {
|
||||
// enforce singleton
|
||||
}
|
||||
|
||||
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
|
||||
int srcCellRow = ec.getRowIndex();
|
||||
int srcCellCol = ec.getColumnIndex();
|
||||
double result;
|
||||
try {
|
||||
int basis = 0; // default
|
||||
switch(args.length) {
|
||||
case 3:
|
||||
basis = evaluateIntArg(args[2], srcCellRow, srcCellCol);
|
||||
case 2:
|
||||
break;
|
||||
default:
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
double startDateVal = evaluateDateArg(args[0], srcCellRow, srcCellCol);
|
||||
double endDateVal = evaluateDateArg(args[1], srcCellRow, srcCellCol);
|
||||
result = YearFracCalculator.calculate(startDateVal, endDateVal, basis);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
private static double evaluateDateArg(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short) srcCellCol);
|
||||
|
||||
if (ve instanceof StringEval) {
|
||||
String strVal = ((StringEval) ve).getStringValue();
|
||||
Double dVal = OperandResolver.parseDouble(strVal);
|
||||
if (dVal != null) {
|
||||
return dVal.doubleValue();
|
||||
}
|
||||
Calendar date = parseDate(strVal);
|
||||
return DateUtil.getExcelDate(date, false);
|
||||
}
|
||||
return OperandResolver.coerceValueToDouble(ve);
|
||||
}
|
||||
|
||||
private static Calendar parseDate(String strVal) throws EvaluationException {
|
||||
String[] parts = Pattern.compile("/").split(strVal);
|
||||
if (parts.length != 3) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
String part2 = parts[2];
|
||||
int spacePos = part2.indexOf(' ');
|
||||
if (spacePos > 0) {
|
||||
// drop time portion if present
|
||||
part2 = part2.substring(0, spacePos);
|
||||
}
|
||||
int f0;
|
||||
int f1;
|
||||
int f2;
|
||||
try {
|
||||
f0 = Integer.parseInt(parts[0]);
|
||||
f1 = Integer.parseInt(parts[1]);
|
||||
f2 = Integer.parseInt(part2);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
if (f0<0 || f1<0 || f2<0 || (f0>12 && f1>12 && f2>12)) {
|
||||
// easy to see this cannot be a valid date
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
|
||||
if (f0 >= 1900 && f0 < 9999) {
|
||||
// when 4 digit value appears first, the format is YYYY/MM/DD, regardless of OS settings
|
||||
return makeDate(f0, f1, f2);
|
||||
}
|
||||
// otherwise the format seems to depend on OS settings (default date format)
|
||||
if (false) {
|
||||
// MM/DD/YYYY is probably a good guess, if the in the US
|
||||
return makeDate(f2, f0, f1);
|
||||
}
|
||||
// TODO - find a way to choose the correct date format
|
||||
throw new RuntimeException("Unable to determine date format for text '" + strVal + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param month 1-based
|
||||
*/
|
||||
private static Calendar makeDate(int year, int month, int day) throws EvaluationException {
|
||||
if (month < 1 || month > 12) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
Calendar cal = new GregorianCalendar(year, month-1, 1, 0, 0, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
if (day <1 || day>cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
cal.set(Calendar.DAY_OF_MONTH, day);
|
||||
return cal;
|
||||
}
|
||||
|
||||
private static int evaluateIntArg(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short) srcCellCol);
|
||||
return OperandResolver.coerceValueToInt(ve);
|
||||
}
|
||||
}
|
@ -1,344 +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.atp;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
|
||||
|
||||
/**
|
||||
* Internal calculation methods for Excel 'Analysis ToolPak' function YEARFRAC()<br/>
|
||||
*
|
||||
* Algorithm inspired by www.dwheeler.com/yearfrac
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class YearFracCalculator {
|
||||
/** use UTC time-zone to avoid daylight savings issues */
|
||||
private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC");
|
||||
private static final int MS_PER_HOUR = 60 * 60 * 1000;
|
||||
private static final int MS_PER_DAY = 24 * MS_PER_HOUR;
|
||||
private static final int DAYS_PER_NORMAL_YEAR = 365;
|
||||
private static final int DAYS_PER_LEAP_YEAR = DAYS_PER_NORMAL_YEAR + 1;
|
||||
|
||||
/** the length of normal long months i.e. 31 */
|
||||
private static final int LONG_MONTH_LEN = 31;
|
||||
/** the length of normal short months i.e. 30 */
|
||||
private static final int SHORT_MONTH_LEN = 30;
|
||||
private static final int SHORT_FEB_LEN = 28;
|
||||
private static final int LONG_FEB_LEN = SHORT_FEB_LEN + 1;
|
||||
|
||||
private YearFracCalculator() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
|
||||
public static double calculate(double pStartDateVal, double pEndDateVal, int basis) throws EvaluationException {
|
||||
|
||||
if (basis < 0 || basis >= 5) {
|
||||
// if basis is invalid the result is #NUM!
|
||||
throw new EvaluationException(ErrorEval.NUM_ERROR);
|
||||
}
|
||||
|
||||
// common logic for all bases
|
||||
|
||||
// truncate day values
|
||||
int startDateVal = (int) Math.floor(pStartDateVal);
|
||||
int endDateVal = (int) Math.floor(pEndDateVal);
|
||||
if (startDateVal == endDateVal) {
|
||||
// when dates are equal, result is zero
|
||||
return 0;
|
||||
}
|
||||
// swap start and end if out of order
|
||||
if (startDateVal > endDateVal) {
|
||||
int temp = startDateVal;
|
||||
startDateVal = endDateVal;
|
||||
endDateVal = temp;
|
||||
}
|
||||
|
||||
switch (basis) {
|
||||
case 0: return basis0(startDateVal, endDateVal);
|
||||
case 1: return basis1(startDateVal, endDateVal);
|
||||
case 2: return basis2(startDateVal, endDateVal);
|
||||
case 3: return basis3(startDateVal, endDateVal);
|
||||
case 4: return basis4(startDateVal, endDateVal);
|
||||
}
|
||||
throw new IllegalStateException("cannot happen");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param startDateVal assumed to be less than or equal to endDateVal
|
||||
* @param endDateVal assumed to be greater than or equal to startDateVal
|
||||
*/
|
||||
public static double basis0(int startDateVal, int endDateVal) {
|
||||
SimpleDate startDate = createDate(startDateVal);
|
||||
SimpleDate endDate = createDate(endDateVal);
|
||||
int date1day = startDate.day;
|
||||
int date2day = endDate.day;
|
||||
|
||||
// basis zero has funny adjustments to the day-of-month fields when at end-of-month
|
||||
if (date1day == LONG_MONTH_LEN && date2day == LONG_MONTH_LEN) {
|
||||
date1day = SHORT_MONTH_LEN;
|
||||
date2day = SHORT_MONTH_LEN;
|
||||
} else if (date1day == LONG_MONTH_LEN) {
|
||||
date1day = SHORT_MONTH_LEN;
|
||||
} else if (date1day == SHORT_MONTH_LEN && date2day == LONG_MONTH_LEN) {
|
||||
date2day = SHORT_MONTH_LEN;
|
||||
// Note: If date2day==31, it STAYS 31 if date1day < 30.
|
||||
// Special fixes for February:
|
||||
} else if (startDate.month == 2 && isLastDayOfMonth(startDate)) {
|
||||
// Note - these assignments deliberately set Feb 30 date.
|
||||
date1day = SHORT_MONTH_LEN;
|
||||
if (endDate.month == 2 && isLastDayOfMonth(endDate)) {
|
||||
// only adjusted when first date is last day in Feb
|
||||
date2day = SHORT_MONTH_LEN;
|
||||
}
|
||||
}
|
||||
return calculateAdjusted(startDate, endDate, date1day, date2day);
|
||||
}
|
||||
/**
|
||||
* @param startDateVal assumed to be less than or equal to endDateVal
|
||||
* @param endDateVal assumed to be greater than or equal to startDateVal
|
||||
*/
|
||||
public static double basis1(int startDateVal, int endDateVal) {
|
||||
SimpleDate startDate = createDate(startDateVal);
|
||||
SimpleDate endDate = createDate(endDateVal);
|
||||
double yearLength;
|
||||
if (isGreaterThanOneYear(startDate, endDate)) {
|
||||
yearLength = averageYearLength(startDate.year, endDate.year);
|
||||
} else if (shouldCountFeb29(startDate, endDate)) {
|
||||
yearLength = DAYS_PER_LEAP_YEAR;
|
||||
} else {
|
||||
yearLength = DAYS_PER_NORMAL_YEAR;
|
||||
}
|
||||
return dateDiff(startDate.tsMilliseconds, endDate.tsMilliseconds) / yearLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param startDateVal assumed to be less than or equal to endDateVal
|
||||
* @param endDateVal assumed to be greater than or equal to startDateVal
|
||||
*/
|
||||
public static double basis2(int startDateVal, int endDateVal) {
|
||||
return (endDateVal - startDateVal) / 360.0;
|
||||
}
|
||||
/**
|
||||
* @param startDateVal assumed to be less than or equal to endDateVal
|
||||
* @param endDateVal assumed to be greater than or equal to startDateVal
|
||||
*/
|
||||
public static double basis3(double startDateVal, double endDateVal) {
|
||||
return (endDateVal - startDateVal) / 365.0;
|
||||
}
|
||||
/**
|
||||
* @param startDateVal assumed to be less than or equal to endDateVal
|
||||
* @param endDateVal assumed to be greater than or equal to startDateVal
|
||||
*/
|
||||
public static double basis4(int startDateVal, int endDateVal) {
|
||||
SimpleDate startDate = createDate(startDateVal);
|
||||
SimpleDate endDate = createDate(endDateVal);
|
||||
int date1day = startDate.day;
|
||||
int date2day = endDate.day;
|
||||
|
||||
|
||||
// basis four has funny adjustments to the day-of-month fields when at end-of-month
|
||||
if (date1day == LONG_MONTH_LEN) {
|
||||
date1day = SHORT_MONTH_LEN;
|
||||
}
|
||||
if (date2day == LONG_MONTH_LEN) {
|
||||
date2day = SHORT_MONTH_LEN;
|
||||
}
|
||||
// Note - no adjustments for end of Feb
|
||||
return calculateAdjusted(startDate, endDate, date1day, date2day);
|
||||
}
|
||||
|
||||
|
||||
private static double calculateAdjusted(SimpleDate startDate, SimpleDate endDate, int date1day,
|
||||
int date2day) {
|
||||
double dayCount
|
||||
= (endDate.year - startDate.year) * 360
|
||||
+ (endDate.month - startDate.month) * SHORT_MONTH_LEN
|
||||
+ (date2day - date1day) * 1;
|
||||
return dayCount / 360;
|
||||
}
|
||||
|
||||
private static boolean isLastDayOfMonth(SimpleDate date) {
|
||||
if (date.day < SHORT_FEB_LEN) {
|
||||
return false;
|
||||
}
|
||||
return date.day == getLastDayOfMonth(date);
|
||||
}
|
||||
|
||||
private static int getLastDayOfMonth(SimpleDate date) {
|
||||
switch (date.month) {
|
||||
case 1:
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
case 8:
|
||||
case 10:
|
||||
case 12:
|
||||
return LONG_MONTH_LEN;
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
return SHORT_MONTH_LEN;
|
||||
}
|
||||
if (isLeapYear(date.year)) {
|
||||
return LONG_FEB_LEN;
|
||||
}
|
||||
return SHORT_FEB_LEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes dates are no more than 1 year apart.
|
||||
* @return <code>true</code> if dates both within a leap year, or span a period including Feb 29
|
||||
*/
|
||||
private static boolean shouldCountFeb29(SimpleDate start, SimpleDate end) {
|
||||
boolean startIsLeapYear = isLeapYear(start.year);
|
||||
if (startIsLeapYear && start.year == end.year) {
|
||||
// note - dates may not actually span Feb-29, but it gets counted anyway in this case
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean endIsLeapYear = isLeapYear(end.year);
|
||||
if (!startIsLeapYear && !endIsLeapYear) {
|
||||
return false;
|
||||
}
|
||||
if (startIsLeapYear) {
|
||||
switch (start.month) {
|
||||
case SimpleDate.JANUARY:
|
||||
case SimpleDate.FEBRUARY:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (endIsLeapYear) {
|
||||
switch (end.month) {
|
||||
case SimpleDate.JANUARY:
|
||||
return false;
|
||||
case SimpleDate.FEBRUARY:
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
return end.day == LONG_FEB_LEN;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the whole number of days between the two time-stamps. Both time-stamps are
|
||||
* assumed to represent 12:00 midnight on the respective day.
|
||||
*/
|
||||
private static int dateDiff(long startDateMS, long endDateMS) {
|
||||
long msDiff = endDateMS - startDateMS;
|
||||
|
||||
// some extra checks to make sure we don't hide some other bug with the rounding
|
||||
int remainderHours = (int) ((msDiff % MS_PER_DAY) / MS_PER_HOUR);
|
||||
switch (remainderHours) {
|
||||
case 0: // normal case
|
||||
break;
|
||||
case 1: // transition from normal time to daylight savings adjusted
|
||||
case 23: // transition from daylight savings adjusted to normal time
|
||||
// Unexpected since we are using UTC_TIME_ZONE
|
||||
default:
|
||||
throw new RuntimeException("Unexpected date diff between " + startDateMS + " and " + endDateMS);
|
||||
|
||||
}
|
||||
return (int) (0.5 + ((double)msDiff / MS_PER_DAY));
|
||||
}
|
||||
|
||||
private static double averageYearLength(int startYear, int endYear) {
|
||||
int dayCount = 0;
|
||||
for (int i=startYear; i<=endYear; i++) {
|
||||
dayCount += DAYS_PER_NORMAL_YEAR;
|
||||
if (isLeapYear(i)) {
|
||||
dayCount++;
|
||||
}
|
||||
}
|
||||
double numberOfYears = endYear-startYear+1;
|
||||
return dayCount / numberOfYears;
|
||||
}
|
||||
|
||||
private static boolean isLeapYear(int i) {
|
||||
// leap years are always divisible by 4
|
||||
if (i % 4 != 0) {
|
||||
return false;
|
||||
}
|
||||
// each 4th century is a leap year
|
||||
if (i % 400 == 0) {
|
||||
return true;
|
||||
}
|
||||
// all other centuries are *not* leap years
|
||||
if (i % 100 == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isGreaterThanOneYear(SimpleDate start, SimpleDate end) {
|
||||
if (start.year == end.year) {
|
||||
return false;
|
||||
}
|
||||
if (start.year + 1 != end.year) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (start.month > end.month) {
|
||||
return false;
|
||||
}
|
||||
if (start.month < end.month) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return start.day < end.day;
|
||||
}
|
||||
|
||||
private static SimpleDate createDate(int dayCount) {
|
||||
GregorianCalendar calendar = new GregorianCalendar(UTC_TIME_ZONE);
|
||||
DateUtil.setCalendar(calendar, dayCount, 0, false);
|
||||
return new SimpleDate(calendar);
|
||||
}
|
||||
|
||||
private static final class SimpleDate {
|
||||
|
||||
public static final int JANUARY = 1;
|
||||
public static final int FEBRUARY = 2;
|
||||
|
||||
public final int year;
|
||||
/** 1-based month */
|
||||
public final int month;
|
||||
/** day of month */
|
||||
public final int day;
|
||||
/** milliseconds since 1970 */
|
||||
public long tsMilliseconds;
|
||||
|
||||
public SimpleDate(Calendar cal) {
|
||||
year = cal.get(Calendar.YEAR);
|
||||
month = cal.get(Calendar.MONTH) + 1;
|
||||
day = cal.get(Calendar.DAY_OF_MONTH);
|
||||
tsMilliseconds = cal.getTimeInMillis();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,93 +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.ss.formula.TwoDEval;
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public interface AreaEval extends TwoDEval {
|
||||
|
||||
/**
|
||||
* returns the 0-based index of the first row in
|
||||
* this area.
|
||||
*/
|
||||
int getFirstRow();
|
||||
|
||||
/**
|
||||
* returns the 0-based index of the last row in
|
||||
* this area.
|
||||
*/
|
||||
int getLastRow();
|
||||
|
||||
/**
|
||||
* returns the 0-based index of the first col in
|
||||
* this area.
|
||||
*/
|
||||
int getFirstColumn();
|
||||
|
||||
/**
|
||||
* returns the 0-based index of the last col in
|
||||
* this area.
|
||||
*/
|
||||
int getLastColumn();
|
||||
|
||||
/**
|
||||
* @return the ValueEval from within this area at the specified row and col index. Never
|
||||
* <code>null</code> (possibly {@link BlankEval}). The specified indexes should be absolute
|
||||
* indexes in the sheet and not relative indexes within the area.
|
||||
*/
|
||||
ValueEval getAbsoluteValue(int row, int col);
|
||||
|
||||
/**
|
||||
* returns true if the cell at row and col specified
|
||||
* as absolute indexes in the sheet is contained in
|
||||
* this area.
|
||||
* @param row
|
||||
* @param col
|
||||
*/
|
||||
boolean contains(int row, int col);
|
||||
|
||||
/**
|
||||
* returns true if the specified col is in range
|
||||
* @param col
|
||||
*/
|
||||
boolean containsColumn(int col);
|
||||
|
||||
/**
|
||||
* returns true if the specified row is in range
|
||||
* @param row
|
||||
*/
|
||||
boolean containsRow(int row);
|
||||
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
/**
|
||||
* @return the ValueEval from within this area at the specified relativeRowIndex and
|
||||
* relativeColumnIndex. Never <code>null</code> (possibly {@link BlankEval}). The
|
||||
* 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);
|
||||
}
|
@ -1,117 +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.AreaI;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public abstract class AreaEvalBase implements AreaEval {
|
||||
|
||||
private final int _firstColumn;
|
||||
private final int _firstRow;
|
||||
private final int _lastColumn;
|
||||
private final int _lastRow;
|
||||
private final int _nColumns;
|
||||
private final int _nRows;
|
||||
|
||||
protected AreaEvalBase(int firstRow, int firstColumn, int lastRow, int lastColumn) {
|
||||
_firstColumn = firstColumn;
|
||||
_firstRow = firstRow;
|
||||
_lastColumn = lastColumn;
|
||||
_lastRow = lastRow;
|
||||
|
||||
_nColumns = _lastColumn - _firstColumn + 1;
|
||||
_nRows = _lastRow - _firstRow + 1;
|
||||
}
|
||||
|
||||
protected AreaEvalBase(AreaI ptg) {
|
||||
_firstRow = ptg.getFirstRow();
|
||||
_firstColumn = ptg.getFirstColumn();
|
||||
_lastRow = ptg.getLastRow();
|
||||
_lastColumn = ptg.getLastColumn();
|
||||
|
||||
_nColumns = _lastColumn - _firstColumn + 1;
|
||||
_nRows = _lastRow - _firstRow + 1;
|
||||
}
|
||||
|
||||
public final int getFirstColumn() {
|
||||
return _firstColumn;
|
||||
}
|
||||
|
||||
public final int getFirstRow() {
|
||||
return _firstRow;
|
||||
}
|
||||
|
||||
public final int getLastColumn() {
|
||||
return _lastColumn;
|
||||
}
|
||||
|
||||
public final int getLastRow() {
|
||||
return _lastRow;
|
||||
}
|
||||
public final ValueEval getAbsoluteValue(int row, int col) {
|
||||
int rowOffsetIx = row - _firstRow;
|
||||
int colOffsetIx = col - _firstColumn;
|
||||
|
||||
if(rowOffsetIx < 0 || rowOffsetIx >= _nRows) {
|
||||
throw new IllegalArgumentException("Specified row index (" + row
|
||||
+ ") is outside the allowed range (" + _firstRow + ".." + _lastRow + ")");
|
||||
}
|
||||
if(colOffsetIx < 0 || colOffsetIx >= _nColumns) {
|
||||
throw new IllegalArgumentException("Specified column index (" + col
|
||||
+ ") is outside the allowed range (" + _firstColumn + ".." + col + ")");
|
||||
}
|
||||
return getRelativeValue(rowOffsetIx, colOffsetIx);
|
||||
}
|
||||
|
||||
public final boolean contains(int row, int col) {
|
||||
return _firstRow <= row && _lastRow >= row
|
||||
&& _firstColumn <= col && _lastColumn >= col;
|
||||
}
|
||||
|
||||
public final boolean containsRow(int row) {
|
||||
return _firstRow <= row && _lastRow >= row;
|
||||
}
|
||||
|
||||
public final boolean containsColumn(int col) {
|
||||
return _firstColumn <= col && _lastColumn >= col;
|
||||
}
|
||||
|
||||
public final boolean isColumn() {
|
||||
return _firstColumn == _lastColumn;
|
||||
}
|
||||
|
||||
public final boolean isRow() {
|
||||
return _firstRow == _lastRow;
|
||||
}
|
||||
public int getHeight() {
|
||||
return _lastRow-_firstRow+1;
|
||||
}
|
||||
|
||||
public final ValueEval getValue(int row, int col) {
|
||||
return getRelativeValue(row, col);
|
||||
}
|
||||
|
||||
public abstract ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex);
|
||||
|
||||
public int getWidth() {
|
||||
return _lastColumn-_firstColumn+1;
|
||||
}
|
||||
}
|
@ -1,35 +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;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com > This class is a
|
||||
* marker class. It is a special value for empty cells.
|
||||
*/
|
||||
public final class BlankEval implements ValueEval {
|
||||
|
||||
public static final BlankEval instance = new BlankEval();
|
||||
/**
|
||||
* @deprecated (Nov 2009) use {@link #instance}
|
||||
*/
|
||||
public static final BlankEval INSTANCE = instance;
|
||||
|
||||
private BlankEval() {
|
||||
// enforce singleton
|
||||
}
|
||||
}
|
@ -1,64 +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;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public final class BoolEval implements NumericValueEval, StringValueEval {
|
||||
|
||||
private boolean _value;
|
||||
|
||||
public static final BoolEval FALSE = new BoolEval(false);
|
||||
|
||||
public static final BoolEval TRUE = new BoolEval(true);
|
||||
|
||||
/**
|
||||
* Convenience method for the following:<br/>
|
||||
* <code>(b ? BoolEval.TRUE : BoolEval.FALSE)</code>
|
||||
*
|
||||
* @return the <tt>BoolEval</tt> instance representing <tt>b</tt>.
|
||||
*/
|
||||
public static final BoolEval valueOf(boolean b) {
|
||||
return b ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
private BoolEval(boolean value) {
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public boolean getBooleanValue() {
|
||||
return _value;
|
||||
}
|
||||
|
||||
public double getNumberValue() {
|
||||
return _value ? 1 : 0;
|
||||
}
|
||||
|
||||
public String getStringValue() {
|
||||
return _value ? "TRUE" : "FALSE";
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(getStringValue());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -1,60 +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.functions.Fixed2ArgFunction;
|
||||
import org.apache.poi.hssf.record.formula.functions.Function;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public final class ConcatEval extends Fixed2ArgFunction {
|
||||
|
||||
public static final Function instance = new ConcatEval();
|
||||
|
||||
private ConcatEval() {
|
||||
// enforce singleton
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
ValueEval ve0;
|
||||
ValueEval ve1;
|
||||
try {
|
||||
ve0 = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
ve1 = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getText(ve0));
|
||||
sb.append(getText(ve1));
|
||||
return new StringEval(sb.toString());
|
||||
}
|
||||
|
||||
private Object getText(ValueEval ve) {
|
||||
if (ve instanceof StringValueEval) {
|
||||
StringValueEval sve = (StringValueEval) ve;
|
||||
return sve.getStringValue();
|
||||
}
|
||||
if (ve == BlankEval.instance) {
|
||||
return "";
|
||||
}
|
||||
throw new IllegalAccessError("Unexpected value type ("
|
||||
+ ve.getClass().getName() + ")");
|
||||
}
|
||||
}
|
@ -1,111 +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.ss.usermodel.ErrorConstants;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public final class ErrorEval implements ValueEval {
|
||||
|
||||
// convenient access to namespace
|
||||
private static final ErrorConstants EC = null;
|
||||
|
||||
/** <b>#NULL!</b> - Intersection of two cell ranges is empty */
|
||||
public static final ErrorEval NULL_INTERSECTION = new ErrorEval(EC.ERROR_NULL);
|
||||
/** <b>#DIV/0!</b> - Division by zero */
|
||||
public static final ErrorEval DIV_ZERO = new ErrorEval(EC.ERROR_DIV_0);
|
||||
/** <b>#VALUE!</b> - Wrong type of operand */
|
||||
public static final ErrorEval VALUE_INVALID = new ErrorEval(EC.ERROR_VALUE);
|
||||
/** <b>#REF!</b> - Illegal or deleted cell reference */
|
||||
public static final ErrorEval REF_INVALID = new ErrorEval(EC.ERROR_REF);
|
||||
/** <b>#NAME?</b> - Wrong function or range name */
|
||||
public static final ErrorEval NAME_INVALID = new ErrorEval(EC.ERROR_NAME);
|
||||
/** <b>#NUM!</b> - Value range overflow */
|
||||
public static final ErrorEval NUM_ERROR = new ErrorEval(EC.ERROR_NUM);
|
||||
/** <b>#N/A</b> - Argument or function not available */
|
||||
public static final ErrorEval NA = new ErrorEval(EC.ERROR_NA);
|
||||
|
||||
|
||||
// POI internal error codes
|
||||
private static final int CIRCULAR_REF_ERROR_CODE = 0xFFFFFFC4;
|
||||
private static final int FUNCTION_NOT_IMPLEMENTED_CODE = 0xFFFFFFE2;
|
||||
|
||||
// Note - Excel does not seem to represent this condition with an error code
|
||||
public static final ErrorEval CIRCULAR_REF_ERROR = new ErrorEval(CIRCULAR_REF_ERROR_CODE);
|
||||
|
||||
|
||||
/**
|
||||
* Translates an Excel internal error code into the corresponding POI ErrorEval instance
|
||||
* @param errorCode
|
||||
*/
|
||||
public static ErrorEval valueOf(int errorCode) {
|
||||
switch(errorCode) {
|
||||
case ErrorConstants.ERROR_NULL: return NULL_INTERSECTION;
|
||||
case ErrorConstants.ERROR_DIV_0: return DIV_ZERO;
|
||||
case ErrorConstants.ERROR_VALUE: return VALUE_INVALID;
|
||||
case ErrorConstants.ERROR_REF: return REF_INVALID;
|
||||
case ErrorConstants.ERROR_NAME: return NAME_INVALID;
|
||||
case ErrorConstants.ERROR_NUM: return NUM_ERROR;
|
||||
case ErrorConstants.ERROR_NA: return NA;
|
||||
// non-std errors (conditions modeled as errors by POI)
|
||||
case CIRCULAR_REF_ERROR_CODE: return CIRCULAR_REF_ERROR;
|
||||
}
|
||||
throw new RuntimeException("Unexpected error code (" + errorCode + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts error codes to text. Handles non-standard error codes OK.
|
||||
* For debug/test purposes (and for formatting error messages).
|
||||
* @return the String representation of the specified Excel error code.
|
||||
*/
|
||||
public static String getText(int errorCode) {
|
||||
if(ErrorConstants.isValidCode(errorCode)) {
|
||||
return ErrorConstants.getText(errorCode);
|
||||
}
|
||||
// It is desirable to make these (arbitrary) strings look clearly different from any other
|
||||
// value expression that might appear in a formula. In addition these error strings should
|
||||
// look unlike the standard Excel errors. Hence tilde ('~') was used.
|
||||
switch(errorCode) {
|
||||
case CIRCULAR_REF_ERROR_CODE: return "~CIRCULAR~REF~";
|
||||
case FUNCTION_NOT_IMPLEMENTED_CODE: return "~FUNCTION~NOT~IMPLEMENTED~";
|
||||
}
|
||||
return "~non~std~err(" + errorCode + ")~";
|
||||
}
|
||||
|
||||
private int _errorCode;
|
||||
/**
|
||||
* @param errorCode an 8-bit value
|
||||
*/
|
||||
private ErrorEval(int errorCode) {
|
||||
_errorCode = errorCode;
|
||||
}
|
||||
|
||||
public int getErrorCode() {
|
||||
return _errorCode;
|
||||
}
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(getText(_errorCode));
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -1,134 +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;
|
||||
|
||||
/**
|
||||
* This class is used to simplify error handling logic <i>within</i> operator and function
|
||||
* implementations. Note - <tt>OperationEval.evaluate()</tt> and <tt>Function.evaluate()</tt>
|
||||
* method signatures do not throw this exception so it cannot propagate outside.<p/>
|
||||
*
|
||||
* Here is an example coded without <tt>EvaluationException</tt>, to show how it can help:
|
||||
* <pre>
|
||||
* public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
* // ...
|
||||
* Eval arg0 = args[0];
|
||||
* if(arg0 instanceof ErrorEval) {
|
||||
* return arg0;
|
||||
* }
|
||||
* if(!(arg0 instanceof AreaEval)) {
|
||||
* return ErrorEval.VALUE_INVALID;
|
||||
* }
|
||||
* double temp = 0;
|
||||
* AreaEval area = (AreaEval)arg0;
|
||||
* ValueEval[] values = area.getValues();
|
||||
* for (int i = 0; i < values.length; i++) {
|
||||
* ValueEval ve = values[i];
|
||||
* if(ve instanceof ErrorEval) {
|
||||
* return ve;
|
||||
* }
|
||||
* if(!(ve instanceof NumericValueEval)) {
|
||||
* return ErrorEval.VALUE_INVALID;
|
||||
* }
|
||||
* temp += ((NumericValueEval)ve).getNumberValue();
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* In this example, if any error is encountered while processing the arguments, an error is
|
||||
* returned immediately. This code is difficult to refactor due to all the points where errors
|
||||
* are returned.<br/>
|
||||
* Using <tt>EvaluationException</tt> allows the error returning code to be consolidated to one
|
||||
* place.<p/>
|
||||
* <pre>
|
||||
* public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
* try {
|
||||
* // ...
|
||||
* AreaEval area = getAreaArg(args[0]);
|
||||
* double temp = sumValues(area.getValues());
|
||||
* // ...
|
||||
* } catch (EvaluationException e) {
|
||||
* return e.getErrorEval();
|
||||
* }
|
||||
*}
|
||||
*
|
||||
*private static AreaEval getAreaArg(Eval arg0) throws EvaluationException {
|
||||
* if (arg0 instanceof ErrorEval) {
|
||||
* throw new EvaluationException((ErrorEval) arg0);
|
||||
* }
|
||||
* if (arg0 instanceof AreaEval) {
|
||||
* return (AreaEval) arg0;
|
||||
* }
|
||||
* throw EvaluationException.invalidValue();
|
||||
*}
|
||||
*
|
||||
*private double sumValues(ValueEval[] values) throws EvaluationException {
|
||||
* double temp = 0;
|
||||
* for (int i = 0; i < values.length; i++) {
|
||||
* ValueEval ve = values[i];
|
||||
* if (ve instanceof ErrorEval) {
|
||||
* throw new EvaluationException((ErrorEval) ve);
|
||||
* }
|
||||
* if (!(ve instanceof NumericValueEval)) {
|
||||
* throw EvaluationException.invalidValue();
|
||||
* }
|
||||
* temp += ((NumericValueEval) ve).getNumberValue();
|
||||
* }
|
||||
* return temp;
|
||||
*}
|
||||
* </pre>
|
||||
* It is not mandatory to use EvaluationException, doing so might give the following advantages:<br/>
|
||||
* - Methods can more easily be extracted, allowing for re-use.<br/>
|
||||
* - Type management (typecasting etc) is simpler because error conditions have been separated from
|
||||
* intermediate calculation values.<br/>
|
||||
* - Fewer local variables are required. Local variables can have stronger types.<br/>
|
||||
* - It is easier to mimic common Excel error handling behaviour (exit upon encountering first
|
||||
* error), because exceptions conveniently propagate up the call stack regardless of execution
|
||||
* points or the number of levels of nested calls.<p/>
|
||||
*
|
||||
* <b>Note</b> - Only standard evaluation errors are represented by <tt>EvaluationException</tt> (
|
||||
* i.e. conditions expected to be encountered when evaluating arbitrary Excel formulas). Conditions
|
||||
* that could never occur in an Excel spreadsheet should result in runtime exceptions. Care should
|
||||
* be taken to not translate any POI internal error into an Excel evaluation error code.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class EvaluationException extends Exception {
|
||||
private final ErrorEval _errorEval;
|
||||
|
||||
public EvaluationException(ErrorEval errorEval) {
|
||||
_errorEval = errorEval;
|
||||
}
|
||||
// some convenience factory methods
|
||||
|
||||
/** <b>#VALUE!</b> - Wrong type of operand */
|
||||
public static EvaluationException invalidValue() {
|
||||
return new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
/** <b>#REF!</b> - Illegal or deleted cell reference */
|
||||
public static EvaluationException invalidRef() {
|
||||
return new EvaluationException(ErrorEval.REF_INVALID);
|
||||
}
|
||||
/** <b>#NUM!</b> - Value range overflow */
|
||||
public static EvaluationException numberError() {
|
||||
return new EvaluationException(ErrorEval.NUM_ERROR);
|
||||
}
|
||||
|
||||
public ErrorEval getErrorEval() {
|
||||
return _errorEval;
|
||||
}
|
||||
}
|
@ -1,250 +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.function.FunctionMetadata;
|
||||
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
|
||||
import org.apache.poi.hssf.record.formula.functions.*;
|
||||
import org.apache.poi.ss.formula.eval.NotImplementedException;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public final class FunctionEval {
|
||||
/**
|
||||
* Some function IDs that require special treatment
|
||||
*/
|
||||
private static final class FunctionID {
|
||||
/** 1 */
|
||||
public static final int IF = FunctionMetadataRegistry.FUNCTION_INDEX_IF;
|
||||
/** 4 */
|
||||
public static final int SUM = FunctionMetadataRegistry.FUNCTION_INDEX_SUM;
|
||||
/** 78 */
|
||||
public static final int OFFSET = 78;
|
||||
/** 100 */
|
||||
public static final int CHOOSE = FunctionMetadataRegistry.FUNCTION_INDEX_CHOOSE;
|
||||
/** 148 */
|
||||
public static final int INDIRECT = FunctionMetadataRegistry.FUNCTION_INDEX_INDIRECT;
|
||||
/** 255 */
|
||||
public static final int EXTERNAL_FUNC = FunctionMetadataRegistry.FUNCTION_INDEX_EXTERNAL;
|
||||
}
|
||||
// convenient access to namespace
|
||||
private static final FunctionID ID = null;
|
||||
|
||||
/**
|
||||
* Array elements corresponding to unimplemented functions are <code>null</code>
|
||||
*/
|
||||
protected static final Function[] functions = produceFunctions();
|
||||
|
||||
private static Function[] produceFunctions() {
|
||||
Function[] retval = new Function[368];
|
||||
|
||||
retval[0] = new Count();
|
||||
retval[ID.IF] = new IfFunc();
|
||||
retval[2] = LogicalFunction.ISNA;
|
||||
retval[3] = LogicalFunction.ISERROR;
|
||||
retval[ID.SUM] = AggregateFunction.SUM;
|
||||
retval[5] = AggregateFunction.AVERAGE;
|
||||
retval[6] = AggregateFunction.MIN;
|
||||
retval[7] = AggregateFunction.MAX;
|
||||
retval[8] = new RowFunc(); // ROW
|
||||
retval[9] = new Column();
|
||||
retval[10] = new Na();
|
||||
retval[11] = new Npv();
|
||||
retval[12] = AggregateFunction.STDEV;
|
||||
retval[13] = NumericFunction.DOLLAR;
|
||||
|
||||
retval[15] = NumericFunction.SIN;
|
||||
retval[16] = NumericFunction.COS;
|
||||
retval[17] = NumericFunction.TAN;
|
||||
retval[18] = NumericFunction.ATAN;
|
||||
retval[19] = NumericFunction.PI;
|
||||
retval[20] = NumericFunction.SQRT;
|
||||
retval[21] = NumericFunction.EXP;
|
||||
retval[22] = NumericFunction.LN;
|
||||
retval[23] = NumericFunction.LOG10;
|
||||
retval[24] = NumericFunction.ABS;
|
||||
retval[25] = NumericFunction.INT;
|
||||
retval[26] = NumericFunction.SIGN;
|
||||
retval[27] = NumericFunction.ROUND;
|
||||
retval[28] = new Lookup();
|
||||
retval[29] = new Index();
|
||||
|
||||
retval[31] = TextFunction.MID;
|
||||
retval[32] = TextFunction.LEN;
|
||||
retval[33] = new Value();
|
||||
retval[34] = BooleanFunction.TRUE;
|
||||
retval[35] = BooleanFunction.FALSE;
|
||||
retval[36] = BooleanFunction.AND;
|
||||
retval[37] = BooleanFunction.OR;
|
||||
retval[38] = BooleanFunction.NOT;
|
||||
retval[39] = NumericFunction.MOD;
|
||||
retval[48] = TextFunction.TEXT;
|
||||
|
||||
retval[56] = FinanceFunction.PV;
|
||||
retval[57] = FinanceFunction.FV;
|
||||
retval[58] = FinanceFunction.NPER;
|
||||
retval[59] = FinanceFunction.PMT;
|
||||
|
||||
retval[63] = NumericFunction.RAND;
|
||||
retval[64] = new Match();
|
||||
retval[65] = DateFunc.instance;
|
||||
retval[66] = new TimeFunc();
|
||||
retval[67] = CalendarFieldFunction.DAY;
|
||||
retval[68] = CalendarFieldFunction.MONTH;
|
||||
retval[69] = CalendarFieldFunction.YEAR;
|
||||
|
||||
retval[74] = new Now();
|
||||
|
||||
retval[76] = new Rows();
|
||||
retval[77] = new Columns();
|
||||
retval[82] = TextFunction.SEARCH;
|
||||
retval[ID.OFFSET] = new Offset();
|
||||
retval[82] = TextFunction.SEARCH;
|
||||
|
||||
retval[97] = NumericFunction.ATAN2;
|
||||
retval[98] = NumericFunction.ASIN;
|
||||
retval[99] = NumericFunction.ACOS;
|
||||
retval[ID.CHOOSE] = new Choose();
|
||||
retval[101] = new Hlookup();
|
||||
retval[102] = new Vlookup();
|
||||
|
||||
retval[105] = LogicalFunction.ISREF;
|
||||
|
||||
retval[109] = NumericFunction.LOG;
|
||||
|
||||
retval[112] = TextFunction.LOWER;
|
||||
retval[113] = TextFunction.UPPER;
|
||||
|
||||
retval[115] = TextFunction.LEFT;
|
||||
retval[116] = TextFunction.RIGHT;
|
||||
retval[117] = TextFunction.EXACT;
|
||||
retval[118] = TextFunction.TRIM;
|
||||
retval[119] = new Replace();
|
||||
retval[120] = new Substitute();
|
||||
|
||||
retval[124] = TextFunction.FIND;
|
||||
|
||||
retval[127] = LogicalFunction.ISTEXT;
|
||||
retval[128] = LogicalFunction.ISNUMBER;
|
||||
retval[129] = LogicalFunction.ISBLANK;
|
||||
retval[130] = new T();
|
||||
|
||||
retval[ID.INDIRECT] = null; // Indirect.evaluate has different signature
|
||||
|
||||
retval[169] = new Counta();
|
||||
|
||||
retval[183] = AggregateFunction.PRODUCT;
|
||||
retval[184] = NumericFunction.FACT;
|
||||
|
||||
retval[190] = LogicalFunction.ISNONTEXT;
|
||||
retval[197] = NumericFunction.TRUNC;
|
||||
retval[198] = LogicalFunction.ISLOGICAL;
|
||||
|
||||
retval[212] = NumericFunction.ROUNDUP;
|
||||
retval[213] = NumericFunction.ROUNDDOWN;
|
||||
|
||||
retval[220] = new Days360();
|
||||
retval[221] = new Today();
|
||||
|
||||
retval[227] = AggregateFunction.MEDIAN;
|
||||
retval[228] = new Sumproduct();
|
||||
retval[229] = NumericFunction.SINH;
|
||||
retval[230] = NumericFunction.COSH;
|
||||
retval[231] = NumericFunction.TANH;
|
||||
retval[232] = NumericFunction.ASINH;
|
||||
retval[233] = NumericFunction.ACOSH;
|
||||
retval[234] = NumericFunction.ATANH;
|
||||
|
||||
retval[ID.EXTERNAL_FUNC] = null; // ExternalFunction is a FreeREfFunction
|
||||
|
||||
retval[261] = new Errortype();
|
||||
|
||||
retval[269] = AggregateFunction.AVEDEV;
|
||||
|
||||
retval[276] = NumericFunction.COMBIN;
|
||||
|
||||
retval[279] = new Even();
|
||||
|
||||
retval[285] = NumericFunction.FLOOR;
|
||||
|
||||
retval[288] = NumericFunction.CEILING;
|
||||
|
||||
retval[298] = new Odd();
|
||||
|
||||
retval[300] = NumericFunction.POISSON;
|
||||
|
||||
retval[303] = new Sumxmy2();
|
||||
retval[304] = new Sumx2my2();
|
||||
retval[305] = new Sumx2py2();
|
||||
|
||||
retval[318] = AggregateFunction.DEVSQ;
|
||||
|
||||
retval[321] = AggregateFunction.SUMSQ;
|
||||
|
||||
retval[325] = AggregateFunction.LARGE;
|
||||
retval[326] = AggregateFunction.SMALL;
|
||||
|
||||
retval[330] = new Mode();
|
||||
|
||||
retval[336] = TextFunction.CONCATENATE;
|
||||
retval[337] = NumericFunction.POWER;
|
||||
|
||||
retval[342] = NumericFunction.RADIANS;
|
||||
retval[343] = NumericFunction.DEGREES;
|
||||
|
||||
retval[344] = new Subtotal();
|
||||
retval[345] = new Sumif();
|
||||
retval[346] = new Countif();
|
||||
retval[347] = new Countblank();
|
||||
|
||||
retval[359] = new Hyperlink();
|
||||
|
||||
retval[362] = MinaMaxa.MAXA;
|
||||
retval[363] = MinaMaxa.MINA;
|
||||
|
||||
for (int i = 0; i < retval.length; i++) {
|
||||
Function f = retval[i];
|
||||
if (f == null) {
|
||||
FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(i);
|
||||
if (fm == null) {
|
||||
continue;
|
||||
}
|
||||
retval[i] = new NotImplementedFunction(fm.getName());
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
/**
|
||||
* @return <code>null</code> if the specified functionIndex is for INDIRECT() or any external (add-in) function.
|
||||
*/
|
||||
public static Function getBasicFunction(int functionIndex) {
|
||||
// check for 'free ref' functions first
|
||||
switch (functionIndex) {
|
||||
case FunctionID.INDIRECT:
|
||||
case FunctionID.EXTERNAL_FUNC:
|
||||
return null;
|
||||
}
|
||||
// else - must be plain function
|
||||
Function result = functions[functionIndex];
|
||||
if (result == null) {
|
||||
throw new NotImplementedException("FuncIx=" + functionIndex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,96 +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.functions.Fixed2ArgFunction;
|
||||
import org.apache.poi.hssf.record.formula.functions.Function;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class IntersectionEval extends Fixed2ArgFunction {
|
||||
|
||||
public static final Function instance = new IntersectionEval();
|
||||
|
||||
private IntersectionEval() {
|
||||
// enforces singleton
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
|
||||
try {
|
||||
AreaEval reA = evaluateRef(arg0);
|
||||
AreaEval reB = evaluateRef(arg1);
|
||||
AreaEval result = resolveRange(reA, reB);
|
||||
if (result == null) {
|
||||
return ErrorEval.NULL_INTERSECTION;
|
||||
}
|
||||
return result;
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return simple rectangular {@link AreaEval} which represents the intersection of areas
|
||||
* <tt>aeA</tt> and <tt>aeB</tt>. If the two areas do not intersect, the result is <code>null</code>.
|
||||
*/
|
||||
private static AreaEval resolveRange(AreaEval aeA, AreaEval aeB) {
|
||||
|
||||
int aeAfr = aeA.getFirstRow();
|
||||
int aeAfc = aeA.getFirstColumn();
|
||||
int aeBlc = aeB.getLastColumn();
|
||||
if (aeAfc > aeBlc) {
|
||||
return null;
|
||||
}
|
||||
int aeBfc = aeB.getFirstColumn();
|
||||
if (aeBfc > aeA.getLastColumn()) {
|
||||
return null;
|
||||
}
|
||||
int aeBlr = aeB.getLastRow();
|
||||
if (aeAfr > aeBlr) {
|
||||
return null;
|
||||
}
|
||||
int aeBfr = aeB.getFirstRow();
|
||||
int aeAlr = aeA.getLastRow();
|
||||
if (aeBfr > aeAlr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
int top = Math.max(aeAfr, aeBfr);
|
||||
int bottom = Math.min(aeAlr, aeBlr);
|
||||
int left = Math.max(aeAfc, aeBfc);
|
||||
int right = Math.min(aeA.getLastColumn(), aeBlc);
|
||||
|
||||
return aeA.offset(top-aeAfr, bottom-aeAfr, left-aeAfc, right-aeAfc);
|
||||
}
|
||||
|
||||
private static AreaEval evaluateRef(ValueEval arg) throws EvaluationException {
|
||||
if (arg instanceof AreaEval) {
|
||||
return (AreaEval) arg;
|
||||
}
|
||||
if (arg instanceof RefEval) {
|
||||
return ((RefEval) arg).offset(0, 0, 0, 0);
|
||||
}
|
||||
if (arg instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval)arg);
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected ref arg class (" + arg.getClass().getName() + ")");
|
||||
}
|
||||
}
|
@ -1,36 +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;
|
||||
|
||||
/**
|
||||
* Represents the (intermediate) evaluated result of a missing function argument. In most cases
|
||||
* this can be translated into {@link BlankEval} but there are some notable exceptions. Functions
|
||||
* COUNT and COUNTA <em>do</em> count their missing args. Note - the differences between
|
||||
* {@link MissingArgEval} and {@link BlankEval} have not been investigated fully, so the POI
|
||||
* evaluator may need to be updated to account for these as they are found.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class MissingArgEval implements ValueEval {
|
||||
|
||||
public static final MissingArgEval instance = new MissingArgEval();
|
||||
|
||||
private MissingArgEval() {
|
||||
// enforce singleton
|
||||
}
|
||||
}
|
@ -1,46 +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;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class NameEval implements ValueEval {
|
||||
|
||||
private final String _functionName;
|
||||
|
||||
/**
|
||||
* Creates a NameEval representing a function name
|
||||
*/
|
||||
public NameEval(String functionName) {
|
||||
_functionName = functionName;
|
||||
}
|
||||
|
||||
|
||||
public String getFunctionName() {
|
||||
return _functionName;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(_functionName);
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -1,44 +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.NameXPtg;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class NameXEval implements ValueEval {
|
||||
|
||||
private final NameXPtg _ptg;
|
||||
|
||||
public NameXEval(NameXPtg ptg) {
|
||||
_ptg = ptg;
|
||||
}
|
||||
|
||||
public NameXPtg getPtg() {
|
||||
return _ptg;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(_ptg.getSheetRefIndex()).append(", ").append(_ptg.getNameIndex());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -1,73 +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.
|
||||
*/
|
||||
/*
|
||||
* Created on May 8, 2005
|
||||
*
|
||||
*/
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.IntPtg;
|
||||
import org.apache.poi.hssf.record.formula.NumberPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.ss.util.NumberToTextConverter;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public final class NumberEval implements NumericValueEval, StringValueEval {
|
||||
|
||||
public static final NumberEval ZERO = new NumberEval(0);
|
||||
|
||||
private final double _value;
|
||||
private String _stringValue;
|
||||
|
||||
public NumberEval(Ptg ptg) {
|
||||
if (ptg == null) {
|
||||
throw new IllegalArgumentException("ptg must not be null");
|
||||
}
|
||||
if (ptg instanceof IntPtg) {
|
||||
_value = ((IntPtg) ptg).getValue();
|
||||
} else if (ptg instanceof NumberPtg) {
|
||||
_value = ((NumberPtg) ptg).getValue();
|
||||
} else {
|
||||
throw new IllegalArgumentException("bad argument type (" + ptg.getClass().getName() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
public NumberEval(double value) {
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public double getNumberValue() {
|
||||
return _value;
|
||||
}
|
||||
|
||||
public String getStringValue() {
|
||||
if (_stringValue == null) {
|
||||
_stringValue = NumberToTextConverter.toText(_value);
|
||||
}
|
||||
return _stringValue;
|
||||
}
|
||||
public final String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(getStringValue());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -1,30 +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.
|
||||
*/
|
||||
/*
|
||||
* Created on May 8, 2005
|
||||
*
|
||||
*/
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public interface NumericValueEval extends ValueEval {
|
||||
|
||||
public abstract double getNumberValue();
|
||||
}
|
@ -1,324 +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 java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Provides functionality for evaluating arguments to functions and operators.
|
||||
*
|
||||
* @author Josh Micich
|
||||
* @author Brendan Nolan
|
||||
*/
|
||||
public final class OperandResolver {
|
||||
|
||||
// Based on regular expression defined in JavaDoc at {@link java.lang.Double#valueOf}
|
||||
// modified to remove support for NaN, Infinity, Hexadecimal support and floating type suffixes
|
||||
private static final String Digits = "(\\p{Digit}+)";
|
||||
private static final String Exp = "[eE][+-]?"+Digits;
|
||||
private static final String fpRegex =
|
||||
("[\\x00-\\x20]*" +
|
||||
"[+-]?(" +
|
||||
"((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+
|
||||
"(\\.("+Digits+")("+Exp+")?))))"+
|
||||
"[\\x00-\\x20]*");
|
||||
|
||||
|
||||
private OperandResolver() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a single value from a variety of different argument types according to standard
|
||||
* Excel rules. Does not perform any type conversion.
|
||||
* @param arg the evaluated argument as passed to the function or operator.
|
||||
* @param srcCellRow used when arg is a single column AreaRef
|
||||
* @param srcCellCol used when arg is a single row AreaRef
|
||||
* @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt> or <tt>BlankEval</tt>.
|
||||
* Never <code>null</code> or <tt>ErrorEval</tt>.
|
||||
* @throws EvaluationException(#VALUE!) if srcCellRow or srcCellCol do not properly index into
|
||||
* an AreaEval. If the actual value retrieved is an ErrorEval, a corresponding
|
||||
* EvaluationException is thrown.
|
||||
*/
|
||||
public static ValueEval getSingleValue(ValueEval arg, int srcCellRow, int srcCellCol)
|
||||
throws EvaluationException {
|
||||
ValueEval result;
|
||||
if (arg instanceof RefEval) {
|
||||
result = ((RefEval) arg).getInnerValueEval();
|
||||
} else if (arg instanceof AreaEval) {
|
||||
result = chooseSingleElementFromArea((AreaEval) arg, srcCellRow, srcCellCol);
|
||||
} else {
|
||||
result = arg;
|
||||
}
|
||||
if (result instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements (some perhaps not well known) Excel functionality to select a single cell from an
|
||||
* area depending on the coordinates of the calling cell. Here is an example demonstrating
|
||||
* both selection from a single row area and a single column area in the same formula.
|
||||
*
|
||||
* <table border="1" cellpadding="1" cellspacing="1" summary="sample spreadsheet">
|
||||
* <tr><th> </th><th> A </th><th> B </th><th> C </th><th> D </th></tr>
|
||||
* <tr><th>1</th><td>15</td><td>20</td><td>25</td><td> </td></tr>
|
||||
* <tr><th>2</th><td> </td><td> </td><td> </td><td>200</td></tr>
|
||||
* <tr><th>3</th><td> </td><td> </td><td> </td><td>300</td></tr>
|
||||
* <tr><th>3</th><td> </td><td> </td><td> </td><td>400</td></tr>
|
||||
* </table>
|
||||
*
|
||||
* If the formula "=1000+A1:B1+D2:D3" is put into the 9 cells from A2 to C4, the spreadsheet
|
||||
* will look like this:
|
||||
*
|
||||
* <table border="1" cellpadding="1" cellspacing="1" summary="sample spreadsheet">
|
||||
* <tr><th> </th><th> A </th><th> B </th><th> C </th><th> D </th></tr>
|
||||
* <tr><th>1</th><td>15</td><td>20</td><td>25</td><td> </td></tr>
|
||||
* <tr><th>2</th><td>1215</td><td>1220</td><td>#VALUE!</td><td>200</td></tr>
|
||||
* <tr><th>3</th><td>1315</td><td>1320</td><td>#VALUE!</td><td>300</td></tr>
|
||||
* <tr><th>4</th><td>#VALUE!</td><td>#VALUE!</td><td>#VALUE!</td><td>400</td></tr>
|
||||
* </table>
|
||||
*
|
||||
* Note that the row area (A1:B1) does not include column C and the column area (D2:D3) does
|
||||
* not include row 4, so the values in C1(=25) and D4(=400) are not accessible to the formula
|
||||
* as written, but in the 4 cells A2:B3, the row and column selection works ok.<p/>
|
||||
*
|
||||
* The same concept is extended to references across sheets, such that even multi-row,
|
||||
* multi-column areas can be useful.<p/>
|
||||
*
|
||||
* Of course with carefully (or carelessly) chosen parameters, cyclic references can occur and
|
||||
* hence this method <b>can</b> throw a 'circular reference' EvaluationException. Note that
|
||||
* this method does not attempt to detect cycles. Every cell in the specified Area <tt>ae</tt>
|
||||
* has already been evaluated prior to this method call. Any cell (or cell<b>s</b>) part of
|
||||
* <tt>ae</tt> that would incur a cyclic reference error if selected by this method, will
|
||||
* already have the value <t>ErrorEval.CIRCULAR_REF_ERROR</tt> upon entry to this method. It
|
||||
* is assumed logic exists elsewhere to produce this behaviour.
|
||||
*
|
||||
* @return whatever the selected cell's evaluated value is. Never <code>null</code>. Never
|
||||
* <tt>ErrorEval</tt>.
|
||||
* @throws EvaluationException if there is a problem with indexing into the area, or if the
|
||||
* evaluated cell has an error.
|
||||
*/
|
||||
public static ValueEval chooseSingleElementFromArea(AreaEval ae,
|
||||
int srcCellRow, int srcCellCol) throws EvaluationException {
|
||||
ValueEval result = chooseSingleElementFromAreaInternal(ae, srcCellRow, srcCellCol);
|
||||
if (result instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return possibly <tt>ErrorEval</tt>, and <code>null</code>
|
||||
*/
|
||||
private static ValueEval chooseSingleElementFromAreaInternal(AreaEval ae,
|
||||
int srcCellRow, int srcCellCol) throws EvaluationException {
|
||||
|
||||
if(false) {
|
||||
// this is too simplistic
|
||||
if(ae.containsRow(srcCellRow) && ae.containsColumn(srcCellCol)) {
|
||||
throw new EvaluationException(ErrorEval.CIRCULAR_REF_ERROR);
|
||||
}
|
||||
/*
|
||||
Circular references are not dealt with directly here, but it is worth noting some issues.
|
||||
|
||||
ANY one of the return statements in this method could return a cell that is identical
|
||||
to the one immediately being evaluated. The evaluating cell is identified by srcCellRow,
|
||||
srcCellRow AND sheet. The sheet is not available in any nearby calling method, so that's
|
||||
one reason why circular references are not easy to detect here. (The sheet of the returned
|
||||
cell can be obtained from ae if it is an Area3DEval.)
|
||||
|
||||
Another reason there's little value in attempting to detect circular references here is
|
||||
that only direct circular references could be detected. If the cycle involved two or more
|
||||
cells this method could not detect it.
|
||||
|
||||
Logic to detect evaluation cycles of all kinds has been coded in EvaluationCycleDetector
|
||||
(and FormulaEvaluator).
|
||||
*/
|
||||
}
|
||||
|
||||
if (ae.isColumn()) {
|
||||
if(ae.isRow()) {
|
||||
return ae.getRelativeValue(0, 0);
|
||||
}
|
||||
if(!ae.containsRow(srcCellRow)) {
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
return ae.getAbsoluteValue(srcCellRow, ae.getFirstColumn());
|
||||
}
|
||||
if(!ae.isRow()) {
|
||||
// multi-column, multi-row area
|
||||
if(ae.containsRow(srcCellRow) && ae.containsColumn(srcCellCol)) {
|
||||
return ae.getAbsoluteValue(ae.getFirstRow(), ae.getFirstColumn());
|
||||
}
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
if(!ae.containsColumn(srcCellCol)) {
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
return ae.getAbsoluteValue(ae.getFirstRow(), srcCellCol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies some conversion rules if the supplied value is not already an integer.<br/>
|
||||
* Value is first coerced to a <tt>double</tt> ( See <tt>coerceValueToDouble()</tt> ).
|
||||
* Note - <tt>BlankEval</tt> is converted to <code>0</code>.<p/>
|
||||
*
|
||||
* Excel typically converts doubles to integers by truncating toward negative infinity.<br/>
|
||||
* The equivalent java code is:<br/>
|
||||
* <code>return (int)Math.floor(d);</code><br/>
|
||||
* <b>not</b>:<br/>
|
||||
* <code>return (int)d; // wrong - rounds toward zero</code>
|
||||
*
|
||||
*/
|
||||
public static int coerceValueToInt(ValueEval ev) throws EvaluationException {
|
||||
if (ev == BlankEval.instance) {
|
||||
return 0;
|
||||
}
|
||||
double d = coerceValueToDouble(ev);
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies some conversion rules if the supplied value is not already a number.
|
||||
* Note - <tt>BlankEval</tt> is converted to {@link NumberEval#ZERO}.
|
||||
* @param ev must be a {@link NumberEval}, {@link StringEval}, {@link BoolEval} or
|
||||
* {@link BlankEval}
|
||||
* @return actual, parsed or interpreted double value (respectively).
|
||||
* @throws EvaluationException(#VALUE!) only if a StringEval is supplied and cannot be parsed
|
||||
* as a double (See <tt>parseDouble()</tt> for allowable formats).
|
||||
* @throws RuntimeException if the supplied parameter is not {@link NumberEval},
|
||||
* {@link StringEval}, {@link BoolEval} or {@link BlankEval}
|
||||
*/
|
||||
public static double coerceValueToDouble(ValueEval ev) throws EvaluationException {
|
||||
|
||||
if (ev == BlankEval.instance) {
|
||||
return 0.0;
|
||||
}
|
||||
if (ev instanceof NumericValueEval) {
|
||||
// this also handles booleans
|
||||
return ((NumericValueEval)ev).getNumberValue();
|
||||
}
|
||||
if (ev instanceof StringEval) {
|
||||
Double dd = parseDouble(((StringEval) ev).getStringValue());
|
||||
if (dd == null) {
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
return dd.doubleValue();
|
||||
}
|
||||
throw new RuntimeException("Unexpected arg eval type (" + ev.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string to a double using standard rules that Excel would use.<br/>
|
||||
* Tolerates leading and trailing spaces, <p/>
|
||||
*
|
||||
* Doesn't support currency prefixes, commas, percentage signs or arithmetic operations strings.
|
||||
*
|
||||
* Some examples:<br/>
|
||||
* " 123 " -> 123.0<br/>
|
||||
* ".123" -> 0.123<br/>
|
||||
* "1E4" -> 1000<br/>
|
||||
* "-123" -> -123.0<br/>
|
||||
* These not supported yet:<br/>
|
||||
* " $ 1,000.00 " -> 1000.0<br/>
|
||||
* "$1.25E4" -> 12500.0<br/>
|
||||
* "5**2" -> 500<br/>
|
||||
* "250%" -> 2.5<br/>
|
||||
*
|
||||
* @return <code>null</code> if the specified text cannot be parsed as a number
|
||||
*/
|
||||
public static Double parseDouble(String pText) {
|
||||
|
||||
if (Pattern.matches(fpRegex, pText))
|
||||
try {
|
||||
return Double.parseDouble(pText);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ve must be a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>, or <tt>BlankEval</tt>
|
||||
* @return the converted string value. never <code>null</code>
|
||||
*/
|
||||
public static String coerceValueToString(ValueEval ve) {
|
||||
if (ve instanceof StringValueEval) {
|
||||
StringValueEval sve = (StringValueEval) ve;
|
||||
return sve.getStringValue();
|
||||
}
|
||||
if (ve == BlankEval.instance) {
|
||||
return "";
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected eval class (" + ve.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>null</code> to represent blank values
|
||||
* @throws EvaluationException if ve is an ErrorEval, or if a string value cannot be converted
|
||||
*/
|
||||
public static Boolean coerceValueToBoolean(ValueEval ve, boolean stringsAreBlanks) throws EvaluationException {
|
||||
|
||||
if (ve == null || ve == BlankEval.instance) {
|
||||
// TODO - remove 've == null' condition once AreaEval is fixed
|
||||
return null;
|
||||
}
|
||||
if (ve instanceof BoolEval) {
|
||||
return Boolean.valueOf(((BoolEval) ve).getBooleanValue());
|
||||
}
|
||||
|
||||
if (ve == BlankEval.instance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ve instanceof StringEval) {
|
||||
if (stringsAreBlanks) {
|
||||
return null;
|
||||
}
|
||||
String str = ((StringEval) ve).getStringValue();
|
||||
if (str.equalsIgnoreCase("true")) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
if (str.equalsIgnoreCase("false")) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
// else - string cannot be converted to boolean
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
|
||||
if (ve instanceof NumericValueEval) {
|
||||
NumericValueEval ne = (NumericValueEval) ve;
|
||||
double d = ne.getNumberValue();
|
||||
if (Double.isNaN(d)) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
return Boolean.valueOf(d != 0);
|
||||
}
|
||||
if (ve instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) ve);
|
||||
}
|
||||
throw new RuntimeException("Unexpected eval (" + ve.getClass().getName() + ")");
|
||||
}
|
||||
}
|
@ -1,49 +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.functions.Fixed1ArgFunction;
|
||||
import org.apache.poi.hssf.record.formula.functions.Function;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of Excel formula token '%'. <p/>
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class PercentEval extends Fixed1ArgFunction {
|
||||
|
||||
public static final Function instance = new PercentEval();
|
||||
|
||||
private PercentEval() {
|
||||
// enforce singleton
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
double d;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
d = OperandResolver.coerceValueToDouble(ve);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
if (d == 0.0) { // this '==' matches +0.0 and -0.0
|
||||
return NumberEval.ZERO;
|
||||
}
|
||||
return new NumberEval(d / 100);
|
||||
}
|
||||
}
|
@ -1,75 +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.functions.Fixed2ArgFunction;
|
||||
import org.apache.poi.hssf.record.formula.functions.Function;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class RangeEval extends Fixed2ArgFunction {
|
||||
|
||||
public static final Function instance = new RangeEval();
|
||||
|
||||
private RangeEval() {
|
||||
// enforces singleton
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
|
||||
try {
|
||||
AreaEval reA = evaluateRef(arg0);
|
||||
AreaEval reB = evaluateRef(arg1);
|
||||
return resolveRange(reA, reB);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return simple rectangular {@link AreaEval} which fully encloses both areas
|
||||
* <tt>aeA</tt> and <tt>aeB</tt>
|
||||
*/
|
||||
private static AreaEval resolveRange(AreaEval aeA, AreaEval aeB) {
|
||||
int aeAfr = aeA.getFirstRow();
|
||||
int aeAfc = aeA.getFirstColumn();
|
||||
|
||||
int top = Math.min(aeAfr, aeB.getFirstRow());
|
||||
int bottom = Math.max(aeA.getLastRow(), aeB.getLastRow());
|
||||
int left = Math.min(aeAfc, aeB.getFirstColumn());
|
||||
int right = Math.max(aeA.getLastColumn(), aeB.getLastColumn());
|
||||
|
||||
return aeA.offset(top-aeAfr, bottom-aeAfr, left-aeAfc, right-aeAfc);
|
||||
}
|
||||
|
||||
private static AreaEval evaluateRef(ValueEval arg) throws EvaluationException {
|
||||
if (arg instanceof AreaEval) {
|
||||
return (AreaEval) arg;
|
||||
}
|
||||
if (arg instanceof RefEval) {
|
||||
return ((RefEval) arg).offset(0, 0, 0, 0);
|
||||
}
|
||||
if (arg instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval)arg);
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected ref arg class (" + arg.getClass().getName() + ")");
|
||||
}
|
||||
}
|
@ -1,51 +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;
|
||||
|
||||
/**
|
||||
* @author Amol S Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
* RefEval is the super interface for Ref2D and Ref3DEval. Basically a RefEval
|
||||
* impl should contain reference to the original ReferencePtg or Ref3DPtg as
|
||||
* well as the final "value" resulting from the evaluation of the cell
|
||||
* reference. Thus if the Cell has type CELL_TYPE_NUMERIC, the contained
|
||||
* value object should be of type NumberEval; if cell type is CELL_TYPE_STRING,
|
||||
* contained value object should be of type StringEval
|
||||
*/
|
||||
public interface RefEval extends ValueEval {
|
||||
|
||||
/**
|
||||
* @return the evaluated value of the cell referred to by this RefEval.
|
||||
*/
|
||||
ValueEval getInnerValueEval();
|
||||
|
||||
/**
|
||||
* returns the zero based column index.
|
||||
*/
|
||||
int getColumn();
|
||||
|
||||
/**
|
||||
* returns the zero based row index.
|
||||
*/
|
||||
int getRow();
|
||||
|
||||
/**
|
||||
* Creates an {@link AreaEval} offset by a relative amount from this RefEval
|
||||
*/
|
||||
AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx);
|
||||
}
|
@ -1,40 +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;
|
||||
|
||||
/**
|
||||
* Common base class for implementors of {@link RefEval}
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,168 +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.functions.Fixed2ArgFunction;
|
||||
import org.apache.poi.hssf.record.formula.functions.Function;
|
||||
import org.apache.poi.ss.util.NumberComparer;
|
||||
|
||||
/**
|
||||
* Base class for all comparison operator evaluators
|
||||
*
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public abstract class RelationalOperationEval extends Fixed2ArgFunction {
|
||||
|
||||
/**
|
||||
* Converts a standard compare result (-1, 0, 1) to <code>true</code> or <code>false</code>
|
||||
* according to subclass' comparison type.
|
||||
*/
|
||||
protected abstract boolean convertComparisonResult(int cmpResult);
|
||||
|
||||
/**
|
||||
* This is a description of how the relational operators apply in MS Excel.
|
||||
* Use this as a guideline when testing/implementing the evaluate methods
|
||||
* for the relational operators Evals.
|
||||
*
|
||||
* <pre>
|
||||
* Bool.TRUE > any number.
|
||||
* Bool > any string. ALWAYS
|
||||
* Bool.TRUE > Bool.FALSE
|
||||
* Bool.FALSE == Blank
|
||||
*
|
||||
* Strings are never converted to numbers or booleans
|
||||
* String > any number. ALWAYS
|
||||
* Non-empty String > Blank
|
||||
* Empty String == Blank
|
||||
* String are sorted dictionary wise
|
||||
*
|
||||
* Blank > Negative numbers
|
||||
* Blank == 0
|
||||
* Blank < Positive numbers
|
||||
* </pre>
|
||||
*/
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
|
||||
ValueEval vA;
|
||||
ValueEval vB;
|
||||
try {
|
||||
vA = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
vB = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
int cmpResult = doCompare(vA, vB);
|
||||
boolean result = convertComparisonResult(cmpResult);
|
||||
return BoolEval.valueOf(result);
|
||||
}
|
||||
|
||||
private static int doCompare(ValueEval va, ValueEval vb) {
|
||||
// special cases when one operand is blank
|
||||
if (va == BlankEval.instance) {
|
||||
return compareBlank(vb);
|
||||
}
|
||||
if (vb == BlankEval.instance) {
|
||||
return -compareBlank(va);
|
||||
}
|
||||
|
||||
if (va instanceof BoolEval) {
|
||||
if (vb instanceof BoolEval) {
|
||||
BoolEval bA = (BoolEval) va;
|
||||
BoolEval bB = (BoolEval) vb;
|
||||
if (bA.getBooleanValue() == bB.getBooleanValue()) {
|
||||
return 0;
|
||||
}
|
||||
return bA.getBooleanValue() ? 1 : -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (vb instanceof BoolEval) {
|
||||
return -1;
|
||||
}
|
||||
if (va instanceof StringEval) {
|
||||
if (vb instanceof StringEval) {
|
||||
StringEval sA = (StringEval) va;
|
||||
StringEval sB = (StringEval) vb;
|
||||
return sA.getStringValue().compareToIgnoreCase(sB.getStringValue());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (vb instanceof StringEval) {
|
||||
return -1;
|
||||
}
|
||||
if (va instanceof NumberEval) {
|
||||
if (vb instanceof NumberEval) {
|
||||
NumberEval nA = (NumberEval) va;
|
||||
NumberEval nB = (NumberEval) vb;
|
||||
return NumberComparer.compare(nA.getNumberValue(), nB.getNumberValue());
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Bad operand types (" + va.getClass().getName() + "), ("
|
||||
+ vb.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
private static int compareBlank(ValueEval v) {
|
||||
if (v == BlankEval.instance) {
|
||||
return 0;
|
||||
}
|
||||
if (v instanceof BoolEval) {
|
||||
BoolEval boolEval = (BoolEval) v;
|
||||
return boolEval.getBooleanValue() ? -1 : 0;
|
||||
}
|
||||
if (v instanceof NumberEval) {
|
||||
NumberEval ne = (NumberEval) v;
|
||||
return NumberComparer.compare(0.0, ne.getNumberValue());
|
||||
}
|
||||
if (v instanceof StringEval) {
|
||||
StringEval se = (StringEval) v;
|
||||
return se.getStringValue().length() < 1 ? 0 : -1;
|
||||
}
|
||||
throw new IllegalArgumentException("bad value class (" + v.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
public static final Function EqualEval = new RelationalOperationEval() {
|
||||
protected boolean convertComparisonResult(int cmpResult) {
|
||||
return cmpResult == 0;
|
||||
}
|
||||
};
|
||||
public static final Function GreaterEqualEval = new RelationalOperationEval() {
|
||||
protected boolean convertComparisonResult(int cmpResult) {
|
||||
return cmpResult >= 0;
|
||||
}
|
||||
};
|
||||
public static final Function GreaterThanEval = new RelationalOperationEval() {
|
||||
protected boolean convertComparisonResult(int cmpResult) {
|
||||
return cmpResult > 0;
|
||||
}
|
||||
};
|
||||
public static final Function LessEqualEval = new RelationalOperationEval() {
|
||||
protected boolean convertComparisonResult(int cmpResult) {
|
||||
return cmpResult <= 0;
|
||||
}
|
||||
};
|
||||
public static final Function LessThanEval = new RelationalOperationEval() {
|
||||
protected boolean convertComparisonResult(int cmpResult) {
|
||||
return cmpResult < 0;
|
||||
}
|
||||
};
|
||||
public static final Function NotEqualEval = new RelationalOperationEval() {
|
||||
protected boolean convertComparisonResult(int cmpResult) {
|
||||
return cmpResult != 0;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,54 +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.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.StringPtg;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public final class StringEval implements StringValueEval {
|
||||
|
||||
public static final StringEval EMPTY_INSTANCE = new StringEval("");
|
||||
|
||||
private final String _value;
|
||||
|
||||
public StringEval(Ptg ptg) {
|
||||
this(((StringPtg) ptg).getValue());
|
||||
}
|
||||
|
||||
public StringEval(String value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("value must not be null");
|
||||
}
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public String getStringValue() {
|
||||
return _value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(_value);
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -1,30 +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;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public interface StringValueEval extends ValueEval {
|
||||
|
||||
/**
|
||||
* @return never <code>null</code>, possibly empty string.
|
||||
*/
|
||||
String getStringValue();
|
||||
}
|
@ -1,87 +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.functions.Fixed2ArgFunction;
|
||||
import org.apache.poi.hssf.record.formula.functions.Function;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public abstract class TwoOperandNumericOperation extends Fixed2ArgFunction {
|
||||
|
||||
protected final double singleOperandEvaluate(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
return OperandResolver.coerceValueToDouble(ve);
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
double result;
|
||||
try {
|
||||
double d0 = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
double d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
result = evaluate(d0, d1);
|
||||
if (result == 0.0) { // this '==' matches +0.0 and -0.0
|
||||
// Excel converts -0.0 to +0.0 for '*', '/', '%', '+' and '^'
|
||||
if (!(this instanceof SubtractEvalClass)) {
|
||||
return NumberEval.ZERO;
|
||||
}
|
||||
}
|
||||
if (Double.isNaN(result) || Double.isInfinite(result)) {
|
||||
return ErrorEval.NUM_ERROR;
|
||||
}
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
protected abstract double evaluate(double d0, double d1) throws EvaluationException;
|
||||
|
||||
public static final Function AddEval = new TwoOperandNumericOperation() {
|
||||
protected double evaluate(double d0, double d1) {
|
||||
return d0+d1;
|
||||
}
|
||||
};
|
||||
public static final Function DivideEval = new TwoOperandNumericOperation() {
|
||||
protected double evaluate(double d0, double d1) throws EvaluationException {
|
||||
if (d1 == 0.0) {
|
||||
throw new EvaluationException(ErrorEval.DIV_ZERO);
|
||||
}
|
||||
return d0/d1;
|
||||
}
|
||||
};
|
||||
public static final Function MultiplyEval = new TwoOperandNumericOperation() {
|
||||
protected double evaluate(double d0, double d1) {
|
||||
return d0*d1;
|
||||
}
|
||||
};
|
||||
public static final Function PowerEval = new TwoOperandNumericOperation() {
|
||||
protected double evaluate(double d0, double d1) {
|
||||
return Math.pow(d0, d1);
|
||||
}
|
||||
};
|
||||
private static final class SubtractEvalClass extends TwoOperandNumericOperation {
|
||||
public SubtractEvalClass() {
|
||||
//
|
||||
}
|
||||
protected double evaluate(double d0, double d1) {
|
||||
return d0-d1;
|
||||
}
|
||||
}
|
||||
public static final Function SubtractEval = new SubtractEvalClass();
|
||||
}
|
@ -1,47 +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.functions.Fixed1ArgFunction;
|
||||
import org.apache.poi.hssf.record.formula.functions.Function;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public final class UnaryMinusEval extends Fixed1ArgFunction {
|
||||
|
||||
public static final Function instance = new UnaryMinusEval();
|
||||
|
||||
private UnaryMinusEval() {
|
||||
// enforce singleton
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
double d;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
d = OperandResolver.coerceValueToDouble(ve);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
if (d == 0.0) { // this '==' matches +0.0 and -0.0
|
||||
return NumberEval.ZERO;
|
||||
}
|
||||
return new NumberEval(-d);
|
||||
}
|
||||
}
|
@ -1,51 +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.functions.Fixed1ArgFunction;
|
||||
import org.apache.poi.hssf.record.formula.functions.Function;
|
||||
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public final class UnaryPlusEval extends Fixed1ArgFunction {
|
||||
|
||||
public static final Function instance = new UnaryPlusEval();
|
||||
|
||||
private UnaryPlusEval() {
|
||||
// enforce singleton
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcCellRow, int srcCellCol, ValueEval arg0) {
|
||||
double d;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg0, srcCellRow, srcCellCol);
|
||||
if(ve instanceof StringEval) {
|
||||
// Note - asymmetric with UnaryMinus
|
||||
// -"hello" evaluates to #VALUE!
|
||||
// but +"hello" evaluates to "hello"
|
||||
return ve;
|
||||
}
|
||||
d = OperandResolver.coerceValueToDouble(ve);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(+d);
|
||||
}
|
||||
}
|
@ -1,25 +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;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public interface ValueEval {
|
||||
// no methods
|
||||
}
|
@ -1,91 +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.function;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Temporarily collects <tt>FunctionMetadata</tt> instances for creation of a
|
||||
* <tt>FunctionMetadataRegistry</tt>.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class FunctionDataBuilder {
|
||||
private int _maxFunctionIndex;
|
||||
private final Map _functionDataByName;
|
||||
private final Map _functionDataByIndex;
|
||||
/** stores indexes of all functions with footnotes (i.e. whose definitions might change) */
|
||||
private final Set _mutatingFunctionIndexes;
|
||||
|
||||
public FunctionDataBuilder(int sizeEstimate) {
|
||||
_maxFunctionIndex = -1;
|
||||
_functionDataByName = new HashMap(sizeEstimate * 3 / 2);
|
||||
_functionDataByIndex = new HashMap(sizeEstimate * 3 / 2);
|
||||
_mutatingFunctionIndexes = new HashSet();
|
||||
}
|
||||
|
||||
public void add(int functionIndex, String functionName, int minParams, int maxParams,
|
||||
byte returnClassCode, byte[] parameterClassCodes, boolean hasFootnote) {
|
||||
FunctionMetadata fm = new FunctionMetadata(functionIndex, functionName, minParams, maxParams,
|
||||
returnClassCode, parameterClassCodes);
|
||||
|
||||
Integer indexKey = Integer.valueOf(functionIndex);
|
||||
|
||||
|
||||
if(functionIndex > _maxFunctionIndex) {
|
||||
_maxFunctionIndex = functionIndex;
|
||||
}
|
||||
// allow function definitions to change only if both previous and the new items have footnotes
|
||||
FunctionMetadata prevFM;
|
||||
prevFM = (FunctionMetadata) _functionDataByName.get(functionName);
|
||||
if(prevFM != null) {
|
||||
if(!hasFootnote || !_mutatingFunctionIndexes.contains(indexKey)) {
|
||||
throw new RuntimeException("Multiple entries for function name '" + functionName + "'");
|
||||
}
|
||||
_functionDataByIndex.remove(Integer.valueOf(prevFM.getIndex()));
|
||||
}
|
||||
prevFM = (FunctionMetadata) _functionDataByIndex.get(indexKey);
|
||||
if(prevFM != null) {
|
||||
if(!hasFootnote || !_mutatingFunctionIndexes.contains(indexKey)) {
|
||||
throw new RuntimeException("Multiple entries for function index (" + functionIndex + ")");
|
||||
}
|
||||
_functionDataByName.remove(prevFM.getName());
|
||||
}
|
||||
if(hasFootnote) {
|
||||
_mutatingFunctionIndexes.add(indexKey);
|
||||
}
|
||||
_functionDataByIndex.put(indexKey, fm);
|
||||
_functionDataByName.put(functionName, fm);
|
||||
}
|
||||
|
||||
public FunctionMetadataRegistry build() {
|
||||
|
||||
FunctionMetadata[] jumbledArray = new FunctionMetadata[_functionDataByName.size()];
|
||||
_functionDataByName.values().toArray(jumbledArray);
|
||||
FunctionMetadata[] fdIndexArray = new FunctionMetadata[_maxFunctionIndex+1];
|
||||
for (int i = 0; i < jumbledArray.length; i++) {
|
||||
FunctionMetadata fd = jumbledArray[i];
|
||||
fdIndexArray[fd.getIndex()] = fd;
|
||||
}
|
||||
|
||||
return new FunctionMetadataRegistry(fdIndexArray, _functionDataByName);
|
||||
}
|
||||
}
|
@ -1,92 +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.function;
|
||||
|
||||
import org.apache.poi.ss.SpreadsheetVersion;
|
||||
|
||||
/**
|
||||
* Holds information about Excel built-in functions.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class FunctionMetadata {
|
||||
/**
|
||||
* maxParams=30 in functionMetadata.txt means the maximum number arguments supported
|
||||
* by the given version of Excel. Validation routines should take the actual limit (Excel 97 or 2007)
|
||||
* from the SpreadsheetVersion enum.
|
||||
* Perhaps a value like 'M' should be used instead of '30' in functionMetadata.txt
|
||||
* to make that file more version neutral.
|
||||
* @see org.apache.poi.ss.formula.FormulaParser#validateNumArgs(int, FunctionMetadata)
|
||||
*/
|
||||
private static final short FUNCTION_MAX_PARAMS = 30;
|
||||
|
||||
private final int _index;
|
||||
private final String _name;
|
||||
private final int _minParams;
|
||||
private final int _maxParams;
|
||||
private final byte _returnClassCode;
|
||||
private final byte[] _parameterClassCodes;
|
||||
|
||||
/* package */ FunctionMetadata(int index, String name, int minParams, int maxParams,
|
||||
byte returnClassCode, byte[] parameterClassCodes) {
|
||||
_index = index;
|
||||
_name = name;
|
||||
_minParams = minParams;
|
||||
_maxParams = maxParams;
|
||||
_returnClassCode = returnClassCode;
|
||||
_parameterClassCodes = parameterClassCodes;
|
||||
}
|
||||
public int getIndex() {
|
||||
return _index;
|
||||
}
|
||||
public String getName() {
|
||||
return _name;
|
||||
}
|
||||
public int getMinParams() {
|
||||
return _minParams;
|
||||
}
|
||||
public int getMaxParams() {
|
||||
return _maxParams;
|
||||
}
|
||||
public boolean hasFixedArgsLength() {
|
||||
return _minParams == _maxParams;
|
||||
}
|
||||
public byte getReturnClassCode() {
|
||||
return _returnClassCode;
|
||||
}
|
||||
public byte[] getParameterClassCodes() {
|
||||
return _parameterClassCodes.clone();
|
||||
}
|
||||
/**
|
||||
* Some varags functions (like VLOOKUP) have a specific limit to the number of arguments that
|
||||
* can be passed. Other functions (like SUM) don't have such a limit. For those functions,
|
||||
* the spreadsheet version determines the maximum number of arguments that can be passed.
|
||||
* @return <code>true</code> if this function can the maximum number of arguments allowable by
|
||||
* the {@link SpreadsheetVersion}
|
||||
*/
|
||||
public boolean hasUnlimitedVarags() {
|
||||
return FUNCTION_MAX_PARAMS == _maxParams;
|
||||
}
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(_index).append(" ").append(_name);
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -1,195 +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.function;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
|
||||
/**
|
||||
* Converts the text meta-data file into a <tt>FunctionMetadataRegistry</tt>
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class FunctionMetadataReader {
|
||||
|
||||
private static final String METADATA_FILE_NAME = "functionMetadata.txt";
|
||||
|
||||
/** plain ASCII text metadata file uses three dots for ellipsis */
|
||||
private static final String ELLIPSIS = "...";
|
||||
|
||||
private static final Pattern TAB_DELIM_PATTERN = Pattern.compile("\t");
|
||||
private static final Pattern SPACE_DELIM_PATTERN = Pattern.compile(" ");
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = { };
|
||||
|
||||
private static final String[] DIGIT_ENDING_FUNCTION_NAMES = {
|
||||
// Digits at the end of a function might be due to a left-over footnote marker.
|
||||
// except in these cases
|
||||
"LOG10", "ATAN2", "DAYS360", "SUMXMY2", "SUMX2MY2", "SUMX2PY2",
|
||||
};
|
||||
private static final Set DIGIT_ENDING_FUNCTION_NAMES_SET = new HashSet(Arrays.asList(DIGIT_ENDING_FUNCTION_NAMES));
|
||||
|
||||
public static FunctionMetadataRegistry createRegistry() {
|
||||
InputStream is = FunctionMetadataReader.class.getResourceAsStream(METADATA_FILE_NAME);
|
||||
if (is == null) {
|
||||
throw new RuntimeException("resource '" + METADATA_FILE_NAME + "' not found");
|
||||
}
|
||||
|
||||
BufferedReader br;
|
||||
try {
|
||||
br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
|
||||
} catch(UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
FunctionDataBuilder fdb = new FunctionDataBuilder(400);
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
String line = br.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
if (line.length() < 1 || line.charAt(0) == '#') {
|
||||
continue;
|
||||
}
|
||||
String trimLine = line.trim();
|
||||
if (trimLine.length() < 1) {
|
||||
continue;
|
||||
}
|
||||
processLine(fdb, line);
|
||||
}
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return fdb.build();
|
||||
}
|
||||
|
||||
private static void processLine(FunctionDataBuilder fdb, String line) {
|
||||
|
||||
String[] parts = TAB_DELIM_PATTERN.split(line, -2);
|
||||
if(parts.length != 8) {
|
||||
throw new RuntimeException("Bad line format '" + line + "' - expected 8 data fields");
|
||||
}
|
||||
int functionIndex = parseInt(parts[0]);
|
||||
String functionName = parts[1];
|
||||
int minParams = parseInt(parts[2]);
|
||||
int maxParams = parseInt(parts[3]);
|
||||
byte returnClassCode = parseReturnTypeCode(parts[4]);
|
||||
byte[] parameterClassCodes = parseOperandTypeCodes(parts[5]);
|
||||
// 6 isVolatile
|
||||
boolean hasNote = parts[7].length() > 0;
|
||||
|
||||
validateFunctionName(functionName);
|
||||
// TODO - make POI use isVolatile
|
||||
fdb.add(functionIndex, functionName, minParams, maxParams,
|
||||
returnClassCode, parameterClassCodes, hasNote);
|
||||
}
|
||||
|
||||
|
||||
private static byte parseReturnTypeCode(String code) {
|
||||
if(code.length() == 0) {
|
||||
return Ptg.CLASS_REF; // happens for GETPIVOTDATA
|
||||
}
|
||||
return parseOperandTypeCode(code);
|
||||
}
|
||||
|
||||
private static byte[] parseOperandTypeCodes(String codes) {
|
||||
if(codes.length() < 1) {
|
||||
return EMPTY_BYTE_ARRAY; // happens for GETPIVOTDATA
|
||||
}
|
||||
if(isDash(codes)) {
|
||||
// '-' means empty:
|
||||
return EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
String[] array = SPACE_DELIM_PATTERN.split(codes);
|
||||
int nItems = array.length;
|
||||
if(ELLIPSIS.equals(array[nItems-1])) {
|
||||
// final ellipsis is optional, and ignored
|
||||
// (all unspecified params are assumed to be the same as the last)
|
||||
nItems --;
|
||||
}
|
||||
byte[] result = new byte[nItems];
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
result[i] = parseOperandTypeCode(array[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isDash(String codes) {
|
||||
if(codes.length() == 1) {
|
||||
switch (codes.charAt(0)) {
|
||||
case '-':
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static byte parseOperandTypeCode(String code) {
|
||||
if(code.length() != 1) {
|
||||
throw new RuntimeException("Bad operand type code format '" + code + "' expected single char");
|
||||
}
|
||||
switch(code.charAt(0)) {
|
||||
case 'V': return Ptg.CLASS_VALUE;
|
||||
case 'R': return Ptg.CLASS_REF;
|
||||
case 'A': return Ptg.CLASS_ARRAY;
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected operand type code '" + code + "' (" + (int)code.charAt(0) + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that footnote digits from the original OOO document have not been accidentally
|
||||
* left behind
|
||||
*/
|
||||
private static void validateFunctionName(String functionName) {
|
||||
int len = functionName.length();
|
||||
int ix = len - 1;
|
||||
if (!Character.isDigit(functionName.charAt(ix))) {
|
||||
return;
|
||||
}
|
||||
while(ix >= 0) {
|
||||
if (!Character.isDigit(functionName.charAt(ix))) {
|
||||
break;
|
||||
}
|
||||
ix--;
|
||||
}
|
||||
if(DIGIT_ENDING_FUNCTION_NAMES_SET.contains(functionName)) {
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("Invalid function name '" + functionName
|
||||
+ "' (is footnote number incorrectly appended)");
|
||||
}
|
||||
|
||||
private static int parseInt(String valStr) {
|
||||
try {
|
||||
return Integer.parseInt(valStr);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new RuntimeException("Value '" + valStr + "' could not be parsed as an integer");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,90 +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.function;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
/**
|
||||
* Allows clients to get {@link FunctionMetadata} instances for any built-in function of Excel.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class FunctionMetadataRegistry {
|
||||
/**
|
||||
* The name of the IF function (i.e. "IF"). Extracted as a constant for clarity.
|
||||
*/
|
||||
public static final String FUNCTION_NAME_IF = "IF";
|
||||
|
||||
public static final int FUNCTION_INDEX_IF = 1;
|
||||
public static final short FUNCTION_INDEX_SUM = 4;
|
||||
public static final int FUNCTION_INDEX_CHOOSE = 100;
|
||||
public static final short FUNCTION_INDEX_INDIRECT = 148;
|
||||
public static final short FUNCTION_INDEX_EXTERNAL = 255;
|
||||
|
||||
private static FunctionMetadataRegistry _instance;
|
||||
|
||||
private final FunctionMetadata[] _functionDataByIndex;
|
||||
private final Map<String, FunctionMetadata> _functionDataByName;
|
||||
|
||||
private static FunctionMetadataRegistry getInstance() {
|
||||
if (_instance == null) {
|
||||
_instance = FunctionMetadataReader.createRegistry();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
|
||||
/* package */ FunctionMetadataRegistry(FunctionMetadata[] functionDataByIndex, Map<String, FunctionMetadata> functionDataByName) {
|
||||
_functionDataByIndex = functionDataByIndex;
|
||||
_functionDataByName = functionDataByName;
|
||||
}
|
||||
|
||||
/* package */ Set<String> getAllFunctionNames() {
|
||||
return _functionDataByName.keySet();
|
||||
}
|
||||
|
||||
|
||||
public static FunctionMetadata getFunctionByIndex(int index) {
|
||||
return getInstance().getFunctionByIndexInternal(index);
|
||||
}
|
||||
|
||||
private FunctionMetadata getFunctionByIndexInternal(int index) {
|
||||
return _functionDataByIndex[index];
|
||||
}
|
||||
/**
|
||||
* Resolves a built-in function index.
|
||||
* @param name uppercase function name
|
||||
* @return a negative value if the function name is not found.
|
||||
* This typically occurs for external functions.
|
||||
*/
|
||||
public static short lookupIndexByName(String name) {
|
||||
FunctionMetadata fd = getInstance().getFunctionByNameInternal(name);
|
||||
if (fd == null) {
|
||||
return -1;
|
||||
}
|
||||
return (short) fd.getIndex();
|
||||
}
|
||||
|
||||
private FunctionMetadata getFunctionByNameInternal(String name) {
|
||||
return _functionDataByName.get(name);
|
||||
}
|
||||
|
||||
|
||||
public static FunctionMetadata getFunctionByName(String name) {
|
||||
return getInstance().getFunctionByNameInternal(name);
|
||||
}
|
||||
}
|
@ -1,145 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public abstract class AggregateFunction extends MultiOperandNumericFunction {
|
||||
|
||||
private static final class LargeSmall extends Fixed2ArgFunction {
|
||||
private final boolean _isLarge;
|
||||
protected LargeSmall(boolean isLarge) {
|
||||
_isLarge = isLarge;
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0,
|
||||
ValueEval arg1) {
|
||||
double dn;
|
||||
try {
|
||||
ValueEval ve1 = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex);
|
||||
dn = OperandResolver.coerceValueToDouble(ve1);
|
||||
} catch (EvaluationException e1) {
|
||||
// all errors in the second arg translate to #VALUE!
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
// weird Excel behaviour on second arg
|
||||
if (dn < 1.0) {
|
||||
// values between 0.0 and 1.0 result in #NUM!
|
||||
return ErrorEval.NUM_ERROR;
|
||||
}
|
||||
// all other values are rounded up to the next integer
|
||||
int k = (int) Math.ceil(dn);
|
||||
|
||||
double result;
|
||||
try {
|
||||
double[] ds = ValueCollector.collectValues(arg0);
|
||||
if (k > ds.length) {
|
||||
return ErrorEval.NUM_ERROR;
|
||||
}
|
||||
result = _isLarge ? StatsLib.kthLargest(ds, k) : StatsLib.kthSmallest(ds, k);
|
||||
NumericFunction.checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
|
||||
return new NumberEval(result);
|
||||
}
|
||||
}
|
||||
private static final class ValueCollector extends MultiOperandNumericFunction {
|
||||
private static final ValueCollector instance = new ValueCollector();
|
||||
public ValueCollector() {
|
||||
super(false, false);
|
||||
}
|
||||
public static double[] collectValues(ValueEval...operands) throws EvaluationException {
|
||||
return instance.getNumberArray(operands);
|
||||
}
|
||||
protected double evaluate(double[] values) {
|
||||
throw new IllegalStateException("should not be called");
|
||||
}
|
||||
}
|
||||
|
||||
protected AggregateFunction() {
|
||||
super(false, false);
|
||||
}
|
||||
|
||||
public static final Function AVEDEV = new AggregateFunction() {
|
||||
protected double evaluate(double[] values) {
|
||||
return StatsLib.avedev(values);
|
||||
}
|
||||
};
|
||||
public static final Function AVERAGE = new AggregateFunction() {
|
||||
protected double evaluate(double[] values) throws EvaluationException {
|
||||
if (values.length < 1) {
|
||||
throw new EvaluationException(ErrorEval.DIV_ZERO);
|
||||
}
|
||||
return MathX.average(values);
|
||||
}
|
||||
};
|
||||
public static final Function DEVSQ = new AggregateFunction() {
|
||||
protected double evaluate(double[] values) {
|
||||
return StatsLib.devsq(values);
|
||||
}
|
||||
};
|
||||
public static final Function LARGE = new LargeSmall(true);
|
||||
public static final Function MAX = new AggregateFunction() {
|
||||
protected double evaluate(double[] values) {
|
||||
return values.length > 0 ? MathX.max(values) : 0;
|
||||
}
|
||||
};
|
||||
public static final Function MEDIAN = new AggregateFunction() {
|
||||
protected double evaluate(double[] values) {
|
||||
return StatsLib.median(values);
|
||||
}
|
||||
};
|
||||
public static final Function MIN = new AggregateFunction() {
|
||||
protected double evaluate(double[] values) {
|
||||
return values.length > 0 ? MathX.min(values) : 0;
|
||||
}
|
||||
};
|
||||
public static final Function PRODUCT = new AggregateFunction() {
|
||||
protected double evaluate(double[] values) {
|
||||
return MathX.product(values);
|
||||
}
|
||||
};
|
||||
public static final Function SMALL = new LargeSmall(false);
|
||||
public static final Function STDEV = new AggregateFunction() {
|
||||
protected double evaluate(double[] values) throws EvaluationException {
|
||||
if (values.length < 1) {
|
||||
throw new EvaluationException(ErrorEval.DIV_ZERO);
|
||||
}
|
||||
return StatsLib.stdev(values);
|
||||
}
|
||||
};
|
||||
public static final Function SUM = new AggregateFunction() {
|
||||
protected double evaluate(double[] values) {
|
||||
return MathX.sum(values);
|
||||
}
|
||||
};
|
||||
public static final Function SUMSQ = new AggregateFunction() {
|
||||
protected double evaluate(double[] values) {
|
||||
return MathX.sumsq(values);
|
||||
}
|
||||
};
|
||||
}
|
@ -1,146 +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.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.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.formula.TwoDEval;
|
||||
|
||||
/**
|
||||
* Here are the general rules concerning Boolean functions:
|
||||
* <ol>
|
||||
* <li> Blanks are ignored (not either true or false) </li>
|
||||
* <li> Strings are ignored if part of an area ref or cell ref, otherwise they must be 'true' or 'false'</li>
|
||||
* <li> Numbers: 0 is false. Any other number is TRUE </li>
|
||||
* <li> Areas: *all* cells in area are evaluated according to the above rules</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public abstract class BooleanFunction implements Function {
|
||||
|
||||
public final ValueEval evaluate(ValueEval[] args, int srcRow, int srcCol) {
|
||||
if (args.length < 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
boolean boolResult;
|
||||
try {
|
||||
boolResult = calculate(args);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return BoolEval.valueOf(boolResult);
|
||||
}
|
||||
|
||||
private boolean calculate(ValueEval[] args) throws EvaluationException {
|
||||
|
||||
boolean result = getInitialResultValue();
|
||||
boolean atleastOneNonBlank = false;
|
||||
|
||||
/*
|
||||
* Note: no short-circuit boolean loop exit because any ErrorEvals will override the result
|
||||
*/
|
||||
for (int i=0, iSize=args.length; i<iSize; i++) {
|
||||
ValueEval arg = args[i];
|
||||
if (arg instanceof TwoDEval) {
|
||||
TwoDEval ae = (TwoDEval) arg;
|
||||
int height = ae.getHeight();
|
||||
int width = ae.getWidth();
|
||||
for (int rrIx=0; rrIx<height; rrIx++) {
|
||||
for (int rcIx=0; rcIx<width; rcIx++) {
|
||||
ValueEval ve = ae.getValue(rrIx, rcIx);
|
||||
Boolean tempVe = OperandResolver.coerceValueToBoolean(ve, true);
|
||||
if (tempVe != null) {
|
||||
result = partialEvaluate(result, tempVe.booleanValue());
|
||||
atleastOneNonBlank = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Boolean tempVe;
|
||||
if (arg instanceof RefEval) {
|
||||
ValueEval ve = ((RefEval) arg).getInnerValueEval();
|
||||
tempVe = OperandResolver.coerceValueToBoolean(ve, true);
|
||||
} else {
|
||||
tempVe = OperandResolver.coerceValueToBoolean(arg, false);
|
||||
}
|
||||
|
||||
|
||||
if (tempVe != null) {
|
||||
result = partialEvaluate(result, tempVe.booleanValue());
|
||||
atleastOneNonBlank = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!atleastOneNonBlank) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
protected abstract boolean getInitialResultValue();
|
||||
protected abstract boolean partialEvaluate(boolean cumulativeResult, boolean currentValue);
|
||||
|
||||
|
||||
public static final Function AND = new BooleanFunction() {
|
||||
protected boolean getInitialResultValue() {
|
||||
return true;
|
||||
}
|
||||
protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
|
||||
return cumulativeResult && currentValue;
|
||||
}
|
||||
};
|
||||
public static final Function OR = new BooleanFunction() {
|
||||
protected boolean getInitialResultValue() {
|
||||
return false;
|
||||
}
|
||||
protected boolean partialEvaluate(boolean cumulativeResult, boolean currentValue) {
|
||||
return cumulativeResult || currentValue;
|
||||
}
|
||||
};
|
||||
public static final Function FALSE = new Fixed0ArgFunction() {
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
|
||||
return BoolEval.FALSE;
|
||||
}
|
||||
};
|
||||
public static final Function TRUE = new Fixed0ArgFunction() {
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
|
||||
return BoolEval.TRUE;
|
||||
}
|
||||
};
|
||||
public static final Function NOT = new Fixed1ArgFunction() {
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
boolean boolArgVal;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
Boolean b = OperandResolver.coerceValueToBoolean(ve, false);
|
||||
boolArgVal = b == null ? false : b.booleanValue();
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
|
||||
return BoolEval.valueOf(!boolArgVal);
|
||||
}
|
||||
};
|
||||
}
|
@ -1,87 +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.functions;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
|
||||
/**
|
||||
* Implementation of Excel functions DAY, MONTH and YEAR
|
||||
*
|
||||
*
|
||||
* @author Guenter Kickinger g.kickinger@gmx.net
|
||||
*/
|
||||
public final class CalendarFieldFunction extends Fixed1ArgFunction {
|
||||
|
||||
public static final Function YEAR = new CalendarFieldFunction(Calendar.YEAR, false);
|
||||
public static final Function MONTH = new CalendarFieldFunction(Calendar.MONTH, true);
|
||||
public static final Function DAY = new CalendarFieldFunction(Calendar.DAY_OF_MONTH, false);
|
||||
|
||||
private final int _dateFieldId;
|
||||
private final boolean _needsOneBaseAdjustment;
|
||||
|
||||
private CalendarFieldFunction(int dateFieldId, boolean needsOneBaseAdjustment) {
|
||||
_dateFieldId = dateFieldId;
|
||||
_needsOneBaseAdjustment = needsOneBaseAdjustment;
|
||||
}
|
||||
|
||||
public final ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
int val;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
val = OperandResolver.coerceValueToInt(ve);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
if (val < 0) {
|
||||
return ErrorEval.NUM_ERROR;
|
||||
}
|
||||
return new NumberEval(getCalField(val));
|
||||
}
|
||||
|
||||
private int getCalField(int serialDay) {
|
||||
if (serialDay == 0) {
|
||||
// Special weird case
|
||||
// day zero should be 31-Dec-1899, but Excel seems to think it is 0-Jan-1900
|
||||
switch (_dateFieldId) {
|
||||
case Calendar.YEAR: return 1900;
|
||||
case Calendar.MONTH: return 1;
|
||||
case Calendar.DAY_OF_MONTH: return 0;
|
||||
}
|
||||
throw new IllegalStateException("bad date field " + _dateFieldId);
|
||||
}
|
||||
Date d = DateUtil.getJavaDate(serialDay, false); // TODO fix 1900/1904 problem
|
||||
|
||||
Calendar c = new GregorianCalendar();
|
||||
c.setTime(d);
|
||||
|
||||
int result = c.get(_dateFieldId);
|
||||
if (_needsOneBaseAdjustment) {
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,57 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.MissingArgEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Choose implements Function {
|
||||
|
||||
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
||||
if (args.length < 2) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
int ix = evaluateFirstArg(args[0], srcRowIndex, srcColumnIndex);
|
||||
if (ix < 1 || ix >= args.length) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
ValueEval result = OperandResolver.getSingleValue(args[ix], srcRowIndex, srcColumnIndex);
|
||||
if (result == MissingArgEval.instance) {
|
||||
return BlankEval.instance;
|
||||
}
|
||||
return result;
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
|
||||
public static int evaluateFirstArg(ValueEval arg0, int srcRowIndex, int srcColumnIndex)
|
||||
throws EvaluationException {
|
||||
ValueEval ev = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
return OperandResolver.coerceValueToInt(ev);
|
||||
}
|
||||
}
|
@ -1,54 +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.functions;
|
||||
|
||||
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.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
public final class Column implements Function0Arg, Function1Arg {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
|
||||
return new NumberEval(srcColumnIndex+1);
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
int rnum;
|
||||
|
||||
if (arg0 instanceof AreaEval) {
|
||||
rnum = ((AreaEval) arg0).getFirstColumn();
|
||||
} else if (arg0 instanceof RefEval) {
|
||||
rnum = ((RefEval) arg0).getColumn();
|
||||
} else {
|
||||
// anything else is not valid argument
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
return new NumberEval(rnum + 1);
|
||||
}
|
||||
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
||||
switch (args.length) {
|
||||
case 1:
|
||||
return evaluate(srcRowIndex, srcColumnIndex, args[0]);
|
||||
case 0:
|
||||
return new NumberEval(srcColumnIndex+1);
|
||||
}
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
@ -1,45 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
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.ValueEval;
|
||||
import org.apache.poi.ss.formula.TwoDEval;
|
||||
|
||||
/**
|
||||
* Implementation for Excel COLUMNS function.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Columns extends Fixed1ArgFunction {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
|
||||
int result;
|
||||
if (arg0 instanceof TwoDEval) {
|
||||
result = ((TwoDEval) arg0).getWidth();
|
||||
} else if (arg0 instanceof RefEval) {
|
||||
result = 1;
|
||||
} else { // anything else is not valid argument
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
}
|
@ -1,77 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.MissingArgEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
|
||||
|
||||
/**
|
||||
* Counts the number of cells that contain numeric data within
|
||||
* the list of arguments.
|
||||
*
|
||||
* Excel Syntax
|
||||
* COUNT(value1,value2,...)
|
||||
* Value1, value2, ... are 1 to 30 arguments representing the values or ranges to be counted.
|
||||
*
|
||||
* TODO: Check this properly matches excel on edge cases
|
||||
* like formula cells, error cells etc
|
||||
*/
|
||||
public final class Count implements Function {
|
||||
|
||||
public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
|
||||
int nArgs = args.length;
|
||||
if (nArgs < 1) {
|
||||
// too few arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (nArgs > 30) {
|
||||
// too many arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
int temp = 0;
|
||||
|
||||
for(int i=0; i<nArgs; i++) {
|
||||
temp += CountUtils.countArg(args[i], predicate);
|
||||
|
||||
}
|
||||
return new NumberEval(temp);
|
||||
}
|
||||
|
||||
private static final I_MatchPredicate predicate = new I_MatchPredicate() {
|
||||
|
||||
public boolean matches(ValueEval valueEval) {
|
||||
|
||||
if(valueEval instanceof NumberEval) {
|
||||
// only numbers are counted
|
||||
return true;
|
||||
}
|
||||
if(valueEval == MissingArgEval.instance) {
|
||||
// oh yeah, and missing arguments
|
||||
return true;
|
||||
}
|
||||
|
||||
// error values and string values not counted
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,80 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.formula.TwoDEval;
|
||||
|
||||
/**
|
||||
* Common logic for COUNT, COUNTA and COUNTIF
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class CountUtils {
|
||||
|
||||
private CountUtils() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
/**
|
||||
* Common interface for the matching criteria.
|
||||
*/
|
||||
public interface I_MatchPredicate {
|
||||
boolean matches(ValueEval x);
|
||||
}
|
||||
/**
|
||||
* @return the number of evaluated cells in the range that match the specified criteria
|
||||
*/
|
||||
public static int countMatchingCellsInArea(TwoDEval areaEval, I_MatchPredicate criteriaPredicate) {
|
||||
int result = 0;
|
||||
|
||||
int height = areaEval.getHeight();
|
||||
int width = areaEval.getWidth();
|
||||
for (int rrIx=0; rrIx<height; rrIx++) {
|
||||
for (int rcIx=0; rcIx<width; rcIx++) {
|
||||
ValueEval ve = areaEval.getValue(rrIx, rcIx);
|
||||
if(criteriaPredicate.matches(ve)) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* @return 1 if the evaluated cell matches the specified criteria
|
||||
*/
|
||||
public static int countMatchingCell(RefEval refEval, I_MatchPredicate criteriaPredicate) {
|
||||
if(criteriaPredicate.matches(refEval.getInnerValueEval())) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public static int countArg(ValueEval eval, I_MatchPredicate criteriaPredicate) {
|
||||
if (eval == null) {
|
||||
throw new IllegalArgumentException("eval must not be null");
|
||||
}
|
||||
if (eval instanceof TwoDEval) {
|
||||
return countMatchingCellsInArea((TwoDEval) eval, criteriaPredicate);
|
||||
}
|
||||
if (eval instanceof RefEval) {
|
||||
return CountUtils.countMatchingCell((RefEval) eval, criteriaPredicate);
|
||||
}
|
||||
return criteriaPredicate.matches(eval) ? 1 : 0;
|
||||
}
|
||||
}
|
@ -1,72 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
|
||||
|
||||
/**
|
||||
* Counts the number of cells that contain data within the list of arguments.
|
||||
*
|
||||
* Excel Syntax
|
||||
* COUNTA(value1,value2,...)
|
||||
* Value1, value2, ... are 1 to 30 arguments representing the values or ranges to be counted.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Counta implements Function {
|
||||
|
||||
public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
|
||||
int nArgs = args.length;
|
||||
if (nArgs < 1) {
|
||||
// too few arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (nArgs > 30) {
|
||||
// too many arguments
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
int temp = 0;
|
||||
|
||||
for(int i=0; i<nArgs; i++) {
|
||||
temp += CountUtils.countArg(args[i], predicate);
|
||||
|
||||
}
|
||||
return new NumberEval(temp);
|
||||
}
|
||||
|
||||
private static final I_MatchPredicate predicate = new I_MatchPredicate() {
|
||||
|
||||
public boolean matches(ValueEval valueEval) {
|
||||
// Note - observed behavior of Excel:
|
||||
// Error values like #VALUE!, #REF!, #DIV/0!, #NAME? etc don't cause this COUNTA to return an error
|
||||
// in fact, they seem to get counted
|
||||
|
||||
if(valueEval == BlankEval.instance) {
|
||||
return false;
|
||||
}
|
||||
// Note - everything but BlankEval counts
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,60 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
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.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate;
|
||||
import org.apache.poi.ss.formula.TwoDEval;
|
||||
|
||||
/**
|
||||
* Implementation for the function COUNTBLANK
|
||||
* <p>
|
||||
* Syntax: COUNTBLANK ( range )
|
||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions">
|
||||
* <tr><th>range </th><td>is the range of cells to count blanks</td></tr>
|
||||
* </table>
|
||||
* </p>
|
||||
*
|
||||
* @author Mads Mohr Christensen
|
||||
*/
|
||||
public final class Countblank extends Fixed1ArgFunction {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
|
||||
double result;
|
||||
if (arg0 instanceof RefEval) {
|
||||
result = CountUtils.countMatchingCell((RefEval) arg0, predicate);
|
||||
} else if (arg0 instanceof TwoDEval) {
|
||||
result = CountUtils.countMatchingCellsInArea((TwoDEval) arg0, predicate);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad range arg type (" + arg0.getClass().getName() + ")");
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
private static final I_MatchPredicate predicate = new I_MatchPredicate() {
|
||||
|
||||
public boolean matches(ValueEval valueEval) {
|
||||
// Note - only BlankEval counts
|
||||
return valueEval == BlankEval.instance;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,528 +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.functions;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
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.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
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.record.formula.functions.CountUtils.I_MatchPredicate;
|
||||
import org.apache.poi.ss.formula.TwoDEval;
|
||||
import org.apache.poi.ss.usermodel.ErrorConstants;
|
||||
|
||||
/**
|
||||
* Implementation for the function COUNTIF
|
||||
* <p>
|
||||
* Syntax: COUNTIF ( range, criteria )
|
||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions">
|
||||
* <tr><th>range </th><td>is the range of cells to be counted based on the criteria</td></tr>
|
||||
* <tr><th>criteria</th><td>is used to determine which cells to count</td></tr>
|
||||
* </table>
|
||||
* </p>
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Countif extends Fixed2ArgFunction {
|
||||
|
||||
private static final class CmpOp {
|
||||
public static final int NONE = 0;
|
||||
public static final int EQ = 1;
|
||||
public static final int NE = 2;
|
||||
public static final int LE = 3;
|
||||
public static final int LT = 4;
|
||||
public static final int GT = 5;
|
||||
public static final int GE = 6;
|
||||
|
||||
public static final CmpOp OP_NONE = op("", NONE);
|
||||
public static final CmpOp OP_EQ = op("=", EQ);
|
||||
public static final CmpOp OP_NE = op("<>", NE);
|
||||
public static final CmpOp OP_LE = op("<=", LE);
|
||||
public static final CmpOp OP_LT = op("<", LT);
|
||||
public static final CmpOp OP_GT = op(">", GT);
|
||||
public static final CmpOp OP_GE = op(">=", GE);
|
||||
private final String _representation;
|
||||
private final int _code;
|
||||
|
||||
private static CmpOp op(String rep, int code) {
|
||||
return new CmpOp(rep, code);
|
||||
}
|
||||
private CmpOp(String representation, int code) {
|
||||
_representation = representation;
|
||||
_code = code;
|
||||
}
|
||||
/**
|
||||
* @return number of characters used to represent this operator
|
||||
*/
|
||||
public int getLength() {
|
||||
return _representation.length();
|
||||
}
|
||||
public int getCode() {
|
||||
return _code;
|
||||
}
|
||||
public static CmpOp getOperator(String value) {
|
||||
int len = value.length();
|
||||
if (len < 1) {
|
||||
return OP_NONE;
|
||||
}
|
||||
|
||||
char firstChar = value.charAt(0);
|
||||
|
||||
switch(firstChar) {
|
||||
case '=':
|
||||
return OP_EQ;
|
||||
case '>':
|
||||
if (len > 1) {
|
||||
switch(value.charAt(1)) {
|
||||
case '=':
|
||||
return OP_GE;
|
||||
}
|
||||
}
|
||||
return OP_GT;
|
||||
case '<':
|
||||
if (len > 1) {
|
||||
switch(value.charAt(1)) {
|
||||
case '=':
|
||||
return OP_LE;
|
||||
case '>':
|
||||
return OP_NE;
|
||||
}
|
||||
}
|
||||
return OP_LT;
|
||||
}
|
||||
return OP_NONE;
|
||||
}
|
||||
public boolean evaluate(boolean cmpResult) {
|
||||
switch (_code) {
|
||||
case NONE:
|
||||
case EQ:
|
||||
return cmpResult;
|
||||
case NE:
|
||||
return !cmpResult;
|
||||
}
|
||||
throw new RuntimeException("Cannot call boolean evaluate on non-equality operator '"
|
||||
+ _representation + "'");
|
||||
}
|
||||
public boolean evaluate(int cmpResult) {
|
||||
switch (_code) {
|
||||
case NONE:
|
||||
case EQ:
|
||||
return cmpResult == 0;
|
||||
case NE: return cmpResult != 0;
|
||||
case LT: return cmpResult < 0;
|
||||
case LE: return cmpResult <= 0;
|
||||
case GT: return cmpResult > 0;
|
||||
case GE: return cmpResult <= 0;
|
||||
}
|
||||
throw new RuntimeException("Cannot call boolean evaluate on non-equality operator '"
|
||||
+ _representation + "'");
|
||||
}
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName());
|
||||
sb.append(" [").append(_representation).append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
public String getRepresentation() {
|
||||
return _representation;
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class MatcherBase implements I_MatchPredicate {
|
||||
private final CmpOp _operator;
|
||||
|
||||
MatcherBase(CmpOp operator) {
|
||||
_operator = operator;
|
||||
}
|
||||
protected final int getCode() {
|
||||
return _operator.getCode();
|
||||
}
|
||||
protected final boolean evaluate(int cmpResult) {
|
||||
return _operator.evaluate(cmpResult);
|
||||
}
|
||||
protected final boolean evaluate(boolean cmpResult) {
|
||||
return _operator.evaluate(cmpResult);
|
||||
}
|
||||
@Override
|
||||
public final String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(_operator.getRepresentation());
|
||||
sb.append(getValueText());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
protected abstract String getValueText();
|
||||
}
|
||||
|
||||
private static final class NumberMatcher extends MatcherBase {
|
||||
|
||||
private final double _value;
|
||||
|
||||
public NumberMatcher(double value, CmpOp operator) {
|
||||
super(operator);
|
||||
_value = value;
|
||||
}
|
||||
@Override
|
||||
protected String getValueText() {
|
||||
return String.valueOf(_value);
|
||||
}
|
||||
|
||||
public boolean matches(ValueEval x) {
|
||||
double testValue;
|
||||
if(x instanceof StringEval) {
|
||||
// if the target(x) is a string, but parses as a number
|
||||
// it may still count as a match, only for the equality operator
|
||||
switch (getCode()) {
|
||||
case CmpOp.EQ:
|
||||
case CmpOp.NONE:
|
||||
break;
|
||||
case CmpOp.NE:
|
||||
// Always matches (inconsistent with above two cases).
|
||||
// for example '<>123' matches '123', '4', 'abc', etc
|
||||
return true;
|
||||
default:
|
||||
// never matches (also inconsistent with above three cases).
|
||||
// for example '>5' does not match '6',
|
||||
return false;
|
||||
}
|
||||
StringEval se = (StringEval)x;
|
||||
Double val = OperandResolver.parseDouble(se.getStringValue());
|
||||
if(val == null) {
|
||||
// x is text that is not a number
|
||||
return false;
|
||||
}
|
||||
return _value == val.doubleValue();
|
||||
} else if((x instanceof NumberEval)) {
|
||||
NumberEval ne = (NumberEval) x;
|
||||
testValue = ne.getNumberValue();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return evaluate(Double.compare(testValue, _value));
|
||||
}
|
||||
}
|
||||
private static final class BooleanMatcher extends MatcherBase {
|
||||
|
||||
private final int _value;
|
||||
|
||||
public BooleanMatcher(boolean value, CmpOp operator) {
|
||||
super(operator);
|
||||
_value = boolToInt(value);
|
||||
}
|
||||
@Override
|
||||
protected String getValueText() {
|
||||
return _value == 1 ? "TRUE" : "FALSE";
|
||||
}
|
||||
|
||||
private static int boolToInt(boolean value) {
|
||||
return value ? 1 : 0;
|
||||
}
|
||||
|
||||
public boolean matches(ValueEval x) {
|
||||
int testValue;
|
||||
if(x instanceof StringEval) {
|
||||
if (true) { // change to false to observe more intuitive behaviour
|
||||
// Note - Unlike with numbers, it seems that COUNTIF never matches
|
||||
// boolean values when the target(x) is a string
|
||||
return false;
|
||||
}
|
||||
StringEval se = (StringEval)x;
|
||||
Boolean val = parseBoolean(se.getStringValue());
|
||||
if(val == null) {
|
||||
// x is text that is not a boolean
|
||||
return false;
|
||||
}
|
||||
testValue = boolToInt(val.booleanValue());
|
||||
} else if((x instanceof BoolEval)) {
|
||||
BoolEval be = (BoolEval) x;
|
||||
testValue = boolToInt(be.getBooleanValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return evaluate(testValue - _value);
|
||||
}
|
||||
}
|
||||
private static final class ErrorMatcher extends MatcherBase {
|
||||
|
||||
private final int _value;
|
||||
|
||||
public ErrorMatcher(int errorCode, CmpOp operator) {
|
||||
super(operator);
|
||||
_value = errorCode;
|
||||
}
|
||||
@Override
|
||||
protected String getValueText() {
|
||||
return ErrorConstants.getText(_value);
|
||||
}
|
||||
|
||||
public boolean matches(ValueEval x) {
|
||||
if(x instanceof ErrorEval) {
|
||||
int testValue = ((ErrorEval)x).getErrorCode();
|
||||
return evaluate(testValue - _value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private static final class StringMatcher extends MatcherBase {
|
||||
|
||||
private final String _value;
|
||||
private final Pattern _pattern;
|
||||
|
||||
public StringMatcher(String value, CmpOp operator) {
|
||||
super(operator);
|
||||
_value = value;
|
||||
switch(operator.getCode()) {
|
||||
case CmpOp.NONE:
|
||||
case CmpOp.EQ:
|
||||
case CmpOp.NE:
|
||||
_pattern = getWildCardPattern(value);
|
||||
break;
|
||||
default:
|
||||
// pattern matching is never used for < > <= =>
|
||||
_pattern = null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected String getValueText() {
|
||||
if (_pattern == null) {
|
||||
return _value;
|
||||
}
|
||||
return _pattern.pattern();
|
||||
}
|
||||
|
||||
public boolean matches(ValueEval x) {
|
||||
if (x instanceof BlankEval) {
|
||||
switch(getCode()) {
|
||||
case CmpOp.NONE:
|
||||
case CmpOp.EQ:
|
||||
return _value.length() == 0;
|
||||
}
|
||||
// no other criteria matches a blank cell
|
||||
return false;
|
||||
}
|
||||
if(!(x instanceof StringEval)) {
|
||||
// must always be string
|
||||
// even if match str is wild, but contains only digits
|
||||
// e.g. '4*7', NumberEval(4567) does not match
|
||||
return false;
|
||||
}
|
||||
String testedValue = ((StringEval) x).getStringValue();
|
||||
if (testedValue.length() < 1 && _value.length() < 1) {
|
||||
// odd case: criteria '=' behaves differently to criteria ''
|
||||
|
||||
switch(getCode()) {
|
||||
case CmpOp.NONE: return true;
|
||||
case CmpOp.EQ: return false;
|
||||
case CmpOp.NE: return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (_pattern != null) {
|
||||
return evaluate(_pattern.matcher(testedValue).matches());
|
||||
}
|
||||
return evaluate(testedValue.compareTo(_value));
|
||||
}
|
||||
/**
|
||||
* Translates Excel countif wildcard strings into java regex strings
|
||||
* @return <code>null</code> if the specified value contains no special wildcard characters.
|
||||
*/
|
||||
private static Pattern getWildCardPattern(String value) {
|
||||
int len = value.length();
|
||||
StringBuffer sb = new StringBuffer(len);
|
||||
boolean hasWildCard = false;
|
||||
for(int i=0; i<len; i++) {
|
||||
char ch = value.charAt(i);
|
||||
switch(ch) {
|
||||
case '?':
|
||||
hasWildCard = true;
|
||||
// match exactly one character
|
||||
sb.append('.');
|
||||
continue;
|
||||
case '*':
|
||||
hasWildCard = true;
|
||||
// match one or more occurrences of any character
|
||||
sb.append(".*");
|
||||
continue;
|
||||
case '~':
|
||||
if (i+1<len) {
|
||||
ch = value.charAt(i+1);
|
||||
switch (ch) {
|
||||
case '?':
|
||||
case '*':
|
||||
hasWildCard = true;
|
||||
sb.append('[').append(ch).append(']');
|
||||
i++; // Note - incrementing loop variable here
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// else not '~?' or '~*'
|
||||
sb.append('~'); // just plain '~'
|
||||
continue;
|
||||
case '.':
|
||||
case '$':
|
||||
case '^':
|
||||
case '[':
|
||||
case ']':
|
||||
case '(':
|
||||
case ')':
|
||||
// escape literal characters that would have special meaning in regex
|
||||
sb.append("\\").append(ch);
|
||||
continue;
|
||||
}
|
||||
sb.append(ch);
|
||||
}
|
||||
if (hasWildCard) {
|
||||
return Pattern.compile(sb.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
|
||||
I_MatchPredicate mp = createCriteriaPredicate(arg1, srcRowIndex, srcColumnIndex);
|
||||
if(mp == null) {
|
||||
// If the criteria arg is a reference to a blank cell, countif always returns zero.
|
||||
return NumberEval.ZERO;
|
||||
}
|
||||
double result = countMatchingCellsInArea(arg0, mp);
|
||||
return new NumberEval(result);
|
||||
}
|
||||
/**
|
||||
* @return the number of evaluated cells in the range that match the specified criteria
|
||||
*/
|
||||
private double countMatchingCellsInArea(ValueEval rangeArg, I_MatchPredicate criteriaPredicate) {
|
||||
|
||||
if (rangeArg instanceof RefEval) {
|
||||
return CountUtils.countMatchingCell((RefEval) rangeArg, criteriaPredicate);
|
||||
} else if (rangeArg instanceof TwoDEval) {
|
||||
return CountUtils.countMatchingCellsInArea((TwoDEval) rangeArg, criteriaPredicate);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad range arg type (" + rangeArg.getClass().getName() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a criteria predicate object for the supplied criteria arg
|
||||
* @return <code>null</code> if the arg evaluates to blank.
|
||||
*/
|
||||
/* package */ static I_MatchPredicate createCriteriaPredicate(ValueEval arg, int srcRowIndex, int srcColumnIndex) {
|
||||
|
||||
ValueEval evaluatedCriteriaArg = evaluateCriteriaArg(arg, srcRowIndex, srcColumnIndex);
|
||||
|
||||
if(evaluatedCriteriaArg instanceof NumberEval) {
|
||||
return new NumberMatcher(((NumberEval)evaluatedCriteriaArg).getNumberValue(), CmpOp.OP_NONE);
|
||||
}
|
||||
if(evaluatedCriteriaArg instanceof BoolEval) {
|
||||
return new BooleanMatcher(((BoolEval)evaluatedCriteriaArg).getBooleanValue(), CmpOp.OP_NONE);
|
||||
}
|
||||
|
||||
if(evaluatedCriteriaArg instanceof StringEval) {
|
||||
return createGeneralMatchPredicate((StringEval)evaluatedCriteriaArg);
|
||||
}
|
||||
if(evaluatedCriteriaArg instanceof ErrorEval) {
|
||||
return new ErrorMatcher(((ErrorEval)evaluatedCriteriaArg).getErrorCode(), CmpOp.OP_NONE);
|
||||
}
|
||||
if(evaluatedCriteriaArg == BlankEval.instance) {
|
||||
return null;
|
||||
}
|
||||
throw new RuntimeException("Unexpected type for criteria ("
|
||||
+ evaluatedCriteriaArg.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the de-referenced criteria arg (possibly {@link ErrorEval})
|
||||
*/
|
||||
private static ValueEval evaluateCriteriaArg(ValueEval arg, int srcRowIndex, int srcColumnIndex) {
|
||||
try {
|
||||
return OperandResolver.getSingleValue(arg, srcRowIndex, (short)srcColumnIndex);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* When the second argument is a string, many things are possible
|
||||
*/
|
||||
private static I_MatchPredicate createGeneralMatchPredicate(StringEval stringEval) {
|
||||
String value = stringEval.getStringValue();
|
||||
CmpOp operator = CmpOp.getOperator(value);
|
||||
value = value.substring(operator.getLength());
|
||||
|
||||
Boolean booleanVal = parseBoolean(value);
|
||||
if(booleanVal != null) {
|
||||
return new BooleanMatcher(booleanVal.booleanValue(), operator);
|
||||
}
|
||||
|
||||
Double doubleVal = OperandResolver.parseDouble(value);
|
||||
if(doubleVal != null) {
|
||||
return new NumberMatcher(doubleVal.doubleValue(), operator);
|
||||
}
|
||||
ErrorEval ee = parseError(value);
|
||||
if (ee != null) {
|
||||
return new ErrorMatcher(ee.getErrorCode(), operator);
|
||||
}
|
||||
|
||||
//else - just a plain string with no interpretation.
|
||||
return new StringMatcher(value, operator);
|
||||
}
|
||||
private static ErrorEval parseError(String value) {
|
||||
if (value.length() < 4 || value.charAt(0) != '#') {
|
||||
return null;
|
||||
}
|
||||
if (value.equals("#NULL!")) return ErrorEval.NULL_INTERSECTION;
|
||||
if (value.equals("#DIV/0!")) return ErrorEval.DIV_ZERO;
|
||||
if (value.equals("#VALUE!")) return ErrorEval.VALUE_INVALID;
|
||||
if (value.equals("#REF!")) return ErrorEval.REF_INVALID;
|
||||
if (value.equals("#NAME?")) return ErrorEval.NAME_INVALID;
|
||||
if (value.equals("#NUM!")) return ErrorEval.NUM_ERROR;
|
||||
if (value.equals("#N/A")) return ErrorEval.NA;
|
||||
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Boolean literals ('TRUE', 'FALSE') treated similarly but NOT same as numbers.
|
||||
*/
|
||||
/* package */ static Boolean parseBoolean(String strRep) {
|
||||
if (strRep.length() < 1) {
|
||||
return null;
|
||||
}
|
||||
switch(strRep.charAt(0)) {
|
||||
case 't':
|
||||
case 'T':
|
||||
if("TRUE".equalsIgnoreCase(strRep)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
if("FALSE".equalsIgnoreCase(strRep)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,92 +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.functions;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation for the Excel function DATE
|
||||
*
|
||||
* @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
|
||||
*/
|
||||
public final class DateFunc extends Fixed3ArgFunction {
|
||||
|
||||
public static final Function instance = new DateFunc();
|
||||
|
||||
private DateFunc() {
|
||||
// no fields to initialise
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2) {
|
||||
double result;
|
||||
try {
|
||||
double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
double d2 = NumericFunction.singleOperandEvaluate(arg2, srcRowIndex, srcColumnIndex);
|
||||
result = evaluate(getYear(d0), (int) (d1 - 1), (int) d2);
|
||||
NumericFunction.checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
private static double evaluate(int year, int month, int pDay) throws EvaluationException {
|
||||
|
||||
if (year < 0 || month < 0 || pDay < 0) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
|
||||
if (year == 1900 && month == Calendar.FEBRUARY && pDay == 29) {
|
||||
return 60.0;
|
||||
}
|
||||
|
||||
int day = pDay;
|
||||
if (year == 1900) {
|
||||
if ((month == Calendar.JANUARY && day >= 60) ||
|
||||
(month == Calendar.FEBRUARY && day >= 30)) {
|
||||
day--;
|
||||
}
|
||||
}
|
||||
|
||||
Calendar c = new GregorianCalendar();
|
||||
|
||||
c.set(year, month, day, 0, 0, 0);
|
||||
c.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
return DateUtil.getExcelDate(c.getTime(), false); // TODO - fix 1900/1904 problem
|
||||
}
|
||||
|
||||
private static int getYear(double d) {
|
||||
int year = (int)d;
|
||||
|
||||
if (year < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return year < 1900 ? 1900 + year : year;
|
||||
}
|
||||
}
|
@ -1,118 +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.functions;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
|
||||
/**
|
||||
* Calculates the number of days between two dates based on a 360-day year
|
||||
* (twelve 30-day months), which is used in some accounting calculations. Use
|
||||
* this function to help compute payments if your accounting system is based on
|
||||
* twelve 30-day months.
|
||||
*
|
||||
* @author PUdalau
|
||||
*/
|
||||
public class Days360 extends Var2or3ArgFunction {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
double result;
|
||||
try {
|
||||
double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
result = evaluate(d0, d1, false);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2) {
|
||||
double result;
|
||||
try {
|
||||
double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg2, srcRowIndex, srcColumnIndex);
|
||||
Boolean method = OperandResolver.coerceValueToBoolean(ve, false);
|
||||
result = evaluate(d0, d1, method == null ? false : method.booleanValue());
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
private static double evaluate(double d0, double d1, boolean method) {
|
||||
Calendar startingDate = getStartingDate(d0);
|
||||
Calendar endingDate = getEndingDateAccordingToStartingDate(d1, startingDate);
|
||||
long startingDay = startingDate.get(Calendar.MONTH) * 30 + startingDate.get(Calendar.DAY_OF_MONTH);
|
||||
long endingDay = (endingDate.get(Calendar.YEAR) - startingDate.get(Calendar.YEAR)) * 360
|
||||
+ endingDate.get(Calendar.MONTH) * 30 + endingDate.get(Calendar.DAY_OF_MONTH);
|
||||
return endingDay - startingDay;
|
||||
}
|
||||
|
||||
private static Calendar getDate(double date) {
|
||||
Calendar processedDate = new GregorianCalendar();
|
||||
processedDate.setTime(DateUtil.getJavaDate(date, false));
|
||||
return processedDate;
|
||||
}
|
||||
|
||||
private static Calendar getStartingDate(double date) {
|
||||
Calendar startingDate = getDate(date);
|
||||
if (isLastDayOfMonth(startingDate)) {
|
||||
startingDate.set(Calendar.DAY_OF_MONTH, 30);
|
||||
}
|
||||
return startingDate;
|
||||
}
|
||||
|
||||
private static Calendar getEndingDateAccordingToStartingDate(double date, Calendar startingDate) {
|
||||
Calendar endingDate = getDate(date);
|
||||
endingDate.setTime(DateUtil.getJavaDate(date, false));
|
||||
if (isLastDayOfMonth(endingDate)) {
|
||||
if (startingDate.get(Calendar.DATE) < 30) {
|
||||
endingDate = getFirstDayOfNextMonth(endingDate);
|
||||
}
|
||||
}
|
||||
return endingDate;
|
||||
}
|
||||
|
||||
private static boolean isLastDayOfMonth(Calendar date) {
|
||||
Calendar clone = (Calendar) date.clone();
|
||||
clone.add(java.util.Calendar.MONTH, 1);
|
||||
clone.add(java.util.Calendar.DAY_OF_MONTH, -1);
|
||||
int lastDayOfMonth = clone.get(Calendar.DAY_OF_MONTH);
|
||||
return date.get(Calendar.DAY_OF_MONTH) == lastDayOfMonth;
|
||||
}
|
||||
|
||||
private static Calendar getFirstDayOfNextMonth(Calendar date) {
|
||||
Calendar newDate = (Calendar) date.clone();
|
||||
if (date.get(Calendar.MONTH) < Calendar.DECEMBER) {
|
||||
newDate.set(Calendar.MONTH, date.get(Calendar.MONTH) + 1);
|
||||
} else {
|
||||
newDate.set(Calendar.MONTH, 1);
|
||||
newDate.set(Calendar.YEAR, date.get(Calendar.YEAR) + 1);
|
||||
}
|
||||
newDate.set(Calendar.DATE, 1);
|
||||
return newDate;
|
||||
}
|
||||
}
|
@ -1,79 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.usermodel.ErrorConstants;
|
||||
|
||||
/**
|
||||
* Implementation for the ERROR.TYPE() Excel function.
|
||||
* <p>
|
||||
* <b>Syntax:</b><br/>
|
||||
* <b>ERROR.TYPE</b>(<b>errorValue</b>)</p>
|
||||
* <p>
|
||||
* Returns a number corresponding to the error type of the supplied argument.<p/>
|
||||
* <p>
|
||||
* <table border="1" cellpadding="1" cellspacing="1" summary="Return values for ERROR.TYPE()">
|
||||
* <tr><td>errorValue</td><td>Return Value</td></tr>
|
||||
* <tr><td>#NULL!</td><td>1</td></tr>
|
||||
* <tr><td>#DIV/0!</td><td>2</td></tr>
|
||||
* <tr><td>#VALUE!</td><td>3</td></tr>
|
||||
* <tr><td>#REF!</td><td>4</td></tr>
|
||||
* <tr><td>#NAME?</td><td>5</td></tr>
|
||||
* <tr><td>#NUM!</td><td>6</td></tr>
|
||||
* <tr><td>#N/A!</td><td>7</td></tr>
|
||||
* <tr><td>everything else</td><td>#N/A!</td></tr>
|
||||
* </table>
|
||||
*
|
||||
* Note - the results of ERROR.TYPE() are different to the constants defined in
|
||||
* <tt>ErrorConstants</tt>.
|
||||
* </p>
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Errortype extends Fixed1ArgFunction {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
|
||||
try {
|
||||
OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
return ErrorEval.NA;
|
||||
} catch (EvaluationException e) {
|
||||
int result = translateErrorCodeToErrorTypeValue(e.getErrorEval().getErrorCode());
|
||||
return new NumberEval(result);
|
||||
}
|
||||
}
|
||||
|
||||
private int translateErrorCodeToErrorTypeValue(int errorCode) {
|
||||
switch (errorCode) {
|
||||
case ErrorConstants.ERROR_NULL: return 1;
|
||||
case ErrorConstants.ERROR_DIV_0: return 2;
|
||||
case ErrorConstants.ERROR_VALUE: return 3;
|
||||
case ErrorConstants.ERROR_REF: return 4;
|
||||
case ErrorConstants.ERROR_NAME: return 5;
|
||||
case ErrorConstants.ERROR_NUM: return 6;
|
||||
case ErrorConstants.ERROR_NA : return 7;
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid error code (" + errorCode + ")");
|
||||
}
|
||||
|
||||
}
|
@ -1,49 +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.functions;
|
||||
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public final class Even extends NumericFunction.OneArg {
|
||||
|
||||
private static final long PARITY_MASK = 0xFFFFFFFFFFFFFFFEL;
|
||||
|
||||
protected double evaluate(double d) {
|
||||
if (d==0) {
|
||||
return 0;
|
||||
}
|
||||
long result;
|
||||
if (d>0) {
|
||||
result = calcEven(d);
|
||||
} else {
|
||||
result = -calcEven(-d);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static long calcEven(double d) {
|
||||
long x = ((long) d) & PARITY_MASK;
|
||||
if (x == d) {
|
||||
return x;
|
||||
}
|
||||
return x + 2;
|
||||
}
|
||||
}
|
@ -1,118 +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.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.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public abstract class FinanceFunction implements Function3Arg, Function4Arg {
|
||||
private static final ValueEval DEFAULT_ARG3 = NumberEval.ZERO;
|
||||
private static final ValueEval DEFAULT_ARG4 = BoolEval.FALSE;
|
||||
|
||||
|
||||
protected FinanceFunction() {
|
||||
// no instance fields
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2) {
|
||||
return evaluate(srcRowIndex, srcColumnIndex, arg0, arg1, arg2, DEFAULT_ARG3);
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2, ValueEval arg3) {
|
||||
return evaluate(srcRowIndex, srcColumnIndex, arg0, arg1, arg2, arg3, DEFAULT_ARG4);
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2, ValueEval arg3, ValueEval arg4) {
|
||||
double result;
|
||||
try {
|
||||
double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
double d2 = NumericFunction.singleOperandEvaluate(arg2, srcRowIndex, srcColumnIndex);
|
||||
double d3 = NumericFunction.singleOperandEvaluate(arg3, srcRowIndex, srcColumnIndex);
|
||||
double d4 = NumericFunction.singleOperandEvaluate(arg4, srcRowIndex, srcColumnIndex);
|
||||
result = evaluate(d0, d1, d2, d3, d4 != 0.0);
|
||||
NumericFunction.checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
||||
switch (args.length) {
|
||||
case 3:
|
||||
return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], DEFAULT_ARG3, DEFAULT_ARG4);
|
||||
case 4:
|
||||
return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], args[3], DEFAULT_ARG4);
|
||||
case 5:
|
||||
return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], args[3], args[4]);
|
||||
}
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
protected double evaluate(double[] ds) throws EvaluationException {
|
||||
// All finance functions have 3 to 5 args, first 4 are numbers, last is boolean
|
||||
// default for last 2 args are 0.0 and false
|
||||
// Text boolean literals are not valid for the last arg
|
||||
|
||||
double arg3 = 0.0;
|
||||
double arg4 = 0.0;
|
||||
|
||||
switch(ds.length) {
|
||||
case 5:
|
||||
arg4 = ds[4];
|
||||
case 4:
|
||||
arg3 = ds[3];
|
||||
case 3:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Wrong number of arguments");
|
||||
}
|
||||
return evaluate(ds[0], ds[1], ds[2], arg3, arg4!=0.0);
|
||||
}
|
||||
|
||||
protected abstract double evaluate(double rate, double arg1, double arg2, double arg3, boolean type) throws EvaluationException ;
|
||||
|
||||
|
||||
public static final Function FV = new FinanceFunction() {
|
||||
protected double evaluate(double rate, double arg1, double arg2, double arg3, boolean type) {
|
||||
return FinanceLib.fv(rate, arg1, arg2, arg3, type);
|
||||
}
|
||||
};
|
||||
public static final Function NPER = new FinanceFunction() {
|
||||
protected double evaluate(double rate, double arg1, double arg2, double arg3, boolean type) {
|
||||
return FinanceLib.nper(rate, arg1, arg2, arg3, type);
|
||||
}
|
||||
};
|
||||
public static final Function PMT = new FinanceFunction() {
|
||||
protected double evaluate(double rate, double arg1, double arg2, double arg3, boolean type) {
|
||||
return FinanceLib.pmt(rate, arg1, arg2, arg3, type);
|
||||
}
|
||||
};
|
||||
public static final Function PV = new FinanceFunction() {
|
||||
protected double evaluate(double rate, double arg1, double arg2, double arg3, boolean type) {
|
||||
return FinanceLib.pv(rate, arg1, arg2, arg3, type);
|
||||
}
|
||||
};
|
||||
}
|
@ -1,185 +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.functions;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*
|
||||
* This class is a functon library for common fiscal functions.
|
||||
* <b>Glossary of terms/abbreviations:</b>
|
||||
* <br/>
|
||||
* <ul>
|
||||
* <li><em>FV:</em> Future Value</li>
|
||||
* <li><em>PV:</em> Present Value</li>
|
||||
* <li><em>NPV:</em> Net Present Value</li>
|
||||
* <li><em>PMT:</em> (Periodic) Payment</li>
|
||||
*
|
||||
* </ul>
|
||||
* For more info on the terms/abbreviations please use the references below
|
||||
* (hyperlinks are subject to change):
|
||||
* </br>Online References:
|
||||
* <ol>
|
||||
* <li>GNU Emacs Calc 2.02 Manual: http://theory.uwinnipeg.ca/gnu/calc/calc_203.html</li>
|
||||
* <li>Yahoo Financial Glossary: http://biz.yahoo.com/f/g/nn.html#y</li>
|
||||
* <li>MS Excel function reference: http://office.microsoft.com/en-us/assistance/CH062528251033.aspx</li>
|
||||
* </ol>
|
||||
* <h3>Implementation Notes:</h3>
|
||||
* Symbols used in the formulae that follow:<br/>
|
||||
* <ul>
|
||||
* <li>p: present value</li>
|
||||
* <li>f: future value</li>
|
||||
* <li>n: number of periods</li>
|
||||
* <li>y: payment (in each period)</li>
|
||||
* <li>r: rate</li>
|
||||
* <li>^: the power operator (NOT the java bitwise XOR operator!)</li>
|
||||
* </ul>
|
||||
* [From MS Excel function reference] Following are some of the key formulas
|
||||
* that are used in this implementation:
|
||||
* <pre>
|
||||
* p(1+r)^n + y(1+rt)((1+r)^n-1)/r + f=0 ...{when r!=0}
|
||||
* ny + p + f=0 ...{when r=0}
|
||||
* </pre>
|
||||
*/
|
||||
final class FinanceLib {
|
||||
|
||||
private FinanceLib() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
/**
|
||||
* Future value of an amount given the number of payments, rate, amount
|
||||
* of individual payment, present value and boolean value indicating whether
|
||||
* payments are due at the beginning of period
|
||||
* (false => payments are due at end of period)
|
||||
* @param r rate
|
||||
* @param n num of periods
|
||||
* @param y pmt per period
|
||||
* @param p future value
|
||||
* @param t type (true=pmt at end of period, false=pmt at begining of period)
|
||||
*/
|
||||
public static double fv(double r, double n, double y, double p, boolean t) {
|
||||
double retval = 0;
|
||||
if (r == 0) {
|
||||
retval = -1*(p+(n*y));
|
||||
}
|
||||
else {
|
||||
double r1 = r + 1;
|
||||
retval =((1-Math.pow(r1, n)) * (t ? r1 : 1) * y ) / r
|
||||
-
|
||||
p*Math.pow(r1, n);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Present value of an amount given the number of future payments, rate, amount
|
||||
* of individual payment, future value and boolean value indicating whether
|
||||
* payments are due at the beginning of period
|
||||
* (false => payments are due at end of period)
|
||||
* @param r
|
||||
* @param n
|
||||
* @param y
|
||||
* @param f
|
||||
* @param t
|
||||
*/
|
||||
public static double pv(double r, double n, double y, double f, boolean t) {
|
||||
double retval = 0;
|
||||
if (r == 0) {
|
||||
retval = -1*((n*y)+f);
|
||||
}
|
||||
else {
|
||||
double r1 = r + 1;
|
||||
retval =(( ( 1 - Math.pow(r1, n) ) / r ) * (t ? r1 : 1) * y - f)
|
||||
/
|
||||
Math.pow(r1, n);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* calculates the Net Present Value of a principal amount
|
||||
* given the discount rate and a sequence of cash flows
|
||||
* (supplied as an array). If the amounts are income the value should
|
||||
* be positive, else if they are payments and not income, the
|
||||
* value should be negative.
|
||||
* @param r
|
||||
* @param cfs cashflow amounts
|
||||
*/
|
||||
public static double npv(double r, double[] cfs) {
|
||||
double npv = 0;
|
||||
double r1 = r + 1;
|
||||
double trate = r1;
|
||||
for (int i=0, iSize=cfs.length; i<iSize; i++) {
|
||||
npv += cfs[i] / trate;
|
||||
trate *= r1;
|
||||
}
|
||||
return npv;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param r
|
||||
* @param n
|
||||
* @param p
|
||||
* @param f
|
||||
* @param t
|
||||
*/
|
||||
public static double pmt(double r, double n, double p, double f, boolean t) {
|
||||
double retval = 0;
|
||||
if (r == 0) {
|
||||
retval = -1*(f+p)/n;
|
||||
}
|
||||
else {
|
||||
double r1 = r + 1;
|
||||
retval = ( f + p * Math.pow(r1, n) ) * r
|
||||
/
|
||||
((t ? r1 : 1) * (1 - Math.pow(r1, n)));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param r
|
||||
* @param y
|
||||
* @param p
|
||||
* @param f
|
||||
* @param t
|
||||
*/
|
||||
public static double nper(double r, double y, double p, double f, boolean t) {
|
||||
double retval = 0;
|
||||
if (r == 0) {
|
||||
retval = -1 * (f + p) / y;
|
||||
} else {
|
||||
double r1 = r + 1;
|
||||
double ryr = (t ? r1 : 1) * y / r;
|
||||
double a1 = ((ryr - f) < 0)
|
||||
? Math.log(f - ryr)
|
||||
: Math.log(ryr - f);
|
||||
double a2 = ((ryr - f) < 0)
|
||||
? Math.log(-p - ryr)
|
||||
: Math.log(p + ryr);
|
||||
double a3 = Math.log(r1);
|
||||
retval = (a1 - a2) / a3;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,35 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Convenience base class for functions that only take zero arguments.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public abstract class Fixed0ArgFunction implements Function0Arg {
|
||||
public final ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
||||
if (args.length != 0) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return evaluate(srcRowIndex, srcColumnIndex);
|
||||
}
|
||||
}
|
@ -1,35 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Convenience base class for functions that must take exactly one argument.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public abstract class Fixed1ArgFunction implements Function1Arg {
|
||||
public final ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
||||
if (args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return evaluate(srcRowIndex, srcColumnIndex, args[0]);
|
||||
}
|
||||
}
|
@ -1,35 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Convenience base class for functions that must take exactly two arguments.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public abstract class Fixed2ArgFunction implements Function2Arg {
|
||||
public final ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
||||
if (args.length != 2) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1]);
|
||||
}
|
||||
}
|
@ -1,35 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Convenience base class for functions that must take exactly three arguments.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public abstract class Fixed3ArgFunction implements Function3Arg {
|
||||
public final ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
||||
if (args.length != 3) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2]);
|
||||
}
|
||||
}
|
@ -1,35 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Convenience base class for functions that must take exactly four arguments.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public abstract class Fixed4ArgFunction implements Function4Arg {
|
||||
public final ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
||||
if (args.length != 4) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], args[3]);
|
||||
}
|
||||
}
|
@ -1,51 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.formula.OperationEvaluationContext;
|
||||
|
||||
|
||||
/**
|
||||
* 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/>
|
||||
*
|
||||
* When POI evaluates formulas, each reference argument is capable of evaluating any cell inside
|
||||
* its range. Actually, even cells outside the reference range but on the same sheet can be
|
||||
* evaluated. This allows <b>OFFSET</b> to be implemented like most other functions - taking only
|
||||
* the arguments, and source cell coordinates.
|
||||
*
|
||||
* For the moment this interface only exists to serve the <b>INDIRECT</b> which can decode
|
||||
* arbitrary text into cell references, and evaluate them..
|
||||
*
|
||||
* @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 ec primarily used to identify the source cell containing the formula being evaluated.
|
||||
* may also be used to dynamically create reference evals.
|
||||
* @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(ValueEval[] args, OperationEvaluationContext ec);
|
||||
}
|
@ -1,43 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.MissingArgEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Common interface for all implementations of Excel built-in functions.
|
||||
*
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public interface Function {
|
||||
|
||||
/**
|
||||
* @param args the evaluated function arguments. Empty values are represented with
|
||||
* {@link BlankEval} or {@link MissingArgEval}, never <code>null</code>.
|
||||
* @param srcRowIndex row index of the cell containing the formula under evaluation
|
||||
* @param srcColumnIndex column index of the cell containing the formula under evaluation
|
||||
* @return The evaluated result, possibly an {@link ErrorEval}, never <code>null</code>.
|
||||
* <b>Note</b> - Excel uses the error code <i>#NUM!</i> instead of IEEE <i>NaN</i>, so when
|
||||
* numeric functions evaluate to {@link Double#NaN} be sure to translate the result to {@link
|
||||
* ErrorEval#NUM_ERROR}.
|
||||
*/
|
||||
ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex);
|
||||
}
|
@ -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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Implemented by all functions that can be called with zero arguments
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public interface Function0Arg extends Function {
|
||||
/**
|
||||
* see {@link Function#evaluate(ValueEval[], int, int)}
|
||||
*/
|
||||
ValueEval evaluate(int srcRowIndex, int srcColumnIndex);
|
||||
}
|
@ -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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Implemented by all functions that can be called with one argument
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public interface Function1Arg extends Function {
|
||||
/**
|
||||
* see {@link Function#evaluate(ValueEval[], int, int)}
|
||||
*/
|
||||
ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0);
|
||||
}
|
@ -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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Implemented by all functions that can be called with two arguments
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public interface Function2Arg extends Function {
|
||||
/**
|
||||
* see {@link Function#evaluate(ValueEval[], int, int)}
|
||||
*/
|
||||
ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1);
|
||||
}
|
@ -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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Implemented by all functions that can be called with three arguments
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public interface Function3Arg extends Function {
|
||||
/**
|
||||
* see {@link Function#evaluate(ValueEval[], int, int)}
|
||||
*/
|
||||
ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, ValueEval arg2);
|
||||
}
|
@ -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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Implemented by all functions that can be called with four arguments
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public interface Function4Arg extends Function {
|
||||
/**
|
||||
* see {@link Function#evaluate(ValueEval[], int, int)}
|
||||
*/
|
||||
ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, ValueEval arg2, ValueEval arg3);
|
||||
}
|
@ -1,80 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
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;
|
||||
import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
|
||||
import org.apache.poi.ss.formula.TwoDEval;
|
||||
/**
|
||||
* Implementation of the HLOOKUP() function.<p/>
|
||||
*
|
||||
* HLOOKUP finds a column in a lookup table by the first row value and returns the value from another row.<br/>
|
||||
*
|
||||
* <b>Syntax</b>:<br/>
|
||||
* <b>HLOOKUP</b>(<b>lookup_value</b>, <b>table_array</b>, <b>row_index_num</b>, range_lookup)<p/>
|
||||
*
|
||||
* <b>lookup_value</b> The value to be found in the first column of the table array.<br/>
|
||||
* <b>table_array</b> An area reference for the lookup data. <br/>
|
||||
* <b>row_index_num</b> a 1 based index specifying which row value of the lookup data will be returned.<br/>
|
||||
* <b>range_lookup</b> If TRUE (default), HLOOKUP finds the largest value less than or equal to
|
||||
* the lookup_value. If FALSE, only exact matches will be considered<br/>
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Hlookup extends Var3or4ArgFunction {
|
||||
private static final ValueEval DEFAULT_ARG3 = BoolEval.TRUE;
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2) {
|
||||
return evaluate(srcRowIndex, srcColumnIndex, arg0, arg1, arg2, DEFAULT_ARG3);
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2, ValueEval arg3) {
|
||||
try {
|
||||
// Evaluation order:
|
||||
// arg0 lookup_value, arg1 table_array, arg3 range_lookup, find lookup value, arg2 row_index, fetch result
|
||||
ValueEval lookupValue = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
TwoDEval tableArray = LookupUtils.resolveTableArrayArg(arg1);
|
||||
boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcRowIndex, srcColumnIndex);
|
||||
int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createRowVector(tableArray, 0), isRangeLookup);
|
||||
int rowIndex = LookupUtils.resolveRowOrColIndexArg(arg2, srcRowIndex, srcColumnIndex);
|
||||
ValueVector resultCol = createResultColumnVector(tableArray, rowIndex);
|
||||
return resultCol.getItem(colIndex);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one column from an <tt>AreaEval</tt>
|
||||
*
|
||||
* @param rowIndex assumed to be non-negative
|
||||
*
|
||||
* @throws EvaluationException (#REF!) if colIndex is too high
|
||||
*/
|
||||
private ValueVector createResultColumnVector(TwoDEval tableArray, int rowIndex) throws EvaluationException {
|
||||
if(rowIndex >= tableArray.getHeight()) {
|
||||
throw EvaluationException.invalidRef();
|
||||
}
|
||||
return LookupUtils.createRowVector(tableArray, rowIndex);
|
||||
}
|
||||
}
|
@ -1,49 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Implementation of Excel HYPERLINK function.<p/>
|
||||
*
|
||||
* In Excel this function has special behaviour - it causes the displayed cell value to behave like
|
||||
* a hyperlink in the GUI. From an evaluation perspective however, it is very simple.<p/>
|
||||
*
|
||||
* <b>Syntax</b>:<br/>
|
||||
* <b>HYPERLINK</b>(<b>link_location</b>, friendly_name)<p/>
|
||||
*
|
||||
* <b>link_location</b> The URL of the hyperlink <br/>
|
||||
* <b>friendly_name</b> (optional) the value to display<p/>
|
||||
*
|
||||
* Returns last argument. Leaves type unchanged (does not convert to {@link StringEval}).
|
||||
*
|
||||
* @author Wayne Clingingsmith
|
||||
*/
|
||||
public final class Hyperlink extends Var1or2ArgFunction {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
return arg0;
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
// note - if last arg is MissingArgEval, result will be NumberEval.ZERO,
|
||||
// but WorkbookEvaluator does that translation
|
||||
return arg1;
|
||||
}
|
||||
}
|
@ -1,79 +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.functions;
|
||||
|
||||
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.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.MissingArgEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Implementation for the Excel function IF
|
||||
*
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public final class IfFunc extends Var2or3ArgFunction {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
boolean b;
|
||||
try {
|
||||
b = evaluateFirstArg(arg0, srcRowIndex, srcColumnIndex);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
if (b) {
|
||||
if (arg1 == MissingArgEval.instance) {
|
||||
return BlankEval.instance;
|
||||
}
|
||||
return arg1;
|
||||
}
|
||||
return BoolEval.FALSE;
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2) {
|
||||
boolean b;
|
||||
try {
|
||||
b = evaluateFirstArg(arg0, srcRowIndex, srcColumnIndex);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
if (b) {
|
||||
if (arg1 == MissingArgEval.instance) {
|
||||
return BlankEval.instance;
|
||||
}
|
||||
return arg1;
|
||||
}
|
||||
if (arg2 == MissingArgEval.instance) {
|
||||
return BlankEval.instance;
|
||||
}
|
||||
return arg2;
|
||||
}
|
||||
|
||||
public static boolean evaluateFirstArg(ValueEval arg, int srcCellRow, int srcCellCol)
|
||||
throws EvaluationException {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
Boolean b = OperandResolver.coerceValueToBoolean(ve, false);
|
||||
if (b == null) {
|
||||
return false;
|
||||
}
|
||||
return b.booleanValue();
|
||||
}
|
||||
}
|
@ -1,171 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.MissingArgEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.formula.TwoDEval;
|
||||
|
||||
/**
|
||||
* Implementation for the Excel function INDEX
|
||||
* <p>
|
||||
*
|
||||
* Syntax : <br/>
|
||||
* INDEX ( reference, row_num[, column_num [, area_num]])</br>
|
||||
* INDEX ( array, row_num[, column_num])
|
||||
* <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions">
|
||||
* <tr><th>reference</th><td>typically an area reference, possibly a union of areas</td></tr>
|
||||
* <tr><th>array</th><td>a literal array value (currently not supported)</td></tr>
|
||||
* <tr><th>row_num</th><td>selects the row within the array or area reference</td></tr>
|
||||
* <tr><th>column_num</th><td>selects column within the array or area reference. default is 1</td></tr>
|
||||
* <tr><th>area_num</th><td>used when reference is a union of areas</td></tr>
|
||||
* </table>
|
||||
* </p>
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Index implements Function2Arg, Function3Arg, Function4Arg {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
TwoDEval reference = convertFirstArg(arg0);
|
||||
|
||||
int columnIx = 0;
|
||||
try {
|
||||
int rowIx = resolveIndexArg(arg1, srcRowIndex, srcColumnIndex);
|
||||
|
||||
if (!reference.isColumn()) {
|
||||
if (!reference.isRow()) {
|
||||
// always an error with 2-D area refs
|
||||
// Note - the type of error changes if the pRowArg is negative
|
||||
return ErrorEval.REF_INVALID;
|
||||
}
|
||||
// When the two-arg version of INDEX() has been invoked and the reference
|
||||
// is a single column ref, the row arg seems to get used as the column index
|
||||
columnIx = rowIx;
|
||||
rowIx = 0;
|
||||
}
|
||||
|
||||
return getValueFromArea(reference, rowIx, columnIx);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2) {
|
||||
TwoDEval reference = convertFirstArg(arg0);
|
||||
|
||||
try {
|
||||
int columnIx = resolveIndexArg(arg2, srcRowIndex, srcColumnIndex);
|
||||
int rowIx = resolveIndexArg(arg1, srcRowIndex, srcColumnIndex);
|
||||
return getValueFromArea(reference, rowIx, columnIx);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2, ValueEval arg3) {
|
||||
throw new RuntimeException("Incomplete code"
|
||||
+ " - don't know how to support the 'area_num' parameter yet)");
|
||||
// Excel expression might look like this "INDEX( (A1:B4, C3:D6, D2:E5 ), 1, 2, 3)
|
||||
// In this example, the 3rd area would be used i.e. D2:E5, and the overall result would be E2
|
||||
// Token array might be encoded like this: MemAreaPtg, AreaPtg, AreaPtg, UnionPtg, UnionPtg, ParenthesesPtg
|
||||
// The formula parser doesn't seem to support this yet. Not sure if the evaluator does either
|
||||
}
|
||||
|
||||
private static TwoDEval convertFirstArg(ValueEval arg0) {
|
||||
ValueEval firstArg = arg0;
|
||||
if (firstArg instanceof RefEval) {
|
||||
// convert to area ref for simpler code in getValueFromArea()
|
||||
return ((RefEval)firstArg).offset(0, 0, 0, 0);
|
||||
}
|
||||
if((firstArg instanceof TwoDEval)) {
|
||||
return (TwoDEval) firstArg;
|
||||
}
|
||||
// else the other variation of this function takes an array as the first argument
|
||||
// it seems like interface 'ArrayEval' does not even exist yet
|
||||
throw new RuntimeException("Incomplete code - cannot handle first arg of type ("
|
||||
+ firstArg.getClass().getName() + ")");
|
||||
|
||||
}
|
||||
|
||||
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
||||
switch (args.length) {
|
||||
case 2:
|
||||
return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1]);
|
||||
case 3:
|
||||
return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2]);
|
||||
case 4:
|
||||
return evaluate(srcRowIndex, srcColumnIndex, args[0], args[1], args[2], args[3]);
|
||||
}
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
private static ValueEval getValueFromArea(TwoDEval ae, int pRowIx, int pColumnIx)
|
||||
throws EvaluationException {
|
||||
assert pRowIx >= 0;
|
||||
assert pColumnIx >= 0;
|
||||
|
||||
TwoDEval result = ae;
|
||||
|
||||
if (pRowIx != 0) {
|
||||
// Slightly irregular logic for bounds checking errors
|
||||
if (pRowIx > ae.getHeight()) {
|
||||
// high bounds check fail gives #REF! if arg was explicitly passed
|
||||
throw new EvaluationException(ErrorEval.REF_INVALID);
|
||||
}
|
||||
result = result.getRow(pRowIx-1);
|
||||
}
|
||||
|
||||
if (pColumnIx != 0) {
|
||||
// Slightly irregular logic for bounds checking errors
|
||||
if (pColumnIx > ae.getWidth()) {
|
||||
// high bounds check fail gives #REF! if arg was explicitly passed
|
||||
throw new EvaluationException(ErrorEval.REF_INVALID);
|
||||
}
|
||||
result = result.getColumn(pColumnIx-1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param arg a 1-based index.
|
||||
* @return the resolved 1-based index. Zero if the arg was missing or blank
|
||||
* @throws EvaluationException if the arg is an error value evaluates to a negative numeric value
|
||||
*/
|
||||
private static int resolveIndexArg(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
|
||||
|
||||
ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
if (ev == MissingArgEval.instance) {
|
||||
return 0;
|
||||
}
|
||||
if (ev == BlankEval.instance) {
|
||||
return 0;
|
||||
}
|
||||
int result = OperandResolver.coerceValueToInt(ev);
|
||||
if (result < 0) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,239 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.MissingArgEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.formula.OperationEvaluationContext;
|
||||
|
||||
/**
|
||||
* 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 static final FreeRefFunction instance = new Indirect();
|
||||
|
||||
private Indirect() {
|
||||
// enforce singleton
|
||||
}
|
||||
|
||||
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
|
||||
if (args.length < 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
boolean isA1style;
|
||||
String text;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(args[0], ec.getRowIndex(), ec
|
||||
.getColumnIndex());
|
||||
text = OperandResolver.coerceValueToString(ve);
|
||||
switch (args.length) {
|
||||
case 1:
|
||||
isA1style = true;
|
||||
break;
|
||||
case 2:
|
||||
isA1style = evaluateBooleanArg(args[1], ec);
|
||||
break;
|
||||
default:
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
|
||||
return evaluateIndirect(ec, text, isA1style);
|
||||
}
|
||||
|
||||
private static boolean evaluateBooleanArg(ValueEval arg, OperationEvaluationContext ec)
|
||||
throws EvaluationException {
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg, ec.getRowIndex(), ec.getColumnIndex());
|
||||
|
||||
if (ve == BlankEval.instance || ve == MissingArgEval.instance) {
|
||||
return false;
|
||||
}
|
||||
// numeric quantities follow standard boolean conversion rules
|
||||
// for strings, only "TRUE" and "FALSE" (case insensitive) are valid
|
||||
return OperandResolver.coerceValueToBoolean(ve, false).booleanValue();
|
||||
}
|
||||
|
||||
private static ValueEval evaluateIndirect(OperationEvaluationContext ec, String text,
|
||||
boolean isA1style) {
|
||||
// Search backwards for '!' because sheet names can contain '!'
|
||||
int plingPos = text.lastIndexOf('!');
|
||||
|
||||
String workbookName;
|
||||
String sheetName;
|
||||
String refText; // whitespace around this gets trimmed OK
|
||||
if (plingPos < 0) {
|
||||
workbookName = null;
|
||||
sheetName = null;
|
||||
refText = text;
|
||||
} else {
|
||||
String[] parts = parseWorkbookAndSheetName(text.subSequence(0, plingPos));
|
||||
if (parts == null) {
|
||||
return ErrorEval.REF_INVALID;
|
||||
}
|
||||
workbookName = parts[0];
|
||||
sheetName = parts[1];
|
||||
refText = text.substring(plingPos + 1);
|
||||
}
|
||||
|
||||
String refStrPart1;
|
||||
String refStrPart2;
|
||||
|
||||
int colonPos = refText.indexOf(':');
|
||||
if (colonPos < 0) {
|
||||
refStrPart1 = refText.trim();
|
||||
refStrPart2 = null;
|
||||
} else {
|
||||
refStrPart1 = refText.substring(0, colonPos).trim();
|
||||
refStrPart2 = refText.substring(colonPos + 1).trim();
|
||||
}
|
||||
return ec.getDynamicReference(workbookName, sheetName, refStrPart1, refStrPart2, isA1style);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of length 2: {workbookName, sheetName,}. Second element will always be
|
||||
* present. First element may be null if sheetName is unqualified.
|
||||
* Returns <code>null</code> if text cannot be parsed.
|
||||
*/
|
||||
private static String[] parseWorkbookAndSheetName(CharSequence text) {
|
||||
int lastIx = text.length() - 1;
|
||||
if (lastIx < 0) {
|
||||
return null;
|
||||
}
|
||||
if (canTrim(text)) {
|
||||
return null;
|
||||
}
|
||||
char firstChar = text.charAt(0);
|
||||
if (Character.isWhitespace(firstChar)) {
|
||||
return null;
|
||||
}
|
||||
if (firstChar == '\'') {
|
||||
// workbookName or sheetName needs quoting
|
||||
// quotes go around both
|
||||
if (text.charAt(lastIx) != '\'') {
|
||||
return null;
|
||||
}
|
||||
firstChar = text.charAt(1);
|
||||
if (Character.isWhitespace(firstChar)) {
|
||||
return null;
|
||||
}
|
||||
String wbName;
|
||||
int sheetStartPos;
|
||||
if (firstChar == '[') {
|
||||
int rbPos = text.toString().lastIndexOf(']');
|
||||
if (rbPos < 0) {
|
||||
return null;
|
||||
}
|
||||
wbName = unescapeString(text.subSequence(2, rbPos));
|
||||
if (wbName == null || canTrim(wbName)) {
|
||||
return null;
|
||||
}
|
||||
sheetStartPos = rbPos + 1;
|
||||
} else {
|
||||
wbName = null;
|
||||
sheetStartPos = 1;
|
||||
}
|
||||
|
||||
// else - just sheet name
|
||||
String sheetName = unescapeString(text.subSequence(sheetStartPos, lastIx));
|
||||
if (sheetName == null) { // note - when quoted, sheetName can
|
||||
// start/end with whitespace
|
||||
return null;
|
||||
}
|
||||
return new String[] { wbName, sheetName, };
|
||||
}
|
||||
|
||||
if (firstChar == '[') {
|
||||
int rbPos = text.toString().lastIndexOf(']');
|
||||
if (rbPos < 0) {
|
||||
return null;
|
||||
}
|
||||
CharSequence wbName = text.subSequence(1, rbPos);
|
||||
if (canTrim(wbName)) {
|
||||
return null;
|
||||
}
|
||||
CharSequence sheetName = text.subSequence(rbPos + 1, text.length());
|
||||
if (canTrim(sheetName)) {
|
||||
return null;
|
||||
}
|
||||
return new String[] { wbName.toString(), sheetName.toString(), };
|
||||
}
|
||||
// else - just sheet name
|
||||
return new String[] { null, text.toString(), };
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>null</code> if there is a syntax error in any escape sequence
|
||||
* (the typical syntax error is a single quote character not followed by another).
|
||||
*/
|
||||
private static String unescapeString(CharSequence text) {
|
||||
int len = text.length();
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
int i = 0;
|
||||
while (i < len) {
|
||||
char ch = text.charAt(i);
|
||||
if (ch == '\'') {
|
||||
// every quote must be followed by another
|
||||
i++;
|
||||
if (i >= len) {
|
||||
return null;
|
||||
}
|
||||
ch = text.charAt(i);
|
||||
if (ch != '\'') {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
sb.append(ch);
|
||||
i++;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static boolean canTrim(CharSequence text) {
|
||||
int lastIx = text.length() - 1;
|
||||
if (lastIx < 0) {
|
||||
return false;
|
||||
}
|
||||
if (Character.isWhitespace(text.charAt(0))) {
|
||||
return true;
|
||||
}
|
||||
if (Character.isWhitespace(text.charAt(lastIx))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,119 +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.functions;
|
||||
|
||||
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.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public abstract class LogicalFunction extends Fixed1ArgFunction {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
ValueEval ve;
|
||||
try {
|
||||
ve = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
} catch (EvaluationException e) {
|
||||
if (false) {
|
||||
// Note - it is more usual to propagate error codes straight to the result like this:
|
||||
return e.getErrorEval();
|
||||
// but logical functions behave a little differently
|
||||
}
|
||||
// this will usually cause a 'FALSE' result except for ISNONTEXT()
|
||||
ve = e.getErrorEval();
|
||||
}
|
||||
return BoolEval.valueOf(evaluate(ve));
|
||||
|
||||
}
|
||||
/**
|
||||
* @param arg any {@link ValueEval}, potentially {@link BlankEval} or {@link ErrorEval}.
|
||||
*/
|
||||
protected abstract boolean evaluate(ValueEval arg);
|
||||
|
||||
public static final Function ISLOGICAL = new LogicalFunction() {
|
||||
protected boolean evaluate(ValueEval arg) {
|
||||
return arg instanceof BoolEval;
|
||||
}
|
||||
};
|
||||
public static final Function ISNONTEXT = new LogicalFunction() {
|
||||
protected boolean evaluate(ValueEval arg) {
|
||||
return !(arg instanceof StringEval);
|
||||
}
|
||||
};
|
||||
public static final Function ISNUMBER = new LogicalFunction() {
|
||||
protected boolean evaluate(ValueEval arg) {
|
||||
return arg instanceof NumberEval;
|
||||
}
|
||||
};
|
||||
public static final Function ISTEXT = new LogicalFunction() {
|
||||
protected boolean evaluate(ValueEval arg) {
|
||||
return arg instanceof StringEval;
|
||||
}
|
||||
};
|
||||
|
||||
public static final Function ISBLANK = new LogicalFunction() {
|
||||
|
||||
protected boolean evaluate(ValueEval arg) {
|
||||
return arg instanceof BlankEval;
|
||||
}
|
||||
};
|
||||
|
||||
public static final Function ISERROR = new LogicalFunction() {
|
||||
|
||||
protected boolean evaluate(ValueEval arg) {
|
||||
return arg instanceof ErrorEval;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation for Excel ISNA() function.<p/>
|
||||
*
|
||||
* <b>Syntax</b>:<br/>
|
||||
* <b>ISNA</b>(<b>value</b>)<p/>
|
||||
*
|
||||
* <b>value</b> The value to be tested<br/>
|
||||
* <br/>
|
||||
* Returns <tt>TRUE</tt> if the specified value is '#N/A', <tt>FALSE</tt> otherwise.
|
||||
*/
|
||||
public static final Function ISNA = new LogicalFunction() {
|
||||
|
||||
protected boolean evaluate(ValueEval arg) {
|
||||
return arg == ErrorEval.NA;
|
||||
}
|
||||
};
|
||||
|
||||
public static final Function ISREF = new Fixed1ArgFunction() {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
if (arg0 instanceof RefEval || arg0 instanceof AreaEval) {
|
||||
return BoolEval.TRUE;
|
||||
}
|
||||
return BoolEval.FALSE;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,76 +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.functions;
|
||||
|
||||
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;
|
||||
import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
|
||||
import org.apache.poi.ss.formula.TwoDEval;
|
||||
|
||||
/**
|
||||
* Implementation of Excel function LOOKUP.<p/>
|
||||
*
|
||||
* LOOKUP finds an index row in a lookup table by the first column value and returns the value from another column.
|
||||
*
|
||||
* <b>Syntax</b>:<br/>
|
||||
* <b>VLOOKUP</b>(<b>lookup_value</b>, <b>lookup_vector</b>, result_vector)<p/>
|
||||
*
|
||||
* <b>lookup_value</b> The value to be found in the lookup vector.<br/>
|
||||
* <b>lookup_vector</> An area reference for the lookup data. <br/>
|
||||
* <b>result_vector</b> Single row or single column area reference from which the result value is chosen.<br/>
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Lookup extends Var2or3ArgFunction {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
// complex rules to choose lookupVector and resultVector from the single area ref
|
||||
throw new RuntimeException("Two arg version of LOOKUP not supported yet");
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2) {
|
||||
try {
|
||||
ValueEval lookupValue = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
TwoDEval aeLookupVector = LookupUtils.resolveTableArrayArg(arg1);
|
||||
TwoDEval aeResultVector = LookupUtils.resolveTableArrayArg(arg2);
|
||||
|
||||
ValueVector lookupVector = createVector(aeLookupVector);
|
||||
ValueVector resultVector = createVector(aeResultVector);
|
||||
if(lookupVector.getSize() > resultVector.getSize()) {
|
||||
// Excel seems to handle this by accessing past the end of the result vector.
|
||||
throw new RuntimeException("Lookup vector and result vector of differing sizes not supported yet");
|
||||
}
|
||||
int index = LookupUtils.lookupIndexOfValue(lookupValue, lookupVector, true);
|
||||
|
||||
return resultVector.getItem(index);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
|
||||
private static ValueVector createVector(TwoDEval ae) {
|
||||
ValueVector result = LookupUtils.createVector(ae);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
// extra complexity required to emulate the way LOOKUP can handles these abnormal cases.
|
||||
throw new RuntimeException("non-vector lookup or result areas not supported yet");
|
||||
}
|
||||
}
|
@ -1,603 +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.functions;
|
||||
|
||||
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.EvaluationException;
|
||||
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.OperandResolver;
|
||||
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.ss.formula.TwoDEval;
|
||||
|
||||
/**
|
||||
* Common functionality used by VLOOKUP, HLOOKUP, LOOKUP and MATCH
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class LookupUtils {
|
||||
|
||||
/**
|
||||
* Represents a single row or column within an <tt>AreaEval</tt>.
|
||||
*/
|
||||
public interface ValueVector {
|
||||
ValueEval getItem(int index);
|
||||
int getSize();
|
||||
}
|
||||
|
||||
|
||||
private static final class RowVector implements ValueVector {
|
||||
|
||||
private final TwoDEval _tableArray;
|
||||
private final int _size;
|
||||
private final int _rowIndex;
|
||||
|
||||
public RowVector(TwoDEval tableArray, int rowIndex) {
|
||||
_rowIndex = rowIndex;
|
||||
int lastRowIx = tableArray.getHeight() - 1;
|
||||
if(rowIndex < 0 || rowIndex > lastRowIx) {
|
||||
throw new IllegalArgumentException("Specified row index (" + rowIndex
|
||||
+ ") is outside the allowed range (0.." + lastRowIx + ")");
|
||||
}
|
||||
_tableArray = tableArray;
|
||||
_size = tableArray.getWidth();
|
||||
}
|
||||
|
||||
public ValueEval getItem(int index) {
|
||||
if(index > _size) {
|
||||
throw new ArrayIndexOutOfBoundsException("Specified index (" + index
|
||||
+ ") is outside the allowed range (0.." + (_size-1) + ")");
|
||||
}
|
||||
return _tableArray.getValue(_rowIndex, index);
|
||||
}
|
||||
public int getSize() {
|
||||
return _size;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ColumnVector implements ValueVector {
|
||||
|
||||
private final TwoDEval _tableArray;
|
||||
private final int _size;
|
||||
private final int _columnIndex;
|
||||
|
||||
public ColumnVector(TwoDEval tableArray, int columnIndex) {
|
||||
_columnIndex = columnIndex;
|
||||
int lastColIx = tableArray.getWidth()-1;
|
||||
if(columnIndex < 0 || columnIndex > lastColIx) {
|
||||
throw new IllegalArgumentException("Specified column index (" + columnIndex
|
||||
+ ") is outside the allowed range (0.." + lastColIx + ")");
|
||||
}
|
||||
_tableArray = tableArray;
|
||||
_size = _tableArray.getHeight();
|
||||
}
|
||||
|
||||
public ValueEval getItem(int index) {
|
||||
if(index > _size) {
|
||||
throw new ArrayIndexOutOfBoundsException("Specified index (" + index
|
||||
+ ") is outside the allowed range (0.." + (_size-1) + ")");
|
||||
}
|
||||
return _tableArray.getValue(index, _columnIndex);
|
||||
}
|
||||
public int getSize() {
|
||||
return _size;
|
||||
}
|
||||
}
|
||||
|
||||
public static ValueVector createRowVector(TwoDEval tableArray, int relativeRowIndex) {
|
||||
return new RowVector(tableArray, relativeRowIndex);
|
||||
}
|
||||
public static ValueVector createColumnVector(TwoDEval tableArray, int relativeColumnIndex) {
|
||||
return new ColumnVector(tableArray, relativeColumnIndex);
|
||||
}
|
||||
/**
|
||||
* @return <code>null</code> if the supplied area is neither a single row nor a single colum
|
||||
*/
|
||||
public static ValueVector createVector(TwoDEval ae) {
|
||||
if (ae.isColumn()) {
|
||||
return createColumnVector(ae, 0);
|
||||
}
|
||||
if (ae.isRow()) {
|
||||
return createRowVector(ae, 0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration to support <b>4</b> valued comparison results.<p/>
|
||||
* Excel lookup functions have complex behaviour in the case where the lookup array has mixed
|
||||
* types, and/or is unordered. Contrary to suggestions in some Excel documentation, there
|
||||
* does not appear to be a universal ordering across types. The binary search algorithm used
|
||||
* changes behaviour when the evaluated 'mid' value has a different type to the lookup value.<p/>
|
||||
*
|
||||
* A simple int might have done the same job, but there is risk in confusion with the well
|
||||
* known <tt>Comparable.compareTo()</tt> and <tt>Comparator.compare()</tt> which both use
|
||||
* a ubiquitous 3 value result encoding.
|
||||
*/
|
||||
public static final class CompareResult {
|
||||
private final boolean _isTypeMismatch;
|
||||
private final boolean _isLessThan;
|
||||
private final boolean _isEqual;
|
||||
private final boolean _isGreaterThan;
|
||||
|
||||
private CompareResult(boolean isTypeMismatch, int simpleCompareResult) {
|
||||
if(isTypeMismatch) {
|
||||
_isTypeMismatch = true;
|
||||
_isLessThan = false;
|
||||
_isEqual = false;
|
||||
_isGreaterThan = false;
|
||||
} else {
|
||||
_isTypeMismatch = false;
|
||||
_isLessThan = simpleCompareResult < 0;
|
||||
_isEqual = simpleCompareResult == 0;
|
||||
_isGreaterThan = simpleCompareResult > 0;
|
||||
}
|
||||
}
|
||||
public static final CompareResult TYPE_MISMATCH = new CompareResult(true, 0);
|
||||
public static final CompareResult LESS_THAN = new CompareResult(false, -1);
|
||||
public static final CompareResult EQUAL = new CompareResult(false, 0);
|
||||
public static final CompareResult GREATER_THAN = new CompareResult(false, +1);
|
||||
|
||||
public static final CompareResult valueOf(int simpleCompareResult) {
|
||||
if(simpleCompareResult < 0) {
|
||||
return LESS_THAN;
|
||||
}
|
||||
if(simpleCompareResult > 0) {
|
||||
return GREATER_THAN;
|
||||
}
|
||||
return EQUAL;
|
||||
}
|
||||
|
||||
public boolean isTypeMismatch() {
|
||||
return _isTypeMismatch;
|
||||
}
|
||||
public boolean isLessThan() {
|
||||
return _isLessThan;
|
||||
}
|
||||
public boolean isEqual() {
|
||||
return _isEqual;
|
||||
}
|
||||
public boolean isGreaterThan() {
|
||||
return _isGreaterThan;
|
||||
}
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(formatAsString());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String formatAsString() {
|
||||
if(_isTypeMismatch) {
|
||||
return "TYPE_MISMATCH";
|
||||
}
|
||||
if(_isLessThan) {
|
||||
return "LESS_THAN";
|
||||
}
|
||||
if(_isEqual) {
|
||||
return "EQUAL";
|
||||
}
|
||||
if(_isGreaterThan) {
|
||||
return "GREATER_THAN";
|
||||
}
|
||||
// toString must be reliable
|
||||
return "??error??";
|
||||
}
|
||||
}
|
||||
|
||||
public interface LookupValueComparer {
|
||||
/**
|
||||
* @return one of 4 instances or <tt>CompareResult</tt>: <tt>LESS_THAN</tt>, <tt>EQUAL</tt>,
|
||||
* <tt>GREATER_THAN</tt> or <tt>TYPE_MISMATCH</tt>
|
||||
*/
|
||||
CompareResult compareTo(ValueEval other);
|
||||
}
|
||||
|
||||
private static abstract class LookupValueComparerBase implements LookupValueComparer {
|
||||
|
||||
private final Class<? extends ValueEval> _targetClass;
|
||||
protected LookupValueComparerBase(ValueEval targetValue) {
|
||||
if(targetValue == null) {
|
||||
throw new RuntimeException("targetValue cannot be null");
|
||||
}
|
||||
_targetClass = targetValue.getClass();
|
||||
}
|
||||
public final CompareResult compareTo(ValueEval other) {
|
||||
if (other == null) {
|
||||
throw new RuntimeException("compare to value cannot be null");
|
||||
}
|
||||
if (_targetClass != other.getClass()) {
|
||||
return CompareResult.TYPE_MISMATCH;
|
||||
}
|
||||
return compareSameType(other);
|
||||
}
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(getValueAsString());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
protected abstract CompareResult compareSameType(ValueEval other);
|
||||
/** used only for debug purposes */
|
||||
protected abstract String getValueAsString();
|
||||
}
|
||||
|
||||
private static final class StringLookupComparer extends LookupValueComparerBase {
|
||||
private String _value;
|
||||
|
||||
protected StringLookupComparer(StringEval se) {
|
||||
super(se);
|
||||
_value = se.getStringValue();
|
||||
}
|
||||
protected CompareResult compareSameType(ValueEval other) {
|
||||
StringEval se = (StringEval) other;
|
||||
return CompareResult.valueOf(_value.compareToIgnoreCase(se.getStringValue()));
|
||||
}
|
||||
protected String getValueAsString() {
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
private static final class NumberLookupComparer extends LookupValueComparerBase {
|
||||
private double _value;
|
||||
|
||||
protected NumberLookupComparer(NumberEval ne) {
|
||||
super(ne);
|
||||
_value = ne.getNumberValue();
|
||||
}
|
||||
protected CompareResult compareSameType(ValueEval other) {
|
||||
NumberEval ne = (NumberEval) other;
|
||||
return CompareResult.valueOf(Double.compare(_value, ne.getNumberValue()));
|
||||
}
|
||||
protected String getValueAsString() {
|
||||
return String.valueOf(_value);
|
||||
}
|
||||
}
|
||||
private static final class BooleanLookupComparer extends LookupValueComparerBase {
|
||||
private boolean _value;
|
||||
|
||||
protected BooleanLookupComparer(BoolEval be) {
|
||||
super(be);
|
||||
_value = be.getBooleanValue();
|
||||
}
|
||||
protected CompareResult compareSameType(ValueEval other) {
|
||||
BoolEval be = (BoolEval) other;
|
||||
boolean otherVal = be.getBooleanValue();
|
||||
if(_value == otherVal) {
|
||||
return CompareResult.EQUAL;
|
||||
}
|
||||
// TRUE > FALSE
|
||||
if(_value) {
|
||||
return CompareResult.GREATER_THAN;
|
||||
}
|
||||
return CompareResult.LESS_THAN;
|
||||
}
|
||||
protected String getValueAsString() {
|
||||
return String.valueOf(_value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the third argument to VLOOKUP, or HLOOKUP (<b>col_index_num</b>
|
||||
* or <b>row_index_num</b> respectively).<br>
|
||||
* Sample behaviour:
|
||||
* <table border="0" cellpadding="1" cellspacing="2" summary="Sample behaviour">
|
||||
* <tr><th>Input Return</th><th>Value </th><th>Thrown Error</th></tr>
|
||||
* <tr><td>5</td><td>4</td><td> </td></tr>
|
||||
* <tr><td>2.9</td><td>2</td><td> </td></tr>
|
||||
* <tr><td>"5"</td><td>4</td><td> </td></tr>
|
||||
* <tr><td>"2.18e1"</td><td>21</td><td> </td></tr>
|
||||
* <tr><td>"-$2"</td><td>-3</td><td>*</td></tr>
|
||||
* <tr><td>FALSE</td><td>-1</td><td>*</td></tr>
|
||||
* <tr><td>TRUE</td><td>0</td><td> </td></tr>
|
||||
* <tr><td>"TRUE"</td><td> </td><td>#REF!</td></tr>
|
||||
* <tr><td>"abc"</td><td> </td><td>#REF!</td></tr>
|
||||
* <tr><td>""</td><td> </td><td>#REF!</td></tr>
|
||||
* <tr><td><blank></td><td> </td><td>#VALUE!</td></tr>
|
||||
* </table><br/>
|
||||
*
|
||||
* Note - out of range errors (result index too high) are handled by the caller.
|
||||
* @return column or row index as a zero-based value, never negative.
|
||||
* @throws EvaluationException when the specified arg cannot be coerced to a non-negative integer
|
||||
*/
|
||||
public static int resolveRowOrColIndexArg(ValueEval rowColIndexArg, int srcCellRow, int srcCellCol) throws EvaluationException {
|
||||
if(rowColIndexArg == null) {
|
||||
throw new IllegalArgumentException("argument must not be null");
|
||||
}
|
||||
|
||||
ValueEval veRowColIndexArg;
|
||||
try {
|
||||
veRowColIndexArg = OperandResolver.getSingleValue(rowColIndexArg, srcCellRow, (short)srcCellCol);
|
||||
} catch (EvaluationException e) {
|
||||
// All errors get translated to #REF!
|
||||
throw EvaluationException.invalidRef();
|
||||
}
|
||||
int oneBasedIndex;
|
||||
if(veRowColIndexArg instanceof StringEval) {
|
||||
StringEval se = (StringEval) veRowColIndexArg;
|
||||
String strVal = se.getStringValue();
|
||||
Double dVal = OperandResolver.parseDouble(strVal);
|
||||
if(dVal == null) {
|
||||
// String does not resolve to a number. Raise #REF! error.
|
||||
throw EvaluationException.invalidRef();
|
||||
// This includes text booleans "TRUE" and "FALSE". They are not valid.
|
||||
}
|
||||
// else - numeric value parses OK
|
||||
}
|
||||
// actual BoolEval values get interpreted as FALSE->0 and TRUE->1
|
||||
oneBasedIndex = OperandResolver.coerceValueToInt(veRowColIndexArg);
|
||||
if (oneBasedIndex < 1) {
|
||||
// note this is asymmetric with the errors when the index is too large (#REF!)
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
return oneBasedIndex - 1; // convert to zero based
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The second argument (table_array) should be an area ref, but can actually be a cell ref, in
|
||||
* which case it is interpreted as a 1x1 area ref. Other scalar values cause #VALUE! error.
|
||||
*/
|
||||
public static TwoDEval resolveTableArrayArg(ValueEval eval) throws EvaluationException {
|
||||
if (eval instanceof TwoDEval) {
|
||||
return (TwoDEval) eval;
|
||||
}
|
||||
|
||||
if(eval instanceof RefEval) {
|
||||
RefEval refEval = (RefEval) eval;
|
||||
// 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.
|
||||
return refEval.offset(0, 0, 0, 0);
|
||||
}
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves the last (optional) parameter (<b>range_lookup</b>) to the VLOOKUP and HLOOKUP functions.
|
||||
* @param rangeLookupArg must not be <code>null</code>
|
||||
*/
|
||||
public static boolean resolveRangeLookupArg(ValueEval rangeLookupArg, int srcCellRow, int srcCellCol) throws EvaluationException {
|
||||
|
||||
ValueEval valEval = OperandResolver.getSingleValue(rangeLookupArg, srcCellRow, srcCellCol);
|
||||
if(valEval instanceof BlankEval) {
|
||||
// Tricky:
|
||||
// fourth arg supplied but evaluates to blank
|
||||
// this does not get the default value
|
||||
return false;
|
||||
}
|
||||
if(valEval instanceof BoolEval) {
|
||||
// Happy day flow
|
||||
BoolEval boolEval = (BoolEval) valEval;
|
||||
return boolEval.getBooleanValue();
|
||||
}
|
||||
|
||||
if (valEval instanceof StringEval) {
|
||||
String stringValue = ((StringEval) valEval).getStringValue();
|
||||
if(stringValue.length() < 1) {
|
||||
// More trickiness:
|
||||
// Empty string is not the same as BlankEval. It causes #VALUE! error
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
// TODO move parseBoolean to OperandResolver
|
||||
Boolean b = Countif.parseBoolean(stringValue);
|
||||
if(b != null) {
|
||||
// string converted to boolean OK
|
||||
return b.booleanValue();
|
||||
}
|
||||
// Even more trickiness:
|
||||
// Note - even if the StringEval represents a number value (for example "1"),
|
||||
// Excel does not resolve it to a boolean.
|
||||
throw EvaluationException.invalidValue();
|
||||
// This is in contrast to the code below,, where NumberEvals values (for
|
||||
// example 0.01) *do* resolve to equivalent boolean values.
|
||||
}
|
||||
if (valEval instanceof NumericValueEval) {
|
||||
NumericValueEval nve = (NumericValueEval) valEval;
|
||||
// zero is FALSE, everything else is TRUE
|
||||
return 0.0 != nve.getNumberValue();
|
||||
}
|
||||
throw new RuntimeException("Unexpected eval type (" + valEval.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
public static int lookupIndexOfValue(ValueEval lookupValue, ValueVector vector, boolean isRangeLookup) throws EvaluationException {
|
||||
LookupValueComparer lookupComparer = createLookupComparer(lookupValue);
|
||||
int result;
|
||||
if(isRangeLookup) {
|
||||
result = performBinarySearch(vector, lookupComparer);
|
||||
} else {
|
||||
result = lookupIndexOfExactValue(lookupComparer, vector);
|
||||
}
|
||||
if(result < 0) {
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds first (lowest index) exact occurrence of specified value.
|
||||
* @param lookupValue the value to be found in column or row vector
|
||||
* @param vector the values to be searched. For VLOOKUP this is the first column of the
|
||||
* tableArray. For HLOOKUP this is the first row of the tableArray.
|
||||
* @return zero based index into the vector, -1 if value cannot be found
|
||||
*/
|
||||
private static int lookupIndexOfExactValue(LookupValueComparer lookupComparer, ValueVector vector) {
|
||||
|
||||
// find first occurrence of lookup value
|
||||
int size = vector.getSize();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if(lookupComparer.compareTo(vector.getItem(i)).isEqual()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encapsulates some standard binary search functionality so the unusual Excel behaviour can
|
||||
* be clearly distinguished.
|
||||
*/
|
||||
private static final class BinarySearchIndexes {
|
||||
|
||||
private int _lowIx;
|
||||
private int _highIx;
|
||||
|
||||
public BinarySearchIndexes(int highIx) {
|
||||
_lowIx = -1;
|
||||
_highIx = highIx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return -1 if the search range is empty
|
||||
*/
|
||||
public int getMidIx() {
|
||||
int ixDiff = _highIx - _lowIx;
|
||||
if(ixDiff < 2) {
|
||||
return -1;
|
||||
}
|
||||
return _lowIx + (ixDiff / 2);
|
||||
}
|
||||
|
||||
public int getLowIx() {
|
||||
return _lowIx;
|
||||
}
|
||||
public int getHighIx() {
|
||||
return _highIx;
|
||||
}
|
||||
public void narrowSearch(int midIx, boolean isLessThan) {
|
||||
if(isLessThan) {
|
||||
_highIx = midIx;
|
||||
} else {
|
||||
_lowIx = midIx;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Excel has funny behaviour when the some elements in the search vector are the wrong type.
|
||||
*
|
||||
*/
|
||||
private static int performBinarySearch(ValueVector vector, LookupValueComparer lookupComparer) {
|
||||
// both low and high indexes point to values assumed too low and too high.
|
||||
BinarySearchIndexes bsi = new BinarySearchIndexes(vector.getSize());
|
||||
|
||||
while(true) {
|
||||
int midIx = bsi.getMidIx();
|
||||
|
||||
if(midIx < 0) {
|
||||
return bsi.getLowIx();
|
||||
}
|
||||
CompareResult cr = lookupComparer.compareTo(vector.getItem(midIx));
|
||||
if(cr.isTypeMismatch()) {
|
||||
int newMidIx = handleMidValueTypeMismatch(lookupComparer, vector, bsi, midIx);
|
||||
if(newMidIx < 0) {
|
||||
continue;
|
||||
}
|
||||
midIx = newMidIx;
|
||||
cr = lookupComparer.compareTo(vector.getItem(midIx));
|
||||
}
|
||||
if(cr.isEqual()) {
|
||||
return findLastIndexInRunOfEqualValues(lookupComparer, vector, midIx, bsi.getHighIx());
|
||||
}
|
||||
bsi.narrowSearch(midIx, cr.isLessThan());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Excel seems to handle mismatched types initially by just stepping 'mid' ix forward to the
|
||||
* first compatible value.
|
||||
* @param midIx 'mid' index (value which has the wrong type)
|
||||
* @return usually -1, signifying that the BinarySearchIndex has been narrowed to the new mid
|
||||
* index. Zero or greater signifies that an exact match for the lookup value was found
|
||||
*/
|
||||
private static int handleMidValueTypeMismatch(LookupValueComparer lookupComparer, ValueVector vector,
|
||||
BinarySearchIndexes bsi, int midIx) {
|
||||
int newMid = midIx;
|
||||
int highIx = bsi.getHighIx();
|
||||
|
||||
while(true) {
|
||||
newMid++;
|
||||
if(newMid == highIx) {
|
||||
// every element from midIx to highIx was the wrong type
|
||||
// move highIx down to the low end of the mid values
|
||||
bsi.narrowSearch(midIx, true);
|
||||
return -1;
|
||||
}
|
||||
CompareResult cr = lookupComparer.compareTo(vector.getItem(newMid));
|
||||
if(cr.isLessThan() && newMid == highIx-1) {
|
||||
// move highIx down to the low end of the mid values
|
||||
bsi.narrowSearch(midIx, true);
|
||||
return -1;
|
||||
// but only when "newMid == highIx-1"? slightly weird.
|
||||
// It would seem more efficient to always do this.
|
||||
}
|
||||
if(cr.isTypeMismatch()) {
|
||||
// keep stepping over values until the right type is found
|
||||
continue;
|
||||
}
|
||||
if(cr.isEqual()) {
|
||||
return newMid;
|
||||
}
|
||||
// Note - if moving highIx down (due to lookup<vector[newMid]),
|
||||
// this execution path only moves highIx it down as far as newMid, not midIx,
|
||||
// which would be more efficient.
|
||||
bsi.narrowSearch(newMid, cr.isLessThan());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Once the binary search has found a single match, (V/H)LOOKUP steps one by one over subsequent
|
||||
* values to choose the last matching item.
|
||||
*/
|
||||
private static int findLastIndexInRunOfEqualValues(LookupValueComparer lookupComparer, ValueVector vector,
|
||||
int firstFoundIndex, int maxIx) {
|
||||
for(int i=firstFoundIndex+1; i<maxIx; i++) {
|
||||
if(!lookupComparer.compareTo(vector.getItem(i)).isEqual()) {
|
||||
return i-1;
|
||||
}
|
||||
}
|
||||
return maxIx - 1;
|
||||
}
|
||||
|
||||
public static LookupValueComparer createLookupComparer(ValueEval lookupValue) {
|
||||
|
||||
if (lookupValue == BlankEval.instance) {
|
||||
// blank eval translates to zero
|
||||
// Note - a blank eval in the lookup column/row never matches anything
|
||||
// empty string in the lookup column/row can only be matched by explicit empty string
|
||||
return new NumberLookupComparer(NumberEval.ZERO);
|
||||
}
|
||||
if (lookupValue instanceof StringEval) {
|
||||
return new StringLookupComparer((StringEval) lookupValue);
|
||||
}
|
||||
if (lookupValue instanceof NumberEval) {
|
||||
return new NumberLookupComparer((NumberEval) lookupValue);
|
||||
}
|
||||
if (lookupValue instanceof BoolEval) {
|
||||
return new BooleanLookupComparer((BoolEval) lookupValue);
|
||||
}
|
||||
throw new IllegalArgumentException("Bad lookup value type (" + lookupValue.getClass().getName() + ")");
|
||||
}
|
||||
}
|
@ -1,251 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
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.OperandResolver;
|
||||
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.record.formula.functions.LookupUtils.CompareResult;
|
||||
import org.apache.poi.hssf.record.formula.functions.LookupUtils.LookupValueComparer;
|
||||
import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
|
||||
import org.apache.poi.ss.formula.TwoDEval;
|
||||
|
||||
/**
|
||||
* Implementation for the MATCH() Excel function.<p/>
|
||||
*
|
||||
* <b>Syntax:</b><br/>
|
||||
* <b>MATCH</b>(<b>lookup_value</b>, <b>lookup_array</b>, match_type)<p/>
|
||||
*
|
||||
* Returns a 1-based index specifying at what position in the <b>lookup_array</b> the specified
|
||||
* <b>lookup_value</b> is found.<p/>
|
||||
*
|
||||
* Specific matching behaviour can be modified with the optional <b>match_type</b> parameter.
|
||||
*
|
||||
* <table border="0" cellpadding="1" cellspacing="0" summary="match_type parameter description">
|
||||
* <tr><th>Value</th><th>Matching Behaviour</th></tr>
|
||||
* <tr><td>1</td><td>(default) find the largest value that is less than or equal to lookup_value.
|
||||
* The lookup_array must be in ascending <i>order</i>*.</td></tr>
|
||||
* <tr><td>0</td><td>find the first value that is exactly equal to lookup_value.
|
||||
* The lookup_array can be in any order.</td></tr>
|
||||
* <tr><td>-1</td><td>find the smallest value that is greater than or equal to lookup_value.
|
||||
* The lookup_array must be in descending <i>order</i>*.</td></tr>
|
||||
* </table>
|
||||
*
|
||||
* * Note regarding <i>order</i> - For the <b>match_type</b> cases that require the lookup_array to
|
||||
* be ordered, MATCH() can produce incorrect results if this requirement is not met. Observed
|
||||
* behaviour in Excel is to return the lowest index value for which every item after that index
|
||||
* breaks the match rule.<br>
|
||||
* The (ascending) sort order expected by MATCH() is:<br/>
|
||||
* numbers (low to high), strings (A to Z), boolean (FALSE to TRUE)<br/>
|
||||
* MATCH() ignores all elements in the lookup_array with a different type to the lookup_value.
|
||||
* Type conversion of the lookup_array elements is never performed.
|
||||
*
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Match extends Var2or3ArgFunction {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
// default match_type is 1.0
|
||||
return eval(srcRowIndex, srcColumnIndex, arg0, arg1, 1.0);
|
||||
}
|
||||
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2) {
|
||||
|
||||
double match_type;
|
||||
|
||||
try {
|
||||
match_type = evaluateMatchTypeArg(arg2, srcRowIndex, srcColumnIndex);
|
||||
} catch (EvaluationException e) {
|
||||
// Excel/MATCH() seems to have slightly abnormal handling of errors with
|
||||
// the last parameter. Errors do not propagate up. Every error gets
|
||||
// translated into #REF!
|
||||
return ErrorEval.REF_INVALID;
|
||||
}
|
||||
|
||||
return eval(srcRowIndex, srcColumnIndex, arg0, arg1, match_type);
|
||||
}
|
||||
|
||||
private static ValueEval eval(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
double match_type) {
|
||||
boolean matchExact = match_type == 0;
|
||||
// Note - Excel does not strictly require -1 and +1
|
||||
boolean findLargestLessThanOrEqual = match_type > 0;
|
||||
|
||||
try {
|
||||
ValueEval lookupValue = OperandResolver.getSingleValue(arg0, srcRowIndex, srcColumnIndex);
|
||||
ValueVector lookupRange = evaluateLookupRange(arg1);
|
||||
int index = findIndexOfValue(lookupValue, lookupRange, matchExact, findLargestLessThanOrEqual);
|
||||
return new NumberEval(index + 1); // +1 to convert to 1-based
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SingleValueVector implements ValueVector {
|
||||
|
||||
private final ValueEval _value;
|
||||
|
||||
public SingleValueVector(ValueEval value) {
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public ValueEval getItem(int index) {
|
||||
if (index != 0) {
|
||||
throw new RuntimeException("Invalid index ("
|
||||
+ index + ") only zero is allowed");
|
||||
}
|
||||
return _value;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static ValueVector evaluateLookupRange(ValueEval eval) throws EvaluationException {
|
||||
if (eval instanceof RefEval) {
|
||||
RefEval re = (RefEval) eval;
|
||||
return new SingleValueVector(re.getInnerValueEval());
|
||||
}
|
||||
if (eval instanceof TwoDEval) {
|
||||
ValueVector result = LookupUtils.createVector((TwoDEval)eval);
|
||||
if (result == null) {
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Error handling for lookup_range arg is also unusual
|
||||
if(eval instanceof NumericValueEval) {
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
if (eval instanceof StringEval) {
|
||||
StringEval se = (StringEval) eval;
|
||||
Double d = OperandResolver.parseDouble(se.getStringValue());
|
||||
if(d == null) {
|
||||
// plain string
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
// else looks like a number
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
throw new RuntimeException("Unexpected eval type (" + eval.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static double evaluateMatchTypeArg(ValueEval arg, int srcCellRow, int srcCellCol)
|
||||
throws EvaluationException {
|
||||
ValueEval match_type = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
|
||||
if(match_type instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval)match_type);
|
||||
}
|
||||
if(match_type instanceof NumericValueEval) {
|
||||
NumericValueEval ne = (NumericValueEval) match_type;
|
||||
return ne.getNumberValue();
|
||||
}
|
||||
if (match_type instanceof StringEval) {
|
||||
StringEval se = (StringEval) match_type;
|
||||
Double d = OperandResolver.parseDouble(se.getStringValue());
|
||||
if(d == null) {
|
||||
// plain string
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
// if the string parses as a number, it is OK
|
||||
return d.doubleValue();
|
||||
}
|
||||
throw new RuntimeException("Unexpected match_type type (" + match_type.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return zero based index
|
||||
*/
|
||||
private static int findIndexOfValue(ValueEval lookupValue, ValueVector lookupRange,
|
||||
boolean matchExact, boolean findLargestLessThanOrEqual) throws EvaluationException {
|
||||
|
||||
LookupValueComparer lookupComparer = createLookupComparer(lookupValue, matchExact);
|
||||
|
||||
int size = lookupRange.getSize();
|
||||
if(matchExact) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if(lookupComparer.compareTo(lookupRange.getItem(i)).isEqual()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
|
||||
if(findLargestLessThanOrEqual) {
|
||||
// Note - backward iteration
|
||||
for (int i = size - 1; i>=0; i--) {
|
||||
CompareResult cmp = lookupComparer.compareTo(lookupRange.getItem(i));
|
||||
if(cmp.isTypeMismatch()) {
|
||||
continue;
|
||||
}
|
||||
if(!cmp.isLessThan()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
|
||||
// else - find smallest greater than or equal to
|
||||
// TODO - is binary search used for (match_type==+1) ?
|
||||
for (int i = 0; i<size; i++) {
|
||||
CompareResult cmp = lookupComparer.compareTo(lookupRange.getItem(i));
|
||||
if(cmp.isEqual()) {
|
||||
return i;
|
||||
}
|
||||
if(cmp.isGreaterThan()) {
|
||||
if(i<1) {
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
return i-1;
|
||||
}
|
||||
}
|
||||
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
|
||||
private static LookupValueComparer createLookupComparer(ValueEval lookupValue, boolean matchExact) {
|
||||
if (matchExact && lookupValue instanceof StringEval) {
|
||||
String stringValue = ((StringEval) lookupValue).getStringValue();
|
||||
if(isLookupValueWild(stringValue)) {
|
||||
throw new RuntimeException("Wildcard lookup values '" + stringValue + "' not supported yet");
|
||||
}
|
||||
|
||||
}
|
||||
return LookupUtils.createLookupComparer(lookupValue);
|
||||
}
|
||||
|
||||
private static boolean isLookupValueWild(String stringValue) {
|
||||
if(stringValue.indexOf('?') >=0 || stringValue.indexOf('*') >=0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,444 +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.functions;
|
||||
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* This class is an extension to the standard math library
|
||||
* provided by java.lang.Math class. It follows the Math class
|
||||
* in that it has a private constructor and all static methods.
|
||||
*/
|
||||
final class MathX {
|
||||
|
||||
private MathX() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a value rounded to p digits after decimal.
|
||||
* If p is negative, then the number is rounded to
|
||||
* places to the left of the decimal point. eg.
|
||||
* 10.23 rounded to -1 will give: 10. If p is zero,
|
||||
* the returned value is rounded to the nearest integral
|
||||
* value.
|
||||
* <p>If n is negative, the resulting value is obtained
|
||||
* as the round value of absolute value of n multiplied
|
||||
* by the sign value of n (@see MathX.sign(double d)).
|
||||
* Thus, -0.6666666 rounded to p=0 will give -1 not 0.
|
||||
* <p>If n is NaN, returned value is NaN.
|
||||
* @param n
|
||||
* @param p
|
||||
*/
|
||||
public static double round(double n, int p) {
|
||||
double retval;
|
||||
|
||||
if (Double.isNaN(n) || Double.isInfinite(n)) {
|
||||
retval = Double.NaN;
|
||||
}
|
||||
else {
|
||||
if (p != 0) {
|
||||
double temp = Math.pow(10, p);
|
||||
retval = Math.round(n*temp)/temp;
|
||||
}
|
||||
else {
|
||||
retval = Math.round(n);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value rounded-up to p digits after decimal.
|
||||
* If p is negative, then the number is rounded to
|
||||
* places to the left of the decimal point. eg.
|
||||
* 10.23 rounded to -1 will give: 20. If p is zero,
|
||||
* the returned value is rounded to the nearest integral
|
||||
* value.
|
||||
* <p>If n is negative, the resulting value is obtained
|
||||
* as the round-up value of absolute value of n multiplied
|
||||
* by the sign value of n (@see MathX.sign(double d)).
|
||||
* Thus, -0.2 rounded-up to p=0 will give -1 not 0.
|
||||
* <p>If n is NaN, returned value is NaN.
|
||||
* @param n
|
||||
* @param p
|
||||
*/
|
||||
public static double roundUp(double n, int p) {
|
||||
double retval;
|
||||
|
||||
if (Double.isNaN(n) || Double.isInfinite(n)) {
|
||||
retval = Double.NaN;
|
||||
}
|
||||
else {
|
||||
if (p != 0) {
|
||||
double temp = Math.pow(10, p);
|
||||
double nat = Math.abs(n*temp);
|
||||
|
||||
retval = sign(n) *
|
||||
((nat == (long) nat)
|
||||
? nat / temp
|
||||
: Math.round(nat + 0.5) / temp);
|
||||
}
|
||||
else {
|
||||
double na = Math.abs(n);
|
||||
retval = sign(n) *
|
||||
((na == (long) na)
|
||||
? na
|
||||
: (long) na + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value rounded to p digits after decimal.
|
||||
* If p is negative, then the number is rounded to
|
||||
* places to the left of the decimal point. eg.
|
||||
* 10.23 rounded to -1 will give: 10. If p is zero,
|
||||
* the returned value is rounded to the nearest integral
|
||||
* value.
|
||||
* <p>If n is negative, the resulting value is obtained
|
||||
* as the round-up value of absolute value of n multiplied
|
||||
* by the sign value of n (@see MathX.sign(double d)).
|
||||
* Thus, -0.8 rounded-down to p=0 will give 0 not -1.
|
||||
* <p>If n is NaN, returned value is NaN.
|
||||
* @param n
|
||||
* @param p
|
||||
*/
|
||||
public static double roundDown(double n, int p) {
|
||||
double retval;
|
||||
|
||||
if (Double.isNaN(n) || Double.isInfinite(n)) {
|
||||
retval = Double.NaN;
|
||||
}
|
||||
else {
|
||||
if (p != 0) {
|
||||
double temp = Math.pow(10, p);
|
||||
retval = sign(n) * Math.round((Math.abs(n)*temp) - 0.5)/temp;
|
||||
}
|
||||
else {
|
||||
retval = (long) n;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If d < 0, returns short -1
|
||||
* <br/>
|
||||
* If d > 0, returns short 1
|
||||
* <br/>
|
||||
* If d == 0, returns short 0
|
||||
* <p> If d is NaN, then 1 will be returned. It is the responsibility
|
||||
* of caller to check for d isNaN if some other value is desired.
|
||||
* @param d
|
||||
*/
|
||||
public static short sign(double d) {
|
||||
return (short) ((d == 0)
|
||||
? 0
|
||||
: (d < 0)
|
||||
? -1
|
||||
: 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* average of all values
|
||||
* @param values
|
||||
*/
|
||||
public static double average(double[] values) {
|
||||
double ave = 0;
|
||||
double sum = 0;
|
||||
for (int i=0, iSize=values.length; i<iSize; i++) {
|
||||
sum += values[i];
|
||||
}
|
||||
ave = sum / values.length;
|
||||
return ave;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* sum of all values
|
||||
* @param values
|
||||
*/
|
||||
public static double sum(double[] values) {
|
||||
double sum = 0;
|
||||
for (int i=0, iSize=values.length; i<iSize; i++) {
|
||||
sum += values[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* sum of squares of all values
|
||||
* @param values
|
||||
*/
|
||||
public static double sumsq(double[] values) {
|
||||
double sumsq = 0;
|
||||
for (int i=0, iSize=values.length; i<iSize; i++) {
|
||||
sumsq += values[i]*values[i];
|
||||
}
|
||||
return sumsq;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* product of all values
|
||||
* @param values
|
||||
*/
|
||||
public static double product(double[] values) {
|
||||
double product = 0;
|
||||
if (values!=null && values.length > 0) {
|
||||
product = 1;
|
||||
for (int i=0, iSize=values.length; i<iSize; i++) {
|
||||
product *= values[i];
|
||||
}
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* min of all values. If supplied array is zero length,
|
||||
* Double.POSITIVE_INFINITY is returned.
|
||||
* @param values
|
||||
*/
|
||||
public static double min(double[] values) {
|
||||
double min = Double.POSITIVE_INFINITY;
|
||||
for (int i=0, iSize=values.length; i<iSize; i++) {
|
||||
min = Math.min(min, values[i]);
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* min of all values. If supplied array is zero length,
|
||||
* Double.NEGATIVE_INFINITY is returned.
|
||||
* @param values
|
||||
*/
|
||||
public static double max(double[] values) {
|
||||
double max = Double.NEGATIVE_INFINITY;
|
||||
for (int i=0, iSize=values.length; i<iSize; i++) {
|
||||
max = Math.max(max, values[i]);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: this function is different from java.lang.Math.floor(..).
|
||||
* <p>
|
||||
* When n and s are "valid" arguments, the returned value is: Math.floor(n/s) * s;
|
||||
* <br/>
|
||||
* n and s are invalid if any of following conditions are true:
|
||||
* <ul>
|
||||
* <li>s is zero</li>
|
||||
* <li>n is negative and s is positive</li>
|
||||
* <li>n is positive and s is negative</li>
|
||||
* </ul>
|
||||
* In all such cases, Double.NaN is returned.
|
||||
* @param n
|
||||
* @param s
|
||||
*/
|
||||
public static double floor(double n, double s) {
|
||||
double f;
|
||||
|
||||
if ((n<0 && s>0) || (n>0 && s<0) || (s==0 && n!=0)) {
|
||||
f = Double.NaN;
|
||||
}
|
||||
else {
|
||||
f = (n==0 || s==0) ? 0 : Math.floor(n/s) * s;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: this function is different from java.lang.Math.ceil(..).
|
||||
* <p>
|
||||
* When n and s are "valid" arguments, the returned value is: Math.ceiling(n/s) * s;
|
||||
* <br/>
|
||||
* n and s are invalid if any of following conditions are true:
|
||||
* <ul>
|
||||
* <li>s is zero</li>
|
||||
* <li>n is negative and s is positive</li>
|
||||
* <li>n is positive and s is negative</li>
|
||||
* </ul>
|
||||
* In all such cases, Double.NaN is returned.
|
||||
* @param n
|
||||
* @param s
|
||||
*/
|
||||
public static double ceiling(double n, double s) {
|
||||
double c;
|
||||
|
||||
if ((n<0 && s>0) || (n>0 && s<0)) {
|
||||
c = Double.NaN;
|
||||
}
|
||||
else {
|
||||
c = (n == 0 || s == 0) ? 0 : Math.ceil(n/s) * s;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* <br/> for all n >= 1; factorial n = n * (n-1) * (n-2) * ... * 1
|
||||
* <br/> else if n == 0; factorial n = 1
|
||||
* <br/> else if n < 0; factorial n = Double.NaN
|
||||
* <br/> Loss of precision can occur if n is large enough.
|
||||
* If n is large so that the resulting value would be greater
|
||||
* than Double.MAX_VALUE; Double.POSITIVE_INFINITY is returned.
|
||||
* If n < 0, Double.NaN is returned.
|
||||
* @param n
|
||||
*/
|
||||
public static double factorial(int n) {
|
||||
double d = 1;
|
||||
|
||||
if (n >= 0) {
|
||||
if (n <= 170) {
|
||||
for (int i=1; i<=n; i++) {
|
||||
d *= i;
|
||||
}
|
||||
}
|
||||
else {
|
||||
d = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
else {
|
||||
d = Double.NaN;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* returns the remainder resulting from operation:
|
||||
* n / d.
|
||||
* <br/> The result has the sign of the divisor.
|
||||
* <br/> Examples:
|
||||
* <ul>
|
||||
* <li>mod(3.4, 2) = 1.4</li>
|
||||
* <li>mod(-3.4, 2) = 0.6</li>
|
||||
* <li>mod(-3.4, -2) = -1.4</li>
|
||||
* <li>mod(3.4, -2) = -0.6</li>
|
||||
* </ul>
|
||||
* If d == 0, result is NaN
|
||||
* @param n
|
||||
* @param d
|
||||
*/
|
||||
public static double mod(double n, double d) {
|
||||
double result = 0;
|
||||
|
||||
if (d == 0) {
|
||||
result = Double.NaN;
|
||||
}
|
||||
else if (sign(n) == sign(d)) {
|
||||
result = n % d;
|
||||
}
|
||||
else {
|
||||
result = ((n % d) + d) % d;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* inverse hyperbolic cosine
|
||||
* @param d
|
||||
*/
|
||||
public static double acosh(double d) {
|
||||
return Math.log(Math.sqrt(Math.pow(d, 2) - 1) + d);
|
||||
}
|
||||
|
||||
/**
|
||||
* inverse hyperbolic sine
|
||||
* @param d
|
||||
*/
|
||||
public static double asinh(double d) {
|
||||
return Math.log(Math.sqrt(d*d + 1) + d);
|
||||
}
|
||||
|
||||
/**
|
||||
* inverse hyperbolic tangent
|
||||
* @param d
|
||||
*/
|
||||
public static double atanh(double d) {
|
||||
return Math.log((1 + d)/(1 - d)) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* hyperbolic cosine
|
||||
* @param d
|
||||
*/
|
||||
public static double cosh(double d) {
|
||||
double ePowX = Math.pow(Math.E, d);
|
||||
double ePowNegX = Math.pow(Math.E, -d);
|
||||
return (ePowX + ePowNegX) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* hyperbolic sine
|
||||
* @param d
|
||||
*/
|
||||
public static double sinh(double d) {
|
||||
double ePowX = Math.pow(Math.E, d);
|
||||
double ePowNegX = Math.pow(Math.E, -d);
|
||||
return (ePowX - ePowNegX) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* hyperbolic tangent
|
||||
* @param d
|
||||
*/
|
||||
public static double tanh(double d) {
|
||||
double ePowX = Math.pow(Math.E, d);
|
||||
double ePowNegX = Math.pow(Math.E, -d);
|
||||
return (ePowX - ePowNegX) / (ePowX + ePowNegX);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* returns the total number of combinations possible when
|
||||
* k items are chosen out of total of n items. If the number
|
||||
* is too large, loss of precision may occur (since returned
|
||||
* value is double). If the returned value is larger than
|
||||
* Double.MAX_VALUE, Double.POSITIVE_INFINITY is returned.
|
||||
* If either of the parameters is negative, Double.NaN is returned.
|
||||
* @param n
|
||||
* @param k
|
||||
*/
|
||||
public static double nChooseK(int n, int k) {
|
||||
double d = 1;
|
||||
if (n<0 || k<0 || n<k) {
|
||||
d= Double.NaN;
|
||||
}
|
||||
else {
|
||||
int minnk = Math.min(n-k, k);
|
||||
int maxnk = Math.max(n-k, k);
|
||||
for (int i=maxnk; i<n; i++) {
|
||||
d *= i+1;
|
||||
}
|
||||
d /= factorial(minnk);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
}
|
@ -1,40 +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.functions;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public abstract class MinaMaxa extends MultiOperandNumericFunction {
|
||||
|
||||
protected MinaMaxa() {
|
||||
super(true, true);
|
||||
}
|
||||
|
||||
public static final Function MAXA = new MinaMaxa() {
|
||||
protected double evaluate(double[] values) {
|
||||
return values.length > 0 ? MathX.max(values) : 0;
|
||||
}
|
||||
};
|
||||
public static final Function MINA = new MinaMaxa() {
|
||||
protected double evaluate(double[] values) {
|
||||
return values.length > 0 ? MathX.min(values) : 0;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,133 +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.functions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
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.EvaluationException;
|
||||
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.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.formula.TwoDEval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public final class Mode implements Function {
|
||||
|
||||
/**
|
||||
* if v is zero length or contains no duplicates, return value is
|
||||
* Double.NaN. Else returns the value that occurs most times and if there is
|
||||
* a tie, returns the first such value.
|
||||
*
|
||||
* @param v
|
||||
*/
|
||||
public static double evaluate(double[] v) throws EvaluationException {
|
||||
if (v.length < 2) {
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
|
||||
// very naive impl, may need to be optimized
|
||||
int[] counts = new int[v.length];
|
||||
Arrays.fill(counts, 1);
|
||||
for (int i = 0, iSize = v.length; i < iSize; i++) {
|
||||
for (int j = i + 1, jSize = v.length; j < jSize; j++) {
|
||||
if (v[i] == v[j])
|
||||
counts[i]++;
|
||||
}
|
||||
}
|
||||
double maxv = 0;
|
||||
int maxc = 0;
|
||||
for (int i = 0, iSize = counts.length; i < iSize; i++) {
|
||||
if (counts[i] > maxc) {
|
||||
maxv = v[i];
|
||||
maxc = counts[i];
|
||||
}
|
||||
}
|
||||
if (maxc > 1) {
|
||||
return maxv;
|
||||
}
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
|
||||
}
|
||||
|
||||
public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
|
||||
double result;
|
||||
try {
|
||||
List<Double> temp = new ArrayList<Double>();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
collectValues(args[i], temp);
|
||||
}
|
||||
double[] values = new double[temp.size()];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = temp.get(i).doubleValue();
|
||||
}
|
||||
result = evaluate(values);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
private static void collectValues(ValueEval arg, List<Double> temp) throws EvaluationException {
|
||||
if (arg instanceof TwoDEval) {
|
||||
TwoDEval ae = (TwoDEval) arg;
|
||||
int width = ae.getWidth();
|
||||
int height = ae.getHeight();
|
||||
for (int rrIx = 0; rrIx < height; rrIx++) {
|
||||
for (int rcIx = 0; rcIx < width; rcIx++) {
|
||||
ValueEval ve1 = ae.getValue(rrIx, rcIx);
|
||||
collectValue(ve1, temp, false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (arg instanceof RefEval) {
|
||||
RefEval re = (RefEval) arg;
|
||||
collectValue(re.getInnerValueEval(), temp, true);
|
||||
return;
|
||||
}
|
||||
collectValue(arg, temp, true);
|
||||
|
||||
}
|
||||
|
||||
private static void collectValue(ValueEval arg, List<Double> temp, boolean mustBeNumber)
|
||||
throws EvaluationException {
|
||||
if (arg instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) arg);
|
||||
}
|
||||
if (arg == BlankEval.instance || arg instanceof BoolEval || arg instanceof StringEval) {
|
||||
if (mustBeNumber) {
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (arg instanceof NumberEval) {
|
||||
temp.add(new Double(((NumberEval) arg).getNumberValue()));
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("Unexpected value type (" + arg.getClass().getName() + ")");
|
||||
}
|
||||
}
|
@ -1,197 +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.functions;
|
||||
|
||||
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.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
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.ss.formula.TwoDEval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* This is the super class for all excel function evaluator
|
||||
* classes that take variable number of operands, and
|
||||
* where the order of operands does not matter
|
||||
*/
|
||||
public abstract class MultiOperandNumericFunction implements Function {
|
||||
|
||||
private final boolean _isReferenceBoolCounted;
|
||||
private final boolean _isBlankCounted;
|
||||
|
||||
protected MultiOperandNumericFunction(boolean isReferenceBoolCounted, boolean isBlankCounted) {
|
||||
_isReferenceBoolCounted = isReferenceBoolCounted;
|
||||
_isBlankCounted = isBlankCounted;
|
||||
}
|
||||
|
||||
static final double[] EMPTY_DOUBLE_ARRAY = { };
|
||||
|
||||
private static class DoubleList {
|
||||
private double[] _array;
|
||||
private int _count;
|
||||
|
||||
public DoubleList() {
|
||||
_array = new double[8];
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public double[] toArray() {
|
||||
if(_count < 1) {
|
||||
return EMPTY_DOUBLE_ARRAY;
|
||||
}
|
||||
double[] result = new double[_count];
|
||||
System.arraycopy(_array, 0, result, 0, _count);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ensureCapacity(int reqSize) {
|
||||
if(reqSize > _array.length) {
|
||||
int newSize = reqSize * 3 / 2; // grow with 50% extra
|
||||
double[] newArr = new double[newSize];
|
||||
System.arraycopy(_array, 0, newArr, 0, _count);
|
||||
_array = newArr;
|
||||
}
|
||||
}
|
||||
|
||||
public void add(double value) {
|
||||
ensureCapacity(_count + 1);
|
||||
_array[_count] = value;
|
||||
_count++;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int DEFAULT_MAX_NUM_OPERANDS = 30;
|
||||
|
||||
public final ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
|
||||
|
||||
double d;
|
||||
try {
|
||||
double[] values = getNumberArray(args);
|
||||
d = evaluate(values);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
|
||||
if (Double.isNaN(d) || Double.isInfinite(d))
|
||||
return ErrorEval.NUM_ERROR;
|
||||
|
||||
return new NumberEval(d);
|
||||
}
|
||||
|
||||
protected abstract double evaluate(double[] values) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Maximum number of operands accepted by this function.
|
||||
* Subclasses may override to change default value.
|
||||
*/
|
||||
protected int getMaxNumOperands() {
|
||||
return DEFAULT_MAX_NUM_OPERANDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a double array that contains values for the numeric cells
|
||||
* from among the list of operands. Blanks and Blank equivalent cells
|
||||
* are ignored. Error operands or cells containing operands of type
|
||||
* that are considered invalid and would result in #VALUE! error in
|
||||
* excel cause this function to return <code>null</code>.
|
||||
*
|
||||
* @return never <code>null</code>
|
||||
*/
|
||||
protected final double[] getNumberArray(ValueEval[] operands) throws EvaluationException {
|
||||
if (operands.length > getMaxNumOperands()) {
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
DoubleList retval = new DoubleList();
|
||||
|
||||
for (int i=0, iSize=operands.length; i<iSize; i++) {
|
||||
collectValues(operands[i], retval);
|
||||
}
|
||||
return retval.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects values from a single argument
|
||||
*/
|
||||
private void collectValues(ValueEval operand, DoubleList temp) throws EvaluationException {
|
||||
|
||||
if (operand instanceof TwoDEval) {
|
||||
TwoDEval ae = (TwoDEval) operand;
|
||||
int width = ae.getWidth();
|
||||
int height = ae.getHeight();
|
||||
for (int rrIx=0; rrIx<height; rrIx++) {
|
||||
for (int rcIx=0; rcIx<width; rcIx++) {
|
||||
ValueEval ve = ae.getValue(rrIx, rcIx);
|
||||
collectValue(ve, true, temp);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (operand instanceof RefEval) {
|
||||
RefEval re = (RefEval) operand;
|
||||
collectValue(re.getInnerValueEval(), true, temp);
|
||||
return;
|
||||
}
|
||||
collectValue(operand, false, temp);
|
||||
}
|
||||
private void collectValue(ValueEval ve, boolean isViaReference, DoubleList temp) throws EvaluationException {
|
||||
if (ve == null) {
|
||||
throw new IllegalArgumentException("ve must not be null");
|
||||
}
|
||||
if (ve instanceof NumberEval) {
|
||||
NumberEval ne = (NumberEval) ve;
|
||||
temp.add(ne.getNumberValue());
|
||||
return;
|
||||
}
|
||||
if (ve instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) ve);
|
||||
}
|
||||
if (ve instanceof StringEval) {
|
||||
if (isViaReference) {
|
||||
// ignore all ref strings
|
||||
return;
|
||||
}
|
||||
String s = ((StringEval) ve).getStringValue();
|
||||
Double d = OperandResolver.parseDouble(s);
|
||||
if(d == null) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
temp.add(d.doubleValue());
|
||||
return;
|
||||
}
|
||||
if (ve instanceof BoolEval) {
|
||||
if (!isViaReference || _isReferenceBoolCounted) {
|
||||
BoolEval boolEval = (BoolEval) ve;
|
||||
temp.add(boolEval.getNumberValue());
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ve == BlankEval.instance) {
|
||||
if (_isBlankCounted) {
|
||||
temp.add(0.0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("Invalid ValueEval type passed for conversion: ("
|
||||
+ ve.getClass() + ")");
|
||||
}
|
||||
}
|
@ -1,33 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Implementation of Excel function NA()
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class Na extends Fixed0ArgFunction {
|
||||
|
||||
public ValueEval evaluate(int srcCellRow, int srcCellCol) {
|
||||
return ErrorEval.NA;
|
||||
}
|
||||
}
|
@ -1,47 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.formula.eval.NotImplementedException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* This is the default implementation of a Function class.
|
||||
* The default behaviour is to raise a POI internal error
|
||||
* ({@link NotImplementedException}). This error should alert
|
||||
* the user that the formula contained a function that is not
|
||||
* yet implemented.
|
||||
*/
|
||||
public final class NotImplementedFunction implements Function {
|
||||
private final String _functionName;
|
||||
protected NotImplementedFunction() {
|
||||
_functionName = getClass().getName();
|
||||
}
|
||||
public NotImplementedFunction(String name) {
|
||||
_functionName = name;
|
||||
}
|
||||
|
||||
public ValueEval evaluate(ValueEval[] operands, int srcRow, int srcCol) {
|
||||
throw new NotImplementedException(_functionName);
|
||||
}
|
||||
public String getFunctionName() {
|
||||
return _functionName;
|
||||
}
|
||||
}
|
@ -1,37 +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.functions;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
|
||||
/**
|
||||
* Implementation of Excel NOW() Function
|
||||
*
|
||||
* @author Frank Taffelt
|
||||
*/
|
||||
public final class Now extends Fixed0ArgFunction {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
|
||||
Date now = new Date(System.currentTimeMillis());
|
||||
return new NumberEval(DateUtil.getExcelDate(now));
|
||||
}
|
||||
}
|
@ -1,107 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* Calculates the net present value of an investment by using a discount rate
|
||||
* and a series of future payments (negative values) and income (positive
|
||||
* values). Minimum 2 arguments, first arg is the rate of discount over the
|
||||
* length of one period others up to 254 arguments representing the payments and
|
||||
* income.
|
||||
*
|
||||
* @author SPetrakovsky
|
||||
*/
|
||||
public final class Npv implements Function2Arg, Function3Arg, Function4Arg {
|
||||
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
double result;
|
||||
try {
|
||||
double rate = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
result = evaluate(rate, d1);
|
||||
NumericFunction.checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2) {
|
||||
double result;
|
||||
try {
|
||||
double rate = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
double d2 = NumericFunction.singleOperandEvaluate(arg2, srcRowIndex, srcColumnIndex);
|
||||
result = evaluate(rate, d1, d2);
|
||||
NumericFunction.checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
||||
ValueEval arg2, ValueEval arg3) {
|
||||
double result;
|
||||
try {
|
||||
double rate = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
double d2 = NumericFunction.singleOperandEvaluate(arg2, srcRowIndex, srcColumnIndex);
|
||||
double d3 = NumericFunction.singleOperandEvaluate(arg3, srcRowIndex, srcColumnIndex);
|
||||
result = evaluate(rate, d1, d2, d3);
|
||||
NumericFunction.checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
||||
int nArgs = args.length;
|
||||
if (nArgs<2) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
int np = nArgs-1;
|
||||
double[] ds = new double[np];
|
||||
double result;
|
||||
try {
|
||||
double rate = NumericFunction.singleOperandEvaluate(args[0], srcRowIndex, srcColumnIndex);
|
||||
for (int i = 0; i < ds.length; i++) {
|
||||
ds[i] = NumericFunction.singleOperandEvaluate(args[i+1], srcRowIndex, srcColumnIndex);
|
||||
}
|
||||
result = evaluate(rate, ds);
|
||||
NumericFunction.checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
private static double evaluate(double rate, double...ds) {
|
||||
double sum = 0;
|
||||
for (int i = 0; i < ds.length; i++) {
|
||||
sum += ds[i] / Math.pow(rate + 1, i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
@ -1,495 +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.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.*;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* @author Josh Micich
|
||||
* @author Stephen Wolke (smwolke at geistig.com)
|
||||
*/
|
||||
public abstract class NumericFunction implements Function {
|
||||
|
||||
static final double ZERO = 0.0;
|
||||
static final double TEN = 10.0;
|
||||
static final double LOG_10_TO_BASE_e = Math.log(TEN);
|
||||
|
||||
protected static final double singleOperandEvaluate(ValueEval arg, int srcRowIndex, int srcColumnIndex) throws EvaluationException {
|
||||
if (arg == null) {
|
||||
throw new IllegalArgumentException("arg must not be null");
|
||||
}
|
||||
ValueEval ve = OperandResolver.getSingleValue(arg, srcRowIndex, srcColumnIndex);
|
||||
double result = OperandResolver.coerceValueToDouble(ve);
|
||||
checkValue(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws EvaluationException (#NUM!) if <tt>result</tt> is <tt>NaN</> or <tt>Infinity</tt>
|
||||
*/
|
||||
static final void checkValue(double result) throws EvaluationException {
|
||||
if (Double.isNaN(result) || Double.isInfinite(result)) {
|
||||
throw new EvaluationException(ErrorEval.NUM_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public final ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {
|
||||
double result;
|
||||
try {
|
||||
result = eval(args, srcCellRow, srcCellCol);
|
||||
checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
protected abstract double eval(ValueEval[] args, int srcCellRow, int srcCellCol) throws EvaluationException;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
// intermediate sub-classes (one-arg, two-arg and multi-arg)
|
||||
|
||||
public static abstract class OneArg extends Fixed1ArgFunction {
|
||||
protected OneArg() {
|
||||
// no fields to initialise
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
double result;
|
||||
try {
|
||||
double d = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
result = evaluate(d);
|
||||
checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
protected final double eval(ValueEval[] args, int srcCellRow, int srcCellCol) throws EvaluationException {
|
||||
if (args.length != 1) {
|
||||
throw new EvaluationException(ErrorEval.VALUE_INVALID);
|
||||
}
|
||||
double d = singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
|
||||
return evaluate(d);
|
||||
}
|
||||
protected abstract double evaluate(double d) throws EvaluationException;
|
||||
}
|
||||
|
||||
public static abstract class TwoArg extends Fixed2ArgFunction {
|
||||
protected TwoArg() {
|
||||
// no fields to initialise
|
||||
}
|
||||
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
double result;
|
||||
try {
|
||||
double d0 = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
double d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
result = evaluate(d0, d1);
|
||||
checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
|
||||
protected abstract double evaluate(double d0, double d1) throws EvaluationException;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
public static final Function ABS = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.abs(d);
|
||||
}
|
||||
};
|
||||
public static final Function ACOS = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.acos(d);
|
||||
}
|
||||
};
|
||||
public static final Function ACOSH = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return MathX.acosh(d);
|
||||
}
|
||||
};
|
||||
public static final Function ASIN = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.asin(d);
|
||||
}
|
||||
};
|
||||
public static final Function ASINH = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return MathX.asinh(d);
|
||||
}
|
||||
};
|
||||
public static final Function ATAN = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.atan(d);
|
||||
}
|
||||
};
|
||||
public static final Function ATANH = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return MathX.atanh(d);
|
||||
}
|
||||
};
|
||||
public static final Function COS = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.cos(d);
|
||||
}
|
||||
};
|
||||
public static final Function COSH = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return MathX.cosh(d);
|
||||
}
|
||||
};
|
||||
public static final Function DEGREES = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.toDegrees(d);
|
||||
}
|
||||
};
|
||||
static final NumberEval DOLLAR_ARG2_DEFAULT = new NumberEval(2.0);
|
||||
public static final Function DOLLAR = new Var1or2ArgFunction() {
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
return evaluate(srcRowIndex, srcColumnIndex, arg0, DOLLAR_ARG2_DEFAULT);
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0,
|
||||
ValueEval arg1) {
|
||||
double val;
|
||||
double d1;
|
||||
try {
|
||||
val = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
// second arg converts to int by truncating toward zero
|
||||
int nPlaces = (int)d1;
|
||||
|
||||
if (nPlaces > 127) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
|
||||
// TODO - DOLLAR() function impl is NQR
|
||||
// result should be StringEval, with leading '$' and thousands separators
|
||||
// current junits are asserting incorrect behaviour
|
||||
return new NumberEval(val);
|
||||
}
|
||||
};
|
||||
public static final Function EXP = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.pow(Math.E, d);
|
||||
}
|
||||
};
|
||||
public static final Function FACT = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return MathX.factorial((int)d);
|
||||
}
|
||||
};
|
||||
public static final Function INT = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.round(d-0.5);
|
||||
}
|
||||
};
|
||||
public static final Function LN = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.log(d);
|
||||
}
|
||||
};
|
||||
public static final Function LOG10 = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.log(d) / LOG_10_TO_BASE_e;
|
||||
}
|
||||
};
|
||||
public static final Function RADIANS = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.toRadians(d);
|
||||
}
|
||||
};
|
||||
public static final Function SIGN = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return MathX.sign(d);
|
||||
}
|
||||
};
|
||||
public static final Function SIN = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.sin(d);
|
||||
}
|
||||
};
|
||||
public static final Function SINH = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return MathX.sinh(d);
|
||||
}
|
||||
};
|
||||
public static final Function SQRT = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.sqrt(d);
|
||||
}
|
||||
};
|
||||
|
||||
public static final Function TAN = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return Math.tan(d);
|
||||
}
|
||||
};
|
||||
public static final Function TANH = new OneArg() {
|
||||
protected double evaluate(double d) {
|
||||
return MathX.tanh(d);
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
public static final Function ATAN2 = new TwoArg() {
|
||||
protected double evaluate(double d0, double d1) throws EvaluationException {
|
||||
if (d0 == ZERO && d1 == ZERO) {
|
||||
throw new EvaluationException(ErrorEval.DIV_ZERO);
|
||||
}
|
||||
return Math.atan2(d1, d0);
|
||||
}
|
||||
};
|
||||
public static final Function CEILING = new TwoArg() {
|
||||
protected double evaluate(double d0, double d1) {
|
||||
return MathX.ceiling(d0, d1);
|
||||
}
|
||||
};
|
||||
public static final Function COMBIN = new TwoArg() {
|
||||
protected double evaluate(double d0, double d1) throws EvaluationException {
|
||||
if (d0 > Integer.MAX_VALUE || d1 > Integer.MAX_VALUE) {
|
||||
throw new EvaluationException(ErrorEval.NUM_ERROR);
|
||||
}
|
||||
return MathX.nChooseK((int) d0, (int) d1);
|
||||
}
|
||||
};
|
||||
public static final Function FLOOR = new TwoArg() {
|
||||
protected double evaluate(double d0, double d1) throws EvaluationException {
|
||||
if (d1 == ZERO) {
|
||||
if (d0 == ZERO) {
|
||||
return ZERO;
|
||||
}
|
||||
throw new EvaluationException(ErrorEval.DIV_ZERO);
|
||||
}
|
||||
return MathX.floor(d0, d1);
|
||||
}
|
||||
};
|
||||
public static final Function MOD = new TwoArg() {
|
||||
protected double evaluate(double d0, double d1) throws EvaluationException {
|
||||
if (d1 == ZERO) {
|
||||
throw new EvaluationException(ErrorEval.DIV_ZERO);
|
||||
}
|
||||
return MathX.mod(d0, d1);
|
||||
}
|
||||
};
|
||||
public static final Function POWER = new TwoArg() {
|
||||
protected double evaluate(double d0, double d1) {
|
||||
return Math.pow(d0, d1);
|
||||
}
|
||||
};
|
||||
public static final Function ROUND = new TwoArg() {
|
||||
protected double evaluate(double d0, double d1) {
|
||||
return MathX.round(d0, (int)d1);
|
||||
}
|
||||
};
|
||||
public static final Function ROUNDDOWN = new TwoArg() {
|
||||
protected double evaluate(double d0, double d1) {
|
||||
return MathX.roundDown(d0, (int)d1);
|
||||
}
|
||||
};
|
||||
public static final Function ROUNDUP = new TwoArg() {
|
||||
protected double evaluate(double d0, double d1) {
|
||||
return MathX.roundUp(d0, (int)d1);
|
||||
}
|
||||
};
|
||||
static final NumberEval TRUNC_ARG2_DEFAULT = new NumberEval(0);
|
||||
public static final Function TRUNC = new Var1or2ArgFunction() {
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
return evaluate(srcRowIndex, srcColumnIndex, arg0, TRUNC_ARG2_DEFAULT);
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
||||
double result;
|
||||
try {
|
||||
double d0 = singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
double d1 = singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
double multi = Math.pow(10d,d1);
|
||||
result = Math.floor(d0 * multi) / multi;
|
||||
checkValue(result);
|
||||
}catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
private static final class Log extends Var1or2ArgFunction {
|
||||
public Log() {
|
||||
// no instance fields
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
|
||||
double result;
|
||||
try {
|
||||
double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
result = Math.log(d0) / LOG_10_TO_BASE_e;
|
||||
NumericFunction.checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0,
|
||||
ValueEval arg1) {
|
||||
double result;
|
||||
try {
|
||||
double d0 = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
double logE = Math.log(d0);
|
||||
double base = d1;
|
||||
if (base == Math.E) {
|
||||
result = logE;
|
||||
} else {
|
||||
result = logE / Math.log(base);
|
||||
}
|
||||
NumericFunction.checkValue(result);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(result);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Function LOG = new Log();
|
||||
|
||||
static final NumberEval PI_EVAL = new NumberEval(Math.PI);
|
||||
public static final Function PI = new Fixed0ArgFunction() {
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
|
||||
return PI_EVAL;
|
||||
}
|
||||
};
|
||||
public static final Function RAND = new Fixed0ArgFunction() {
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex) {
|
||||
return new NumberEval(Math.random());
|
||||
}
|
||||
};
|
||||
public static final Function POISSON = new Fixed3ArgFunction() {
|
||||
|
||||
private final static double DEFAULT_RETURN_RESULT =1;
|
||||
|
||||
/**
|
||||
* This checks is x = 0 and the mean = 0.
|
||||
* Excel currently returns the value 1 where as the
|
||||
* maths common implementation will error.
|
||||
* @param x The number.
|
||||
* @param mean The mean.
|
||||
* @return If a default value should be returned.
|
||||
*/
|
||||
private boolean isDefaultResult(double x, double mean) {
|
||||
|
||||
if ( x == 0 && mean == 0 ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkArgument(double aDouble) throws EvaluationException {
|
||||
|
||||
NumericFunction.checkValue(aDouble);
|
||||
|
||||
// make sure that the number is positive
|
||||
if (aDouble < 0) {
|
||||
throw new EvaluationException(ErrorEval.NUM_ERROR);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private double probability(int k, double lambda) {
|
||||
return Math.pow(lambda, k) * Math.exp(-lambda) / factorial(k);
|
||||
}
|
||||
|
||||
private double cumulativeProbability(int x, double lambda) {
|
||||
double result = 0;
|
||||
for(int k = 0; k <= x; k++){
|
||||
result += probability(k, lambda);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** All long-representable factorials */
|
||||
private final long[] FACTORIALS = new long[] {
|
||||
1l, 1l, 2l,
|
||||
6l, 24l, 120l,
|
||||
720l, 5040l, 40320l,
|
||||
362880l, 3628800l, 39916800l,
|
||||
479001600l, 6227020800l, 87178291200l,
|
||||
1307674368000l, 20922789888000l, 355687428096000l,
|
||||
6402373705728000l, 121645100408832000l, 2432902008176640000l };
|
||||
|
||||
|
||||
public long factorial(final int n) {
|
||||
if (n < 0 || n > 20) {
|
||||
throw new IllegalArgumentException("Valid argument should be in the range [0..20]");
|
||||
}
|
||||
return FACTORIALS[n];
|
||||
}
|
||||
|
||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1, ValueEval arg2) {
|
||||
|
||||
// arguments/result for this function
|
||||
double mean=0;
|
||||
double x=0;
|
||||
boolean cumulative = ((BoolEval)arg2).getBooleanValue();
|
||||
double result=0;
|
||||
|
||||
try {
|
||||
x = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
||||
mean = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
||||
|
||||
// check for default result : excel implementation for 0,0
|
||||
// is different to Math Common.
|
||||
if (isDefaultResult(x,mean)) {
|
||||
return new NumberEval(DEFAULT_RETURN_RESULT);
|
||||
}
|
||||
// check the arguments : as per excel function def
|
||||
checkArgument(x);
|
||||
checkArgument(mean);
|
||||
|
||||
// truncate x : as per excel function def
|
||||
if ( cumulative ) {
|
||||
result = cumulativeProbability((int)x, mean);
|
||||
} else {
|
||||
result = probability((int)x, mean);
|
||||
}
|
||||
|
||||
// check the result
|
||||
NumericFunction.checkValue(result);
|
||||
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
|
||||
return new NumberEval(result);
|
||||
|
||||
}
|
||||
};
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user