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:
Yegor Kozlov 2010-11-21 11:54:40 +00:00
parent 533f3a4f4a
commit 619e612817
130 changed files with 27 additions and 12285 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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/>

View File

@ -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;
/**

View File

@ -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;
/**

View File

@ -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;
/**

View File

@ -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() {

View File

@ -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

View File

@ -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() + ")");
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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&nbsp;</th><th>Result&nbsp;</th><th>Comments</th></tr>
* <tr><td>"A1"&nbsp;&nbsp;</td><td>true</td><td>&nbsp;</td></tr>
* <tr><td>"a111"&nbsp;&nbsp;</td><td>true</td><td>&nbsp;</td></tr>
* <tr><td>"AA"&nbsp;&nbsp;</td><td>false</td><td>&nbsp;</td></tr>
* <tr><td>"aa1"&nbsp;&nbsp;</td><td>true</td><td>&nbsp;</td></tr>
* <tr><td>"A1A"&nbsp;&nbsp;</td><td>false</td><td>&nbsp;</td></tr>
* <tr><td>"A1A1"&nbsp;&nbsp;</td><td>false</td><td>&nbsp;</td></tr>
* <tr><td>"A$1:$C$20"&nbsp;&nbsp;</td><td>false</td><td>Not a plain cell reference</td></tr>
* <tr><td>"SALES20080101"&nbsp;&nbsp;</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);
}
}

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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))));
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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);
}

View File

@ -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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt; 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
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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();
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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() + ")");
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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();
}
}

View File

@ -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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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;
}
}

View File

@ -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() + ")");
}
}

View File

@ -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
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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();
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
public interface NumericValueEval extends ValueEval {
public abstract double getNumberValue();
}

View File

@ -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>&nbsp;</th><th>&nbsp;A&nbsp;</th><th>&nbsp;B&nbsp;</th><th>&nbsp;C&nbsp;</th><th>&nbsp;D&nbsp;</th></tr>
* <tr><th>1</th><td>15</td><td>20</td><td>25</td><td>&nbsp;</td></tr>
* <tr><th>2</th><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>200</td></tr>
* <tr><th>3</th><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>300</td></tr>
* <tr><th>3</th><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</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>&nbsp;</th><th>&nbsp;A&nbsp;</th><th>&nbsp;B&nbsp;</th><th>&nbsp;C&nbsp;</th><th>&nbsp;D&nbsp;</th></tr>
* <tr><th>1</th><td>15</td><td>20</td><td>25</td><td>&nbsp;</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/>
* &nbsp;&nbsp;<code>return (int)Math.floor(d);</code><br/>
* <b>not</b>:<br/>
* &nbsp;&nbsp;<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 " -&gt; 123.0<br/>
* ".123" -&gt; 0.123<br/>
* "1E4" -&gt; 1000<br/>
* "-123" -&gt; -123.0<br/>
* These not supported yet:<br/>
* " $ 1,000.00 " -&gt; 1000.0<br/>
* "$1.25E4" -&gt; 12500.0<br/>
* "5**2" -&gt; 500<br/>
* "250%" -&gt; 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() + ")");
}
}

View File

@ -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);
}
}

View File

@ -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() + ")");
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
* 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);
}

View File

@ -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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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;
}
};
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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();
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
public interface StringValueEval extends ValueEval {
/**
* @return never <code>null</code>, possibly empty string.
*/
String getStringValue();
}

View File

@ -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();
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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);
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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);
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
public interface ValueEval {
// no methods
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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");
}
}
}

View File

@ -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);
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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);
}
};
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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);
}
};
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
};
}

View File

@ -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;
}
}

View File

@ -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;
}
};
}

View File

@ -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&nbsp;&nbsp;&nbsp;</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;
}
};
}

View File

@ -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&nbsp;&nbsp;&nbsp;</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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 + ")");
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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);
}
};
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*
* 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;
}
}

View File

@ -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);
}
}

View File

@ -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]);
}
}

View File

@ -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]);
}
}

View File

@ -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]);
}
}

View File

@ -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]);
}
}

View File

@ -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);
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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);
}

View File

@ -1,32 +0,0 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.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);
}

View File

@ -1,32 +0,0 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.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);
}

View File

@ -1,32 +0,0 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.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);
}

View File

@ -1,32 +0,0 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.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);
}

View File

@ -1,32 +0,0 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.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);
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*/
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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
* @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;
}
};
}

View File

@ -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");
}
}

View File

@ -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&nbsp;&nbsp;&nbsp;Return</th><th>Value&nbsp;&nbsp;</th><th>Thrown Error</th></tr>
* <tr><td>5</td><td>4</td><td>&nbsp;</td></tr>
* <tr><td>2.9</td><td>2</td><td>&nbsp;</td></tr>
* <tr><td>"5"</td><td>4</td><td>&nbsp;</td></tr>
* <tr><td>"2.18e1"</td><td>21</td><td>&nbsp;</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>&nbsp;</td></tr>
* <tr><td>"TRUE"</td><td>&nbsp;</td><td>#REF!</td></tr>
* <tr><td>"abc"</td><td>&nbsp;</td><td>#REF!</td></tr>
* <tr><td>""</td><td>&nbsp;</td><td>#REF!</td></tr>
* <tr><td>&lt;blank&gt;</td><td>&nbsp;</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() + ")");
}
}

View File

@ -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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
* 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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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;
}
};
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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() + ")");
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
* 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() + ")");
}
}

View File

@ -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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
* 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;
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
* @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