diff options
author | Nick Burch <nick@apache.org> | 2008-09-13 13:48:27 +0000 |
---|---|---|
committer | Nick Burch <nick@apache.org> | 2008-09-13 13:48:27 +0000 |
commit | 1255f2e0bcf5b8fb102fd5fcb3aeefcb2f03d572 (patch) | |
tree | e5ffaf04ec3f7a7b7e884a4486bc11b927e975a1 | |
parent | 6d1b247b69deebf1c5eee0d98ab6ffa99a460830 (diff) | |
download | poi-1255f2e0bcf5b8fb102fd5fcb3aeefcb2f03d572.tar.gz poi-1255f2e0bcf5b8fb102fd5fcb3aeefcb2f03d572.zip |
Merged revisions 693591,693639,693658,693939,693941,693947,693990,694050,694065,694153,694534,694615,694619-694620,694631,694643,694877,694881 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk
........
r693591 | josh | 2008-09-09 21:25:16 +0100 (Tue, 09 Sep 2008) | 1 line
Added support for parsing array constants in formulas. (Helping investigation for bug 45752)
........
r693639 | josh | 2008-09-09 23:26:28 +0100 (Tue, 09 Sep 2008) | 1 line
removed debug code accidentally submitted with r693591
........
r693658 | josh | 2008-09-10 00:46:46 +0100 (Wed, 10 Sep 2008) | 2 lines
Fixed special cases of INDEX function (single columns / single rows, and errors)
........
r693939 | josh | 2008-09-10 20:23:43 +0100 (Wed, 10 Sep 2008) | 1 line
Fixing error value handling for numeric functions. Refactored hierarchy.
........
r693941 | josh | 2008-09-10 20:27:24 +0100 (Wed, 10 Sep 2008) | 1 line
(Should have been submitted with 693939) Fixing error value handling for numeric functions. Refactored hierarchy.
........
r693947 | josh | 2008-09-10 20:33:58 +0100 (Wed, 10 Sep 2008) | 1 line
(Should have been submitted with 693939) Fixing error value handling for numeric functions. Refactored hierarchy.
........
r693990 | josh | 2008-09-10 22:21:28 +0100 (Wed, 10 Sep 2008) | 1 line
Refactored hierarchy of MultiOperandNumericFunction. Fixed error value handling. Enabled error value check in TestFormulasFromSpreadsheet
........
r694050 | josh | 2008-09-10 23:43:30 +0100 (Wed, 10 Sep 2008) | 1 line
Refactored finance functions.
........
r694065 | josh | 2008-09-11 00:37:22 +0100 (Thu, 11 Sep 2008) | 1 line
fixed special cases of MODE function
........
r694153 | josh | 2008-09-11 08:16:20 +0100 (Thu, 11 Sep 2008) | 1 line
Refactoring MultiOperandNumericFunction - removed Ref2DEval.
........
r694534 | josh | 2008-09-12 00:18:50 +0100 (Fri, 12 Sep 2008) | 1 line
Fix for bug 45639 - cleaned up index logic inside ColumnInfoRecordsAggregate
........
r694615 | josh | 2008-09-12 07:14:07 +0100 (Fri, 12 Sep 2008) | 1 line
small tweak to unit test which was silently creating UnknownPtgs
........
r694619 | josh | 2008-09-12 07:58:52 +0100 (Fri, 12 Sep 2008) | 1 line
Removed trailing comma from output of HexDump.toHex()
........
r694620 | josh | 2008-09-12 08:03:00 +0100 (Fri, 12 Sep 2008) | 1 line
clarification of ArrayPtg size increment
........
r694631 | josh | 2008-09-12 08:43:20 +0100 (Fri, 12 Sep 2008) | 1 line
Extended support for cached results of formula cells
........
r694643 | josh | 2008-09-12 09:18:54 +0100 (Fri, 12 Sep 2008) | 2 lines
Made HSSFFormulaEvaluator no longer require initialisation with sheet or row.
........
r694877 | josh | 2008-09-13 06:14:26 +0100 (Sat, 13 Sep 2008) | 1 line
Refactored TextFunctions. Some minor fixes - test cases added.
........
r694881 | josh | 2008-09-13 06:43:41 +0100 (Sat, 13 Sep 2008) | 1 line
Added toString methods formatAsString to CellValue. Changed deprecation on CellValue.getRichTextStringValue
........
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@694947 13f79535-47bb-0310-9956-ffa450edef68
152 files changed, 4427 insertions, 6500 deletions
diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index c1152ef344..6025906757 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -65,6 +65,10 @@ <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action> </release> <release version="3.1.1-alpha1" date="2008-??-??"> + <action dev="POI-DEVELOPERS" type="add">Made HSSFFormulaEvaluator no longer require initialisation with sheet or row</action> + <action dev="POI-DEVELOPERS" type="add">Extended support for cached results of formula cells</action> + <action dev="POI-DEVELOPERS" type="fix">45639 - Fixed AIOOBE due to bad index logic in ColumnInfoRecordsAggregate</action> + <action dev="POI-DEVELOPERS" type="fix">Fixed special cases of INDEX function (single column/single row, errors)</action> <action dev="POI-DEVELOPERS" type="add">45761 - Support for Very Hidden excel sheets in HSSF</action> <action dev="POI-DEVELOPERS" type="add">45738 - Initial HWPF support for Office Art Shapes</action> <action dev="POI-DEVELOPERS" type="fix">45720 - Fixed HSSFWorkbook.cloneSheet to correctly clone sheets with drawings</action> diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 155d522d7b..9eaab3dad1 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -62,6 +62,10 @@ <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action> </release> <release version="3.1.1-alpha1" date="2008-??-??"> + <action dev="POI-DEVELOPERS" type="add">Made HSSFFormulaEvaluator no longer require initialisation with sheet or row</action> + <action dev="POI-DEVELOPERS" type="add">Extended support for cached results of formula cells</action> + <action dev="POI-DEVELOPERS" type="fix">45639 - Fixed AIOOBE due to bad index logic in ColumnInfoRecordsAggregate</action> + <action dev="POI-DEVELOPERS" type="fix">Fixed special cases of INDEX function (single column/single row, errors)</action> <action dev="POI-DEVELOPERS" type="add">45761 - Support for Very Hidden excel sheets in HSSF</action> <action dev="POI-DEVELOPERS" type="add">45738 - Initial HWPF support for Office Art Shapes</action> <action dev="POI-DEVELOPERS" type="fix">45720 - Fixed HSSFWorkbook.cloneSheet to correctly clone sheets with drawings</action> diff --git a/src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java b/src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java index 8f3eebb2d3..2ea35c773e 100644 --- a/src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java +++ b/src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ + package org.apache.poi.hssf.extractor; import java.io.IOException; @@ -49,10 +50,10 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem; /** * A text extractor for Excel files, that is based * on the hssf eventusermodel api. - * It will typically use less memory than + * It will typically use less memory than * {@link ExcelExtractor}, but may not provide * the same richness of formatting. - * Returns the textual content of the file, suitable for + * Returns the textual content of the file, suitable for * indexing by something like Lucene, but not really * intended for display to the user. * To turn an excel file into a CSV or similar, then see @@ -63,8 +64,8 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor { private POIFSFileSystem fs; private boolean includeSheetNames = true; private boolean formulasNotResults = false; - - public EventBasedExcelExtractor(POIFSFileSystem fs) throws IOException { + + public EventBasedExcelExtractor(POIFSFileSystem fs) { super(null); this.fs = fs; } @@ -98,8 +99,8 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor { public void setFormulasNotResults(boolean formulasNotResults) { this.formulasNotResults = formulasNotResults; } - - + + /** * Retreives the text contents of the file */ @@ -107,7 +108,7 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor { String text = null; try { TextListener tl = triggerExtraction(); - + text = tl.text.toString(); if(! text.endsWith("\n")) { text = text + "\n"; @@ -115,37 +116,37 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor { } catch(IOException e) { throw new RuntimeException(e); } - + return text; } - + private TextListener triggerExtraction() throws IOException { TextListener tl = new TextListener(); FormatTrackingHSSFListener ft = new FormatTrackingHSSFListener(tl); tl.ft = ft; - + // Register and process HSSFEventFactory factory = new HSSFEventFactory(); HSSFRequest request = new HSSFRequest(); request.addListenerForAllRecords(ft); - + factory.processWorkbookEvents(request, fs); - + return tl; } - + private class TextListener implements HSSFListener { private FormatTrackingHSSFListener ft; private SSTRecord sstRecord; - + private List sheetNames = new ArrayList(); private StringBuffer text = new StringBuffer(); private int sheetNum = -1; private int rowNum; - + private boolean outputNextStringValue = false; private int nextRow = -1; - + public void processRecord(Record record) { String thisText = null; int thisRow = -1; @@ -160,7 +161,7 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor { if(bof.getType() == BOFRecord.TYPE_WORKSHEET) { sheetNum++; rowNum = -1; - + if(includeSheetNames) { if(text.length() > 0) text.append("\n"); text.append(sheetNames.get(sheetNum)); @@ -170,60 +171,60 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor { case SSTRecord.sid: sstRecord = (SSTRecord)record; break; - - case FormulaRecord.sid: - FormulaRecord frec = (FormulaRecord) record; - thisRow = frec.getRow(); - - if(formulasNotResults) { - thisText = FormulaParser.toFormulaString(null, frec.getParsedExpression()); - } else { - if(Double.isNaN( frec.getValue() )) { - // Formula result is a string - // This is stored in the next record - outputNextStringValue = true; - nextRow = frec.getRow(); - } else { - thisText = formatNumberDateCell(frec, frec.getValue()); - } - } - break; - case StringRecord.sid: - if(outputNextStringValue) { - // String for formula - StringRecord srec = (StringRecord)record; - thisText = srec.getString(); - thisRow = nextRow; - outputNextStringValue = false; - } - break; - case LabelRecord.sid: - LabelRecord lrec = (LabelRecord) record; - thisRow = lrec.getRow(); - thisText = lrec.getValue(); - break; - case LabelSSTRecord.sid: - LabelSSTRecord lsrec = (LabelSSTRecord) record; - thisRow = lsrec.getRow(); - if(sstRecord == null) { - throw new IllegalStateException("No SST record found"); - } - thisText = sstRecord.getString(lsrec.getSSTIndex()).toString(); - break; - case NoteRecord.sid: - NoteRecord nrec = (NoteRecord) record; - thisRow = nrec.getRow(); - // TODO: Find object to match nrec.getShapeId() - break; - case NumberRecord.sid: - NumberRecord numrec = (NumberRecord) record; - thisRow = numrec.getRow(); - thisText = formatNumberDateCell(numrec, numrec.getValue()); - break; - default: - break; + + case FormulaRecord.sid: + FormulaRecord frec = (FormulaRecord) record; + thisRow = frec.getRow(); + + if(formulasNotResults) { + thisText = FormulaParser.toFormulaString(null, frec.getParsedExpression()); + } else { + if(frec.hasCachedResultString()) { + // Formula result is a string + // This is stored in the next record + outputNextStringValue = true; + nextRow = frec.getRow(); + } else { + thisText = formatNumberDateCell(frec, frec.getValue()); + } + } + break; + case StringRecord.sid: + if(outputNextStringValue) { + // String for formula + StringRecord srec = (StringRecord)record; + thisText = srec.getString(); + thisRow = nextRow; + outputNextStringValue = false; + } + break; + case LabelRecord.sid: + LabelRecord lrec = (LabelRecord) record; + thisRow = lrec.getRow(); + thisText = lrec.getValue(); + break; + case LabelSSTRecord.sid: + LabelSSTRecord lsrec = (LabelSSTRecord) record; + thisRow = lsrec.getRow(); + if(sstRecord == null) { + throw new IllegalStateException("No SST record found"); + } + thisText = sstRecord.getString(lsrec.getSSTIndex()).toString(); + break; + case NoteRecord.sid: + NoteRecord nrec = (NoteRecord) record; + thisRow = nrec.getRow(); + // TODO: Find object to match nrec.getShapeId() + break; + case NumberRecord.sid: + NumberRecord numrec = (NumberRecord) record; + thisRow = numrec.getRow(); + thisText = formatNumberDateCell(numrec, numrec.getValue()); + break; + default: + break; } - + if(thisText != null) { if(thisRow != rowNum) { rowNum = thisRow; @@ -235,42 +236,42 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor { text.append(thisText); } } - + /** - * Formats a number or date cell, be that a real number, or the + * Formats a number or date cell, be that a real number, or the * answer to a formula */ private String formatNumberDateCell(CellValueRecordInterface cell, double value) { - // Get the built in format, if there is one + // Get the built in format, if there is one int formatIndex = ft.getFormatIndex(cell); String formatString = ft.getFormatString(cell); - + if(formatString == null) { - return Double.toString(value); - } else { - // Is it a date? - if(HSSFDateUtil.isADateFormat(formatIndex,formatString) && - HSSFDateUtil.isValidExcelDate(value)) { - // Java wants M not m for month - formatString = formatString.replace('m','M'); - // Change \- into -, if it's there - formatString = formatString.replaceAll("\\\\-","-"); - - // Format as a date - Date d = HSSFDateUtil.getJavaDate(value, false); - DateFormat df = new SimpleDateFormat(formatString); - return df.format(d); - } else { - if(formatString == "General") { - // Some sort of wierd default - return Double.toString(value); - } - - // Format as a number - DecimalFormat df = new DecimalFormat(formatString); - return df.format(value); - } - } + return Double.toString(value); + } else { + // Is it a date? + if(HSSFDateUtil.isADateFormat(formatIndex,formatString) && + HSSFDateUtil.isValidExcelDate(value)) { + // Java wants M not m for month + formatString = formatString.replace('m','M'); + // Change \- into -, if it's there + formatString = formatString.replaceAll("\\\\-","-"); + + // Format as a date + Date d = HSSFDateUtil.getJavaDate(value, false); + DateFormat df = new SimpleDateFormat(formatString); + return df.format(d); + } else { + if(formatString == "General") { + // Some sort of wierd default + return Double.toString(value); + } + + // Format as a number + DecimalFormat df = new DecimalFormat(formatString); + return df.format(value); + } + } } } } diff --git a/src/java/org/apache/poi/hssf/extractor/ExcelExtractor.java b/src/java/org/apache/poi/hssf/extractor/ExcelExtractor.java index b9750fc58a..17bde6da5f 100644 --- a/src/java/org/apache/poi/hssf/extractor/ExcelExtractor.java +++ b/src/java/org/apache/poi/hssf/extractor/ExcelExtractor.java @@ -14,12 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ + package org.apache.poi.hssf.extractor; import java.io.IOException; import org.apache.poi.POIOLE2TextExtractor; import org.apache.poi.ss.usermodel.HeaderFooter; +import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFComment; import org.apache.poi.hssf.usermodel.HSSFRichTextString; @@ -112,40 +114,52 @@ public class ExcelExtractor extends POIOLE2TextExtractor implements org.apache.p int lastCell = row.getLastCellNum(); for(int k=firstCell;k<lastCell;k++) { HSSFCell cell = row.getCell(k); - boolean outputContents = false; if(cell == null) { continue; } + boolean outputContents = true; switch(cell.getCellType()) { + case HSSFCell.CELL_TYPE_BLANK: + outputContents = false; + break; case HSSFCell.CELL_TYPE_STRING: text.append(cell.getRichStringCellValue().getString()); - outputContents = true; break; case HSSFCell.CELL_TYPE_NUMERIC: // Note - we don't apply any formatting! text.append(cell.getNumericCellValue()); - outputContents = true; break; case HSSFCell.CELL_TYPE_BOOLEAN: text.append(cell.getBooleanCellValue()); - outputContents = true; + break; + case HSSFCell.CELL_TYPE_ERROR: + text.append(ErrorEval.getText(cell.getErrorCellValue())); break; case HSSFCell.CELL_TYPE_FORMULA: if(formulasNotResults) { text.append(cell.getCellFormula()); } else { - // Try it as a string, if not as a number - HSSFRichTextString str = - cell.getRichStringCellValue(); - if(str != null && str.length() > 0) { - text.append(str.toString()); - } else { - // Try and treat it as a number - double val = cell.getNumericCellValue(); - text.append(val); + switch(cell.getCachedFormulaResultType()) { + case HSSFCell.CELL_TYPE_STRING: + HSSFRichTextString str = cell.getRichStringCellValue(); + if(str != null && str.length() > 0) { + text.append(str.toString()); + } + break; + case HSSFCell.CELL_TYPE_NUMERIC: + text.append(cell.getNumericCellValue()); + break; + case HSSFCell.CELL_TYPE_BOOLEAN: + text.append(cell.getBooleanCellValue()); + break; + case HSSFCell.CELL_TYPE_ERROR: + text.append(ErrorEval.getText(cell.getErrorCellValue())); + break; + } } - outputContents = true; break; + default: + throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")"); } // Output the comment, if requested and exists diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java index 5fd30d06e0..32f38d40d1 100644 --- a/src/java/org/apache/poi/hssf/model/FormulaParser.java +++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java @@ -22,11 +22,14 @@ import java.util.List; import java.util.Stack; //import PTGs .. since we need everything, import * +import org.apache.poi.hssf.record.UnicodeString; +import org.apache.poi.hssf.record.constant.ErrorConstant; import 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.usermodel.Name; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.hssf.usermodel.HSSFErrorConstants; import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.util.AreaReference; @@ -70,9 +73,9 @@ public final class FormulaParser { public static final int FORMULA_TYPE_ARRAY =2; public static final int FORMULA_TYPE_CONDFORMAT = 3; public static final int FORMULA_TYPE_NAMEDRANGE = 4; - // this constant is currently very specific. The exact differences from general data + // this constant is currently very specific. The exact differences from general data // validation formulas or conditional format formulas is not known yet - public static final int FORMULA_TYPE_DATAVALIDATION_LIST = 5; + public static final int FORMULA_TYPE_DATAVALIDATION_LIST = 5; private final String formulaString; private final int formulaLength; @@ -140,9 +143,9 @@ public final class FormulaParser { /** Report What Was Expected */ private RuntimeException expected(String s) { String msg; - + if (look == '=' && formulaString.substring(0, pointer-1).trim().length() < 1) { - msg = "The specified formula '" + formulaString + msg = "The specified formula '" + formulaString + "' starts with an equals sign which is not allowed."; } else { msg = "Parse error near char " + (pointer-1) + " '" + look + "'" @@ -194,8 +197,8 @@ public final class FormulaParser { /** * Parses a sheet name, named range name, or simple cell reference.<br/> * Note - identifiers in Excel can contain dots, so this method may return a String - * which may need to be converted to an area reference. For example, this method - * may return a value like "A1..B2", in which case the caller must convert it to + * which may need to be converted to an area reference. For example, this method + * may return a value like "A1..B2", in which case the caller must convert it to * an area reference like "A1:B2" */ private String parseIdentifier() { @@ -251,7 +254,7 @@ public final class FormulaParser { } private Ptg parseNameOrReference(String name) { - + AreaReference areaRef = parseArea(name); if (areaRef != null) { // will happen if dots are used instead of colon @@ -373,30 +376,28 @@ public final class FormulaParser { private ParseNode function(String name) { Ptg nameToken = null; if(!AbstractFunctionPtg.isBuiltInFunctionName(name)) { - // user defined function + // user defined function // in the token tree, the name is more or less the first argument - - - int nameIndex = book.getNameIndex(name); - if (nameIndex >= 0) { - Name hName = book.getNameAt(nameIndex); - if (!hName.isFunctionName()) { - throw new FormulaParseException("Attempt to use name '" + name - + "' as a function, but defined name in workbook does not refer to a function"); - } - - // calls to user-defined functions within the workbook - // get a Name token which points to a defined name record - nameToken = new NamePtg(name, this.book); - } else { + int nameIndex = book.getNameIndex(name); + if (nameIndex >= 0) { + Name hName = book.getNameAt(nameIndex); + if (!hName.isFunctionName()) { + throw new FormulaParseException("Attempt to use name '" + name + + "' as a function, but defined name in workbook does not refer to a function"); + } + + // calls to user-defined functions within the workbook + // get a Name token which points to a defined name record + nameToken = new NamePtg(name, this.book); + } else { if(book instanceof HSSFWorkbook) { nameToken = ((HSSFWorkbook)book).getNameXPtg(name); } - if (nameToken == null) { - throw new FormulaParseException("Name '" + name - + "' is completely unknown in the current workbook"); - } - } + if (nameToken == null) { + throw new FormulaParseException("Name '" + name + + "' is completely unknown in the current workbook"); + } + } } Match('('); @@ -544,7 +545,7 @@ public final class FormulaParser { SkipWhite(); switch(look) { case '#': - return new ParseNode(parseErrorLiteral()); + return new ParseNode(ErrPtg.valueOf(parseErrorLiteral())); case '-': Match('-'); return new ParseNode(UnaryMinusPtg.instance, powerFactor()); @@ -557,7 +558,12 @@ public final class FormulaParser { Match(')'); return new ParseNode(ParenthesisPtg.instance, inside); case '"': - return new ParseNode(parseStringLiteral()); + return new ParseNode(new StringPtg(parseStringLiteral())); + case '{': + Match('{'); + ParseNode arrayNode = parseArray(); + Match('}'); + return arrayNode; } if (IsAlpha(look) || look == '\''){ return parseFunctionReferenceOrName(); @@ -567,6 +573,95 @@ public final class FormulaParser { } + private ParseNode parseArray() { + List rowsData = new ArrayList(); + while(true) { + Object[] singleRowData = parseArrayRow(); + rowsData.add(singleRowData); + if (look == '}') { + break; + } + if (look != ';') { + throw expected("'}' or ';'"); + } + Match(';'); + } + int nRows = rowsData.size(); + Object[][] values2d = new Object[nRows][]; + rowsData.toArray(values2d); + int nColumns = values2d[0].length; + checkRowLengths(values2d, nColumns); + + return new ParseNode(new ArrayPtg(values2d)); + } + private void checkRowLengths(Object[][] values2d, int nColumns) { + for (int i = 0; i < values2d.length; i++) { + int rowLen = values2d[i].length; + if (rowLen != nColumns) { + throw new FormulaParseException("Array row " + i + " has length " + rowLen + + " but row 0 has length " + nColumns); + } + } + } + + private Object[] parseArrayRow() { + List temp = new ArrayList(); + while (true) { + temp.add(parseArrayItem()); + SkipWhite(); + switch(look) { + case '}': + case ';': + break; + case ',': + Match(','); + continue; + default: + throw expected("'}' or ','"); + + } + break; + } + + Object[] result = new Object[temp.size()]; + temp.toArray(result); + return result; + } + + private Object parseArrayItem() { + SkipWhite(); + switch(look) { + case '"': return new UnicodeString(parseStringLiteral()); + case '#': return ErrorConstant.valueOf(parseErrorLiteral()); + case 'F': case 'f': + case 'T': case 't': + return parseBooleanLiteral(); + } + // else assume number + return convertArrayNumber(parseNumber()); + } + + private Boolean parseBooleanLiteral() { + String iden = parseIdentifier(); + if ("TRUE".equalsIgnoreCase(iden)) { + return Boolean.TRUE; + } + if ("FALSE".equalsIgnoreCase(iden)) { + return Boolean.FALSE; + } + throw expected("'TRUE' or 'FALSE'"); + } + + private static Double convertArrayNumber(Ptg ptg) { + if (ptg instanceof IntPtg) { + return new Double(((IntPtg)ptg).getValue()); + } + if (ptg instanceof NumberPtg) { + return new Double(((NumberPtg)ptg).getValue()); + } + throw new RuntimeException("Unexpected ptg (" + ptg.getClass().getName() + ")"); + } + private Ptg parseNumber() { String number2 = null; String exponent = null; @@ -603,7 +698,7 @@ public final class FormulaParser { } - private ErrPtg parseErrorLiteral() { + private int parseErrorLiteral() { Match('#'); String part1 = parseIdentifier().toUpperCase(); @@ -611,13 +706,13 @@ public final class FormulaParser { case 'V': if(part1.equals("VALUE")) { Match('!'); - return ErrPtg.VALUE_INVALID; + return HSSFErrorConstants.ERROR_VALUE; } throw expected("#VALUE!"); case 'R': if(part1.equals("REF")) { Match('!'); - return ErrPtg.REF_INVALID; + return HSSFErrorConstants.ERROR_REF; } throw expected("#REF!"); case 'D': @@ -625,21 +720,21 @@ public final class FormulaParser { Match('/'); Match('0'); Match('!'); - return ErrPtg.DIV_ZERO; + return HSSFErrorConstants.ERROR_DIV_0; } throw expected("#DIV/0!"); case 'N': if(part1.equals("NAME")) { Match('?'); // only one that ends in '?' - return ErrPtg.NAME_INVALID; + return HSSFErrorConstants.ERROR_NAME; } if(part1.equals("NUM")) { Match('!'); - return ErrPtg.NUM_ERROR; + return HSSFErrorConstants.ERROR_NUM; } if(part1.equals("NULL")) { Match('!'); - return ErrPtg.NULL_INTERSECTION; + return HSSFErrorConstants.ERROR_NULL; } if(part1.equals("N")) { Match('/'); @@ -648,7 +743,7 @@ public final class FormulaParser { } Match(look); // Note - no '!' or '?' suffix - return ErrPtg.N_A; + return HSSFErrorConstants.ERROR_NA; } throw expected("#NAME?, #NUM!, #NULL! or #N/A"); @@ -701,7 +796,7 @@ public final class FormulaParser { } - private StringPtg parseStringLiteral() { + private String parseStringLiteral() { Match('"'); StringBuffer token = new StringBuffer(); @@ -715,7 +810,7 @@ public final class FormulaParser { token.append(look); GetChar(); } - return new StringPtg(token.toString()); + return token.toString(); } /** Parse and Translate a Math Term */ @@ -972,7 +1067,7 @@ end; } return result; } - + private static String[] getOperands(Stack stack, int nOperands) { String[] operands = new String[nOperands]; diff --git a/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java b/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java index 9b5804f0c4..8b7b56638b 100644 --- a/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java +++ b/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java @@ -74,7 +74,7 @@ final class OperandClassTransformer { + _formulaType + ") not supported yet"); } - transformNode(rootNode, rootNodeOperandClass, false, false); + transformNode(rootNode, rootNodeOperandClass, false); } /** @@ -83,22 +83,35 @@ final class OperandClassTransformer { * the function return value). */ private void transformNode(ParseNode node, byte desiredOperandClass, - boolean callerForceArrayFlag, boolean isDirectChildOfValueOperator) { + boolean callerForceArrayFlag) { Ptg token = node.getToken(); ParseNode[] children = node.getChildren(); + boolean isSimpleValueFunc = isSimpleValueFunction(token); + + if (isSimpleValueFunc) { + boolean localForceArray = desiredOperandClass == Ptg.CLASS_ARRAY; + for (int i = 0; i < children.length; i++) { + transformNode(children[i], desiredOperandClass, localForceArray); + } + setSimpleValueFuncClass((AbstractFunctionPtg) token, desiredOperandClass, callerForceArrayFlag); + return; + } + if (token instanceof ValueOperatorPtg || token instanceof ControlPtg) { // Value Operator Ptgs and Control are base tokens, so token will be unchanged - // but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag + + // As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1" + // All direct operands of value operators that are initially 'R' type will + // be converted to 'V' type. + byte localDesiredOperandClass = desiredOperandClass == Ptg.CLASS_REF ? Ptg.CLASS_VALUE : desiredOperandClass; for (int i = 0; i < children.length; i++) { - ParseNode child = children[i]; - transformNode(child, desiredOperandClass, callerForceArrayFlag, true); + transformNode(children[i], localDesiredOperandClass, callerForceArrayFlag); } return; } if (token instanceof AbstractFunctionPtg) { - transformFunctionNode((AbstractFunctionPtg) token, children, desiredOperandClass, - callerForceArrayFlag); + transformFunctionNode((AbstractFunctionPtg) token, children, desiredOperandClass, callerForceArrayFlag); return; } if (children.length > 0) { @@ -109,15 +122,24 @@ final class OperandClassTransformer { // nothing to do return; } - if (isDirectChildOfValueOperator) { - // As per OOO documentation Sec 3.2.4 "Token Class Transformation", "Step 1" - // All direct operands of value operators that are initially 'R' type will - // be converted to 'V' type. - if (token.getPtgClass() == Ptg.CLASS_REF) { - token.setClass(Ptg.CLASS_VALUE); + token.setClass(transformClass(token.getPtgClass(), desiredOperandClass, callerForceArrayFlag)); + } + + private static boolean isSimpleValueFunction(Ptg token) { + if (token instanceof AbstractFunctionPtg) { + AbstractFunctionPtg aptg = (AbstractFunctionPtg) token; + if (aptg.getDefaultOperandClass() != Ptg.CLASS_VALUE) { + return false; + } + int numberOfOperands = aptg.getNumberOfOperands(); + for (int i=numberOfOperands-1; i>=0; i--) { + if (aptg.getParameterClass(i) != Ptg.CLASS_VALUE) { + return false; + } } + return true; } - token.setClass(transformClass(token.getPtgClass(), desiredOperandClass, callerForceArrayFlag)); + return false; } private byte transformClass(byte currentOperandClass, byte desiredOperandClass, @@ -185,6 +207,7 @@ final class OperandClassTransformer { switch (defaultReturnOperandClass) { case Ptg.CLASS_REF: afp.setClass(Ptg.CLASS_REF); +// afp.setClass(Ptg.CLASS_ARRAY); break; case Ptg.CLASS_VALUE: afp.setClass(Ptg.CLASS_ARRAY); @@ -220,7 +243,17 @@ final class OperandClassTransformer { for (int i = 0; i < children.length; i++) { ParseNode child = children[i]; byte paramOperandClass = afp.getParameterClass(i); - transformNode(child, paramOperandClass, localForceArrayFlag, false); + transformNode(child, paramOperandClass, localForceArrayFlag); + } + } + + private void setSimpleValueFuncClass(AbstractFunctionPtg afp, + byte desiredOperandClass, boolean callerForceArrayFlag) { + + if (callerForceArrayFlag || desiredOperandClass == Ptg.CLASS_ARRAY) { + afp.setClass(Ptg.CLASS_ARRAY); + } else { + afp.setClass(Ptg.CLASS_VALUE); } } } diff --git a/src/java/org/apache/poi/hssf/model/Sheet.java b/src/java/org/apache/poi/hssf/model/Sheet.java index 388f1a6ee4..8bbc30435a 100644 --- a/src/java/org/apache/poi/hssf/model/Sheet.java +++ b/src/java/org/apache/poi/hssf/model/Sheet.java @@ -1055,7 +1055,7 @@ public final class Sheet implements Model { ColumnInfoRecord ci = _columnInfos.findColumnInfo(columnIndex); if (ci != null) { - return ci.getColumnWidth(); + return (short)ci.getColumnWidth(); } //default column width is measured in characters //multiply @@ -1079,8 +1079,8 @@ public final class Sheet implements Model { public short getXFIndexForColAt(short columnIndex) { ColumnInfoRecord ci = _columnInfos.findColumnInfo(columnIndex); if (ci != null) { - return ci.getXFIndex(); - } + return (short)ci.getXFIndex(); + } return 0xF; } @@ -1138,8 +1138,7 @@ public final class Sheet implements Model { * @param indent if true the group will be indented by one level, * if false indenting will be removed by one level. */ - public void groupColumnRange(short fromColumn, short toColumn, boolean indent) - { + public void groupColumnRange(int fromColumn, int toColumn, boolean indent) { // Set the level for each column _columnInfos.groupColumnRange( fromColumn, toColumn, indent); @@ -1709,17 +1708,13 @@ public final class Sheet implements Model { } - public void setColumnGroupCollapsed( short columnNumber, boolean collapsed ) - { - if (collapsed) - { - _columnInfos.collapseColumn( columnNumber ); - } - else - { - _columnInfos.expandColumn( columnNumber ); - } - } + public void setColumnGroupCollapsed(int columnNumber, boolean collapsed) { + if (collapsed) { + _columnInfos.collapseColumn(columnNumber); + } else { + _columnInfos.expandColumn(columnNumber); + } + } /** * protect a spreadsheet with a password (not encypted, just sets protect diff --git a/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java b/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java index b77dca3e17..32aef3a6c3 100644 --- a/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java +++ b/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java @@ -17,6 +17,7 @@ package org.apache.poi.hssf.record; +import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.BitField; import org.apache.poi.util.BitFieldFactory; @@ -30,19 +31,24 @@ import org.apache.poi.util.BitFieldFactory; */ public final class ColumnInfoRecord extends Record { public static final short sid = 0x7d; - private short field_1_first_col; - private short field_2_last_col; - private short field_3_col_width; - private short field_4_xf_index; - private short field_5_options; + private int field_1_first_col; + private int field_2_last_col; + private int field_3_col_width; + private int field_4_xf_index; + private int field_5_options; private static final BitField hidden = BitFieldFactory.getInstance(0x01); private static final BitField outlevel = BitFieldFactory.getInstance(0x0700); private static final BitField collapsed = BitFieldFactory.getInstance(0x1000); // Excel seems write values 2, 10, and 260, even though spec says "must be zero" private short field_6_reserved; - public ColumnInfoRecord() - { + /** + * Creates a column info record with default width and format + */ + public ColumnInfoRecord() { + setColumnWidth(2275); + field_5_options = 2; + field_4_xf_index = 0x0f; field_6_reserved = 2; // seems to be the most common value } @@ -90,7 +96,7 @@ public final class ColumnInfoRecord extends Record { * @param fc - the first column index (0-based) */ - public void setFirstColumn(short fc) + public void setFirstColumn(int fc) { field_1_first_col = fc; } @@ -100,7 +106,7 @@ public final class ColumnInfoRecord extends Record { * @param lc - the last column index (0-based) */ - public void setLastColumn(short lc) + public void setLastColumn(int lc) { field_2_last_col = lc; } @@ -110,7 +116,7 @@ public final class ColumnInfoRecord extends Record { * @param cw - column width */ - public void setColumnWidth(short cw) + public void setColumnWidth(int cw) { field_3_col_width = cw; } @@ -121,20 +127,11 @@ public final class ColumnInfoRecord extends Record { * @see org.apache.poi.hssf.record.ExtendedFormatRecord */ - public void setXFIndex(short xfi) + public void setXFIndex(int xfi) { field_4_xf_index = xfi; } - /** - * set the options bitfield - use the bitsetters instead - * @param options - the bitfield raw value - */ - - public void setOptions(short options) - { - field_5_options = options; - } // start options bitfield @@ -146,7 +143,7 @@ public final class ColumnInfoRecord extends Record { public void setHidden(boolean ishidden) { - field_5_options = hidden.setShortBoolean(field_5_options, ishidden); + field_5_options = hidden.setBoolean(field_5_options, ishidden); } /** @@ -155,9 +152,9 @@ public final class ColumnInfoRecord extends Record { * @param olevel -outline level for the cells */ - public void setOutlineLevel(short olevel) + public void setOutlineLevel(int olevel) { - field_5_options = outlevel.setShortValue(field_5_options, olevel); + field_5_options = outlevel.setValue(field_5_options, olevel); } /** @@ -168,7 +165,7 @@ public final class ColumnInfoRecord extends Record { public void setCollapsed(boolean iscollapsed) { - field_5_options = collapsed.setShortBoolean(field_5_options, + field_5_options = collapsed.setBoolean(field_5_options, iscollapsed); } @@ -179,7 +176,7 @@ public final class ColumnInfoRecord extends Record { * @return the first column index (0-based) */ - public short getFirstColumn() + public int getFirstColumn() { return field_1_first_col; } @@ -189,7 +186,7 @@ public final class ColumnInfoRecord extends Record { * @return the last column index (0-based) */ - public short getLastColumn() + public int getLastColumn() { return field_2_last_col; } @@ -199,7 +196,7 @@ public final class ColumnInfoRecord extends Record { * @return column width */ - public short getColumnWidth() + public int getColumnWidth() { return field_3_col_width; } @@ -210,21 +207,18 @@ public final class ColumnInfoRecord extends Record { * @see org.apache.poi.hssf.record.ExtendedFormatRecord */ - public short getXFIndex() + public int getXFIndex() { return field_4_xf_index; } - /** - * get the options bitfield - use the bitsetters instead - * @return the bitfield raw value - */ - - public short getOptions() - { + public int getOptions() { return field_5_options; } - + public void setOptions(int field_5_options) { + this.field_5_options = field_5_options; + } + // start options bitfield /** @@ -244,9 +238,9 @@ public final class ColumnInfoRecord extends Record { * @return outline level for the cells */ - public short getOutlineLevel() + public int getOutlineLevel() { - return outlevel.getShortValue(field_5_options); + return outlevel.getValue(field_5_options); } /** @@ -261,6 +255,31 @@ public final class ColumnInfoRecord extends Record { } // end options bitfield + + public boolean containsColumn(int columnIndex) { + return field_1_first_col <= columnIndex && columnIndex <= field_2_last_col; + } + public boolean isAdjacentBefore(ColumnInfoRecord other) { + return field_2_last_col == other.field_1_first_col - 1; + } + + /** + * @return <code>true</code> if the format, options and column width match + */ + public boolean formatMatches(ColumnInfoRecord other) { + if (field_4_xf_index != other.field_4_xf_index) { + return false; + } + if (field_5_options != other.field_5_options) { + return false; + } + if (field_3_col_width != other.field_3_col_width) { + return false; + } + return true; + } + + public short getSid() { return sid; @@ -269,13 +288,13 @@ public final class ColumnInfoRecord extends Record { public int serialize(int offset, byte [] data) { LittleEndian.putShort(data, 0 + offset, sid); - LittleEndian.putShort(data, 2 + offset, ( short ) 12); - LittleEndian.putShort(data, 4 + offset, getFirstColumn()); - LittleEndian.putShort(data, 6 + offset, getLastColumn()); - LittleEndian.putShort(data, 8 + offset, getColumnWidth()); - LittleEndian.putShort(data, 10 + offset, getXFIndex()); - LittleEndian.putShort(data, 12 + offset, getOptions()); - LittleEndian.putShort(data, 14 + offset, field_6_reserved); + LittleEndian.putUShort(data, 2 + offset, 12); + LittleEndian.putUShort(data, 4 + offset, getFirstColumn()); + LittleEndian.putUShort(data, 6 + offset, getLastColumn()); + LittleEndian.putUShort(data, 8 + offset, getColumnWidth()); + LittleEndian.putUShort(data, 10 + offset, getXFIndex()); + LittleEndian.putUShort(data, 12 + offset, field_5_options); + LittleEndian.putUShort(data, 14 + offset, field_6_reserved); return getRecordSize(); } @@ -286,24 +305,19 @@ public final class ColumnInfoRecord extends Record { public String toString() { - StringBuffer buffer = new StringBuffer(); - - buffer.append("[COLINFO]\n"); - buffer.append("colfirst = ").append(getFirstColumn()) - .append("\n"); - buffer.append("collast = ").append(getLastColumn()) - .append("\n"); - buffer.append("colwidth = ").append(getColumnWidth()) - .append("\n"); - buffer.append("xfindex = ").append(getXFIndex()).append("\n"); - buffer.append("options = ").append(getOptions()).append("\n"); - buffer.append(" hidden = ").append(getHidden()).append("\n"); - buffer.append(" olevel = ").append(getOutlineLevel()) - .append("\n"); - buffer.append(" collapsed = ").append(getCollapsed()) - .append("\n"); - buffer.append("[/COLINFO]\n"); - return buffer.toString(); + StringBuffer sb = new StringBuffer(); + + sb.append("[COLINFO]\n"); + sb.append(" colfirst = ").append(getFirstColumn()).append("\n"); + sb.append(" collast = ").append(getLastColumn()).append("\n"); + sb.append(" colwidth = ").append(getColumnWidth()).append("\n"); + sb.append(" xfindex = ").append(getXFIndex()).append("\n"); + sb.append(" options = ").append(HexDump.shortToHex(field_5_options)).append("\n"); + sb.append(" hidden = ").append(getHidden()).append("\n"); + sb.append(" olevel = ").append(getOutlineLevel()).append("\n"); + sb.append(" collapsed= ").append(getCollapsed()).append("\n"); + sb.append("[/COLINFO]\n"); + return sb.toString(); } public Object clone() { diff --git a/src/java/org/apache/poi/hssf/record/FormulaRecord.java b/src/java/org/apache/poi/hssf/record/FormulaRecord.java index 46e8283dc2..b9616e0db7 100644 --- a/src/java/org/apache/poi/hssf/record/FormulaRecord.java +++ b/src/java/org/apache/poi/hssf/record/FormulaRecord.java @@ -18,6 +18,8 @@ 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.hssf.usermodel.HSSFCell; import org.apache.poi.util.BitField; import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.HexDump; @@ -32,264 +34,430 @@ import org.apache.poi.util.LittleEndian; */ public final class FormulaRecord extends Record implements CellValueRecordInterface { - public static final short sid = 0x0006; // docs say 406...because of a bug Microsoft support site article #Q184647) - private static int FIXED_SIZE = 22; - - private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001); - private static final BitField calcOnLoad = BitFieldFactory.getInstance(0x0002); - private static final BitField sharedFormula = BitFieldFactory.getInstance(0x0008); - - private int field_1_row; - private short field_2_column; - private short field_3_xf; - private double field_4_value; - private short field_5_options; - private int field_6_zero; - private Ptg[] field_8_parsed_expr; - - /** - * Since the NaN support seems sketchy (different constants) we'll store and spit it out directly - */ - private byte[] value_data; - - /** Creates new FormulaRecord */ - - public FormulaRecord() { - field_8_parsed_expr = Ptg.EMPTY_PTG_ARRAY; - } - - /** - * Constructs a Formula record and sets its fields appropriately. - * Note - id must be 0x06 (NOT 0x406 see MSKB #Q184647 for an - * "explanation of this bug in the documentation) or an exception - * will be throw upon validation - * - * @param in the RecordInputstream to read the record from - */ - - public FormulaRecord(RecordInputStream in) { - super(in); - } - - protected void fillFields(RecordInputStream in) { - field_1_row = in.readUShort(); - field_2_column = in.readShort(); - field_3_xf = in.readShort(); - field_4_value = in.readDouble(); - field_5_options = in.readShort(); - - if (Double.isNaN(field_4_value)) { - value_data = in.getNANData(); - } - - field_6_zero = in.readInt(); - int field_7_expression_len = in.readShort(); // this length does not include any extra array data - field_8_parsed_expr = Ptg.readTokens(field_7_expression_len, in); - if (in.remaining() == 10) { - // TODO - this seems to occur when IntersectionPtg is present - // 10 extra bytes are just 0x01 and 0x00 - // This causes POI stderr: "WARN. Unread 10 bytes of record 0x6" - } - } - - public void setRow(int row) { - field_1_row = row; - } - - public void setColumn(short column) { - field_2_column = column; - } - - public void setXFIndex(short xf) { - field_3_xf = xf; - } - - /** - * set the calculated value of the formula - * - * @param value calculated value - */ - public void setValue(double value) { - field_4_value = value; - } - - /** - * set the option flags - * - * @param options bitmask - */ - public void setOptions(short options) { - field_5_options = options; - } - - public int getRow() { - return field_1_row; - } - - public short getColumn() { - return field_2_column; - } - - public short getXFIndex() { - return field_3_xf; - } - - /** - * get the calculated value of the formula - * - * @return calculated value - */ - public double getValue() { - return field_4_value; - } - - /** - * get the option flags - * - * @return bitmask - */ - public short getOptions() { - return field_5_options; - } - - public boolean isSharedFormula() { - return sharedFormula.isSet(field_5_options); - } - public void setSharedFormula(boolean flag) { - field_5_options = - sharedFormula.setShortBoolean(field_5_options, flag); - } - - public boolean isAlwaysCalc() { - return alwaysCalc.isSet(field_5_options); - } - public void setAlwaysCalc(boolean flag) { - field_5_options = - alwaysCalc.setShortBoolean(field_5_options, flag); - } - - public boolean isCalcOnLoad() { - return calcOnLoad.isSet(field_5_options); - } - public void setCalcOnLoad(boolean flag) { - field_5_options = - calcOnLoad.setShortBoolean(field_5_options, flag); - } - - /** - * @return the formula tokens. never <code>null</code> - */ - public Ptg[] getParsedExpression() { - return (Ptg[]) field_8_parsed_expr.clone(); - } - - public void setParsedExpression(Ptg[] ptgs) { - field_8_parsed_expr = ptgs; - } - - /** - * called by constructor, should throw runtime exception in the event of a - * record passed with a differing ID. - * - * @param id alleged id for this record - */ - protected void validateSid(short id) { - if (id != sid) { - throw new RecordFormatException("NOT A FORMULA RECORD"); - } - } - - public short getSid() { - return sid; - } - - private int getDataSize() { - return FIXED_SIZE + Ptg.getEncodedSize(field_8_parsed_expr); - } - public int serialize(int offset, byte [] data) { - - int dataSize = getDataSize(); - - LittleEndian.putShort(data, 0 + offset, sid); - LittleEndian.putUShort(data, 2 + offset, dataSize); - LittleEndian.putUShort(data, 4 + offset, getRow()); - LittleEndian.putShort(data, 6 + offset, getColumn()); - LittleEndian.putShort(data, 8 + offset, getXFIndex()); - - //only reserialize if the value is still NaN and we have old nan data - if (Double.isNaN(getValue()) && value_data != null) { - System.arraycopy(value_data,0,data,10 + offset,value_data.length); - } else { - LittleEndian.putDouble(data, 10 + offset, field_4_value); - } - - LittleEndian.putShort(data, 18 + offset, getOptions()); - - //when writing the chn field (offset 20), it's supposed to be 0 but ignored on read - //Microsoft Excel Developer's Kit Page 318 - LittleEndian.putInt(data, 20 + offset, 0); - int formulaTokensSize = Ptg.getEncodedSizeWithoutArrayData(field_8_parsed_expr); - LittleEndian.putUShort(data, 24 + offset, formulaTokensSize); - Ptg.serializePtgs(field_8_parsed_expr, data, 26+offset); - return 4 + dataSize; - } - - public int getRecordSize() { - return 4 + getDataSize(); - } - - public boolean isInValueSection() { - return true; - } - - public boolean isValue() { - return true; - } - - public String toString() { - - StringBuffer sb = new StringBuffer(); - sb.append("[FORMULA]\n"); - sb.append(" .row = ").append(HexDump.shortToHex(getRow())).append("\n"); - sb.append(" .column = ").append(HexDump.shortToHex(getColumn())).append("\n"); - sb.append(" .xf = ").append(HexDump.shortToHex(getXFIndex())).append("\n"); - sb.append(" .value = "); - if (Double.isNaN(this.getValue()) && value_data != null) { - sb.append("(NaN)").append(HexDump.dump(value_data,0,0)).append("\n"); - } else { - sb.append(getValue()).append("\n"); - } - sb.append(" .options = ").append(HexDump.shortToHex(getOptions())).append("\n"); - sb.append(" .alwaysCalc= ").append(alwaysCalc.isSet(getOptions())).append("\n"); - sb.append(" .calcOnLoad= ").append(calcOnLoad.isSet(getOptions())).append("\n"); - sb.append(" .shared = ").append(sharedFormula.isSet(getOptions())).append("\n"); - sb.append(" .zero = ").append(HexDump.intToHex(field_6_zero)).append("\n"); - - for (int k = 0; k < field_8_parsed_expr.length; k++ ) { - sb.append(" Ptg[").append(k).append("]="); - Ptg ptg = field_8_parsed_expr[k]; - sb.append(ptg.toString()).append(ptg.getRVAType()).append("\n"); - } - sb.append("[/FORMULA]\n"); - return sb.toString(); - } - - public Object clone() { - FormulaRecord rec = new FormulaRecord(); - rec.field_1_row = field_1_row; - rec.field_2_column = field_2_column; - rec.field_3_xf = field_3_xf; - rec.field_4_value = field_4_value; - rec.field_5_options = field_5_options; - rec.field_6_zero = field_6_zero; - int nTokens = field_8_parsed_expr.length; - Ptg[] ptgs = new Ptg[nTokens]; - for (int i=0; i< nTokens; i++) { - ptgs[i] = field_8_parsed_expr[i].copy(); - } - rec.field_8_parsed_expr = ptgs; - rec.value_data = value_data; - return rec; - } + public static final short sid = 0x0006; // docs say 406...because of a bug Microsoft support site article #Q184647) + private static int FIXED_SIZE = 22; + + private static final BitField alwaysCalc = BitFieldFactory.getInstance(0x0001); + private static final BitField calcOnLoad = BitFieldFactory.getInstance(0x0002); + private static final BitField sharedFormula = BitFieldFactory.getInstance(0x0008); + + /** + * Manages the cached formula result values of other types besides numeric. + * Excel encodes the same 8 bytes that would be field_4_value with various NaN + * values that are decoded/encoded by this class. + */ + private static final class SpecialCachedValue { + /** deliberately chosen by Excel in order to encode other values within Double NaNs */ + private static final long BIT_MARKER = 0xFFFF000000000000L; + private static final int VARIABLE_DATA_LENGTH = 6; + private static final int DATA_INDEX = 2; + + public static final int STRING = 0; + public static final int BOOLEAN = 1; + public static final int ERROR_CODE = 2; + public static final int EMPTY = 3; + + private final byte[] _variableData; + + private SpecialCachedValue(byte[] data) { + _variableData = data; + } + public int getTypeCode() { + return _variableData[0]; + } + + /** + * @return <code>null</code> if the double value encoded by <tt>valueLongBits</tt> + * is a normal (non NaN) double value. + */ + public static SpecialCachedValue create(long valueLongBits) { + if ((BIT_MARKER & valueLongBits) != BIT_MARKER) { + return null; + } + + byte[] result = new byte[VARIABLE_DATA_LENGTH]; + long x = valueLongBits; + for (int i=0; i<VARIABLE_DATA_LENGTH; i++) { + result[i] = (byte) x; + x >>= 8; + } + switch (result[0]) { + case STRING: + case BOOLEAN: + case ERROR_CODE: + case EMPTY: + break; + default: + throw new RecordFormatException("Bad special value code (" + result[0] + ")"); + } + return new SpecialCachedValue(result); + } + public void serialize(byte[] data, int offset) { + System.arraycopy(_variableData, 0, data, offset, VARIABLE_DATA_LENGTH); + LittleEndian.putUShort(data, offset+VARIABLE_DATA_LENGTH, 0xFFFF); + } + public String formatDebugString() { + return formatValue() + ' ' + HexDump.toHex(_variableData); + } + private String formatValue() { + int typeCode = getTypeCode(); + switch (typeCode) { + case STRING: return "<string>"; + case BOOLEAN: return getDataValue() == 0 ? "FALSE" : "TRUE"; + case ERROR_CODE: return ErrorEval.getText(getDataValue()); + case EMPTY: return "<empty>"; + } + return "#error(type=" + typeCode + ")#"; + } + private int getDataValue() { + return _variableData[DATA_INDEX]; + } + public static SpecialCachedValue createCachedEmptyValue() { + return create(EMPTY, 0); + } + public static SpecialCachedValue createForString() { + return create(STRING, 0); + } + public static SpecialCachedValue createCachedBoolean(boolean b) { + return create(BOOLEAN, b ? 0 : 1); + } + public static SpecialCachedValue createCachedErrorCode(int errorCode) { + return create(ERROR_CODE, errorCode); + } + private static SpecialCachedValue create(int code, int data) { + byte[] vd = { + (byte) code, + 0, + (byte) data, + 0, + 0, + 0, + }; + return new SpecialCachedValue(vd); + } + public String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()); + sb.append('[').append(formatValue()).append(']'); + return sb.toString(); + } + public int getValueType() { + int typeCode = getTypeCode(); + switch (typeCode) { + case STRING: return HSSFCell.CELL_TYPE_STRING; + case BOOLEAN: return HSSFCell.CELL_TYPE_BOOLEAN; + case ERROR_CODE: return HSSFCell.CELL_TYPE_ERROR; + case EMPTY: return HSSFCell.CELL_TYPE_STRING; // is this correct? + } + throw new IllegalStateException("Unexpected type id (" + typeCode + ")"); + } + public boolean getBooleanValue() { + if (getTypeCode() != BOOLEAN) { + throw new IllegalStateException("Not a boolean cached value - " + formatValue()); + } + return getDataValue() != 0; + } + public int getErrorValue() { + if (getTypeCode() != ERROR_CODE) { + throw new IllegalStateException("Not an error cached value - " + formatValue()); + } + return getDataValue(); + } + } + + + + private int field_1_row; + private short field_2_column; + private short field_3_xf; + private double field_4_value; + private short field_5_options; + private int field_6_zero; + private Ptg[] field_8_parsed_expr; + + /** + * Since the NaN support seems sketchy (different constants) we'll store and spit it out directly + */ + private SpecialCachedValue specialCachedValue; + + /** Creates new FormulaRecord */ + + public FormulaRecord() { + field_8_parsed_expr = Ptg.EMPTY_PTG_ARRAY; + } + + /** + * Constructs a Formula record and sets its fields appropriately. + * Note - id must be 0x06 (NOT 0x406 see MSKB #Q184647 for an + * "explanation of this bug in the documentation) or an exception + * will be throw upon validation + * + * @param in the RecordInputstream to read the record from + */ + + public FormulaRecord(RecordInputStream in) { + super(in); + } + + protected void fillFields(RecordInputStream in) { + field_1_row = in.readUShort(); + field_2_column = in.readShort(); + field_3_xf = in.readShort(); + long valueLongBits = in.readLong(); + field_5_options = in.readShort(); + specialCachedValue = SpecialCachedValue.create(valueLongBits); + if (specialCachedValue == null) { + field_4_value = Double.longBitsToDouble(valueLongBits); + } + + field_6_zero = in.readInt(); + int field_7_expression_len = in.readShort(); // this length does not include any extra array data + field_8_parsed_expr = Ptg.readTokens(field_7_expression_len, in); + if (in.remaining() == 10) { + // TODO - this seems to occur when IntersectionPtg is present + // 10 extra bytes are just 0x01 and 0x00 + // This causes POI stderr: "WARN. Unread 10 bytes of record 0x6" + } + } + + + public void setRow(int row) { + field_1_row = row; + } + + public void setColumn(short column) { + field_2_column = column; + } + + public void setXFIndex(short xf) { + field_3_xf = xf; + } + + /** + * set the calculated value of the formula + * + * @param value calculated value + */ + public void setValue(double value) { + field_4_value = value; + specialCachedValue = null; + } + + public void setCachedResultTypeEmptyString() { + specialCachedValue = SpecialCachedValue.createCachedEmptyValue(); + } + public void setCachedResultTypeString() { + specialCachedValue = SpecialCachedValue.createForString(); + } + public void setCachedResultErrorCode(int errorCode) { + specialCachedValue = SpecialCachedValue.createCachedErrorCode(errorCode); + } + public void setCachedResultBoolean(boolean value) { + specialCachedValue = SpecialCachedValue.createCachedBoolean(value); + } + /** + * @return <code>true</code> if this {@link FormulaRecord} is followed by a + * {@link StringRecord} representing the cached text result of the formula + * evaluation. + */ + public boolean hasCachedResultString() { + if (specialCachedValue == null) { + return false; + } + return specialCachedValue.getTypeCode() == SpecialCachedValue.STRING; + } + + public int getCachedResultType() { + if (specialCachedValue == null) { + return HSSFCell.CELL_TYPE_NUMERIC; + } + return specialCachedValue.getValueType(); + } + + public boolean getCachedBooleanValue() { + return specialCachedValue.getBooleanValue(); + } + public int getCachedErrorValue() { + return specialCachedValue.getErrorValue(); + } + + + /** + * set the option flags + * + * @param options bitmask + */ + public void setOptions(short options) { + field_5_options = options; + } + + public int getRow() { + return field_1_row; + } + + public short getColumn() { + return field_2_column; + } + + public short getXFIndex() { + return field_3_xf; + } + + /** + * get the calculated value of the formula + * + * @return calculated value + */ + public double getValue() { + return field_4_value; + } + + /** + * get the option flags + * + * @return bitmask + */ + public short getOptions() { + return field_5_options; + } + + public boolean isSharedFormula() { + return sharedFormula.isSet(field_5_options); + } + public void setSharedFormula(boolean flag) { + field_5_options = + sharedFormula.setShortBoolean(field_5_options, flag); + } + + public boolean isAlwaysCalc() { + return alwaysCalc.isSet(field_5_options); + } + public void setAlwaysCalc(boolean flag) { + field_5_options = + alwaysCalc.setShortBoolean(field_5_options, flag); + } + + public boolean isCalcOnLoad() { + return calcOnLoad.isSet(field_5_options); + } + public void setCalcOnLoad(boolean flag) { + field_5_options = + calcOnLoad.setShortBoolean(field_5_options, flag); + } + + /** + * @return the formula tokens. never <code>null</code> + */ + public Ptg[] getParsedExpression() { + return (Ptg[]) field_8_parsed_expr.clone(); + } + + public void setParsedExpression(Ptg[] ptgs) { + field_8_parsed_expr = ptgs; + } + + /** + * called by constructor, should throw runtime exception in the event of a + * record passed with a differing ID. + * + * @param id alleged id for this record + */ + protected void validateSid(short id) { + if (id != sid) { + throw new RecordFormatException("NOT A FORMULA RECORD"); + } + } + + public short getSid() { + return sid; + } + + private int getDataSize() { + return FIXED_SIZE + Ptg.getEncodedSize(field_8_parsed_expr); + } + public int serialize(int offset, byte [] data) { + + int dataSize = getDataSize(); + + LittleEndian.putShort(data, 0 + offset, sid); + LittleEndian.putUShort(data, 2 + offset, dataSize); + LittleEndian.putUShort(data, 4 + offset, getRow()); + LittleEndian.putShort(data, 6 + offset, getColumn()); + LittleEndian.putShort(data, 8 + offset, getXFIndex()); + + if (specialCachedValue == null) { + LittleEndian.putDouble(data, 10 + offset, field_4_value); + } else { + specialCachedValue.serialize(data, 10+offset); + } + + LittleEndian.putShort(data, 18 + offset, getOptions()); + + //when writing the chn field (offset 20), it's supposed to be 0 but ignored on read + //Microsoft Excel Developer's Kit Page 318 + LittleEndian.putInt(data, 20 + offset, 0); + int formulaTokensSize = Ptg.getEncodedSizeWithoutArrayData(field_8_parsed_expr); + LittleEndian.putUShort(data, 24 + offset, formulaTokensSize); + Ptg.serializePtgs(field_8_parsed_expr, data, 26+offset); + return 4 + dataSize; + } + + public int getRecordSize() { + return 4 + getDataSize(); + } + + public boolean isInValueSection() { + return true; + } + + public boolean isValue() { + return true; + } + + public String toString() { + + StringBuffer sb = new StringBuffer(); + sb.append("[FORMULA]\n"); + sb.append(" .row = ").append(HexDump.shortToHex(getRow())).append("\n"); + sb.append(" .column = ").append(HexDump.shortToHex(getColumn())).append("\n"); + sb.append(" .xf = ").append(HexDump.shortToHex(getXFIndex())).append("\n"); + sb.append(" .value = "); + if (specialCachedValue == null) { + sb.append(field_4_value).append("\n"); + } else { + sb.append(specialCachedValue.formatDebugString()).append("\n"); + } + sb.append(" .options = ").append(HexDump.shortToHex(getOptions())).append("\n"); + sb.append(" .alwaysCalc= ").append(alwaysCalc.isSet(getOptions())).append("\n"); + sb.append(" .calcOnLoad= ").append(calcOnLoad.isSet(getOptions())).append("\n"); + sb.append(" .shared = ").append(sharedFormula.isSet(getOptions())).append("\n"); + sb.append(" .zero = ").append(HexDump.intToHex(field_6_zero)).append("\n"); + + for (int k = 0; k < field_8_parsed_expr.length; k++ ) { + sb.append(" Ptg[").append(k).append("]="); + Ptg ptg = field_8_parsed_expr[k]; + sb.append(ptg.toString()).append(ptg.getRVAType()).append("\n"); + } + sb.append("[/FORMULA]\n"); + return sb.toString(); + } + + public Object clone() { + FormulaRecord rec = new FormulaRecord(); + rec.field_1_row = field_1_row; + rec.field_2_column = field_2_column; + rec.field_3_xf = field_3_xf; + rec.field_4_value = field_4_value; + rec.field_5_options = field_5_options; + rec.field_6_zero = field_6_zero; + int nTokens = field_8_parsed_expr.length; + Ptg[] ptgs = new Ptg[nTokens]; + for (int i = 0; i < nTokens; i++) { + ptgs[i] = field_8_parsed_expr[i].copy(); + } + rec.field_8_parsed_expr = ptgs; + rec.specialCachedValue = specialCachedValue; + return rec; + } } diff --git a/src/java/org/apache/poi/hssf/record/RecordInputStream.java b/src/java/org/apache/poi/hssf/record/RecordInputStream.java index 12c818b183..fe6a4b2ea3 100755 --- a/src/java/org/apache/poi/hssf/record/RecordInputStream.java +++ b/src/java/org/apache/poi/hssf/record/RecordInputStream.java @@ -209,30 +209,18 @@ public class RecordInputStream extends InputStream { return result; } - byte[] NAN_data = null; public double readDouble() { - checkRecordPosition(); - //Reset NAN data - NAN_data = null; - double result = LittleEndian.getDouble(data, recordOffset); - //Excel represents NAN in several ways, at this point in time we do not often - //know the sequence of bytes, so as a hack we store the NAN byte sequence - //so that it is not corrupted. + checkRecordPosition(); + long valueLongBits = LittleEndian.getLong(data, recordOffset); + double result = Double.longBitsToDouble(valueLongBits); if (Double.isNaN(result)) { - NAN_data = new byte[8]; - System.arraycopy(data, recordOffset, NAN_data, 0, 8); + throw new RuntimeException("Did not expect to read NaN"); } - recordOffset += LittleEndian.DOUBLE_SIZE; pos += LittleEndian.DOUBLE_SIZE; return result; } - - public byte[] getNANData() { - if (NAN_data == null) - throw new RecordFormatException("Do NOT call getNANData without calling readDouble that returns NaN"); - return NAN_data; - } + public short[] readShortArray() { checkRecordPosition(); @@ -276,9 +264,6 @@ public class RecordInputStream extends InputStream { } public String readCompressedUnicode(int length) { - if(length == 0) { - return ""; - } if ((length < 0) || ((remaining() < length) && !isContinueNext())) { throw new IllegalArgumentException("Illegal length " + length); } @@ -291,9 +276,7 @@ public class RecordInputStream extends InputStream { if(compressByte != 0) throw new IllegalArgumentException("compressByte in continue records must be 0 while reading compressed unicode"); } byte b = readByte(); - //Typecast direct to char from byte with high bit set causes all ones - //in the high byte of the char (which is of course incorrect) - char ch = (char)( (short)0xff & (short)b ); + char ch = (char)(0x00FF & b); // avoid sex buf.append(ch); } return buf.toString(); diff --git a/src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java index b24d8c5b45..0f405bd111 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java @@ -18,18 +18,35 @@ package org.apache.poi.hssf.record.aggregates; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import org.apache.poi.hssf.model.RecordStream; import org.apache.poi.hssf.record.ColumnInfoRecord; -import org.apache.poi.hssf.record.Record; /** * @author Glen Stampoultzis - * @version $Id$ */ public final class ColumnInfoRecordsAggregate extends RecordAggregate { + /** + * List of {@link ColumnInfoRecord}s assumed to be in order + */ private final List records; + + + private static final class CIRComparator implements Comparator { + public static final Comparator instance = new CIRComparator(); + private CIRComparator() { + // enforce singleton + } + public int compare(Object a, Object b) { + return compareColInfos((ColumnInfoRecord)a, (ColumnInfoRecord)b); + } + public static int compareColInfos(ColumnInfoRecord a, ColumnInfoRecord b) { + return a.getFirstColumn()-b.getFirstColumn(); + } + } /** * Creates an empty aggregate @@ -37,486 +54,470 @@ public final class ColumnInfoRecordsAggregate extends RecordAggregate { public ColumnInfoRecordsAggregate() { records = new ArrayList(); } - public ColumnInfoRecordsAggregate(RecordStream rs) { - this(); - - while(rs.peekNextClass() == ColumnInfoRecord.class) { - records.add(rs.getNext()); - } - if (records.size() < 1) { - throw new RuntimeException("No column info records found"); - } - } - - /** - * Performs a deep clone of the record - */ - public Object clone() - { - ColumnInfoRecordsAggregate rec = new ColumnInfoRecordsAggregate(); - for (int k = 0; k < records.size(); k++) - { - ColumnInfoRecord ci = ( ColumnInfoRecord ) records.get(k); - ci=(ColumnInfoRecord) ci.clone(); - rec.insertColumn( ci ); - } - return rec; - } - - /** - * Inserts a column into the aggregate (at the end of the list). - */ - public void insertColumn( ColumnInfoRecord col ) - { - records.add( col ); - } - - /** - * Inserts a column into the aggregate (at the position specified - * by <code>idx</code>. - */ - public void insertColumn( int idx, ColumnInfoRecord col ) - { - records.add( idx, col ); - } - - public int getNumColumns( ) - { - return records.size(); - } - - public void visitContainedRecords(RecordVisitor rv) { - int nItems = records.size(); - if (nItems < 1) { - return; - } - for(int i=0; i<nItems; i++) { - rv.visitRecord((Record)records.get(i)); - } - } - - public int findStartOfColumnOutlineGroup(int idx) - { - // Find the start of the group. - ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get( idx ); - int level = columnInfo.getOutlineLevel(); - while (idx != 0) - { - ColumnInfoRecord prevColumnInfo = (ColumnInfoRecord) records.get( idx - 1 ); - if (columnInfo.getFirstColumn() - 1 == prevColumnInfo.getLastColumn()) - { - if (prevColumnInfo.getOutlineLevel() < level) - { - break; - } - idx--; - columnInfo = prevColumnInfo; - } - else - { - break; - } - } - - return idx; - } - - public int findEndOfColumnOutlineGroup(int idx) - { - // Find the end of the group. - ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get( idx ); - int level = columnInfo.getOutlineLevel(); - while (idx < records.size() - 1) - { - ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) records.get( idx + 1 ); - if (columnInfo.getLastColumn() + 1 == nextColumnInfo.getFirstColumn()) - { - if (nextColumnInfo.getOutlineLevel() < level) - { - break; - } - idx++; - columnInfo = nextColumnInfo; - } - else - { - break; - } - } - - return idx; - } - - private ColumnInfoRecord getColInfo(int idx) { - return (ColumnInfoRecord) records.get( idx ); - } - - public ColumnInfoRecord writeHidden( ColumnInfoRecord columnInfo, int idx, boolean hidden ) - { - int level = columnInfo.getOutlineLevel(); - while (idx < records.size()) - { - columnInfo.setHidden( hidden ); - if (idx + 1 < records.size()) - { - ColumnInfoRecord nextColumnInfo = getColInfo(idx + 1); - if (columnInfo.getLastColumn() + 1 == nextColumnInfo.getFirstColumn()) - { - if (nextColumnInfo.getOutlineLevel() < level) - break; - columnInfo = nextColumnInfo; - } - else - { - break; - } - } - idx++; - } - return columnInfo; - } - - public boolean isColumnGroupCollapsed( int idx ) - { - int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx ); - if (endOfOutlineGroupIdx >= records.size()) - return false; - if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn()) - return false; - else - return getColInfo(endOfOutlineGroupIdx+1).getCollapsed(); - } - - - public boolean isColumnGroupHiddenByParent( int idx ) - { - // Look out outline details of end - int endLevel; - boolean endHidden; - int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx ); - if (endOfOutlineGroupIdx >= records.size()) - { - endLevel = 0; - endHidden = false; - } - else if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn()) - { - endLevel = 0; - endHidden = false; - } - else - { - endLevel = getColInfo( endOfOutlineGroupIdx + 1).getOutlineLevel(); - endHidden = getColInfo( endOfOutlineGroupIdx + 1).getHidden(); - } - - // Look out outline details of start - int startLevel; - boolean startHidden; - int startOfOutlineGroupIdx = findStartOfColumnOutlineGroup( idx ); - if (startOfOutlineGroupIdx <= 0) - { - startLevel = 0; - startHidden = false; - } - else if (getColInfo(startOfOutlineGroupIdx).getFirstColumn() - 1 != getColInfo(startOfOutlineGroupIdx - 1).getLastColumn()) - { - startLevel = 0; - startHidden = false; - } - else - { - startLevel = getColInfo( startOfOutlineGroupIdx - 1).getOutlineLevel(); - startHidden = getColInfo( startOfOutlineGroupIdx - 1 ).getHidden(); - } - - if (endLevel > startLevel) - { - return endHidden; - } - else - { - return startHidden; - } - } - - public void collapseColumn( short columnNumber ) - { - int idx = findColumnIdx( columnNumber, 0 ); - if (idx == -1) - return; - - // Find the start of the group. - ColumnInfoRecord columnInfo = getColInfo( findStartOfColumnOutlineGroup( idx ) ); - - // Hide all the columns until the end of the group - columnInfo = writeHidden( columnInfo, idx, true ); - - // Write collapse field - setColumn( (short) ( columnInfo.getLastColumn() + 1 ), null, null, null, null, Boolean.TRUE); - } - - public void expandColumn( short columnNumber ) - { - int idx = findColumnIdx( columnNumber, 0 ); - if (idx == -1) - return; - - // If it is already exapanded do nothing. - if (!isColumnGroupCollapsed(idx)) - return; - - // Find the start of the group. - int startIdx = findStartOfColumnOutlineGroup( idx ); - ColumnInfoRecord columnInfo = getColInfo( startIdx ); - - // Find the end of the group. - int endIdx = findEndOfColumnOutlineGroup( idx ); - ColumnInfoRecord endColumnInfo = getColInfo( endIdx ); - - // expand: - // colapsed bit must be unset - // hidden bit gets unset _if_ surrounding groups are expanded you can determine - // this by looking at the hidden bit of the enclosing group. You will have - // to look at the start and the end of the current group to determine which - // is the enclosing group - // hidden bit only is altered for this outline level. ie. don't uncollapse contained groups - if (!isColumnGroupHiddenByParent( idx )) - { - for (int i = startIdx; i <= endIdx; i++) - { - if (columnInfo.getOutlineLevel() == getColInfo(i).getOutlineLevel()) - getColInfo(i).setHidden( false ); - } - } - - // Write collapse field - setColumn( (short) ( columnInfo.getLastColumn() + 1 ), null, null, null, null, Boolean.FALSE); - } - - /** - * creates the ColumnInfo Record and sets it to a default column/width - * @see org.apache.poi.hssf.record.ColumnInfoRecord - * @return record containing a ColumnInfoRecord - */ - public static ColumnInfoRecord createColInfo() - { - ColumnInfoRecord retval = new ColumnInfoRecord(); - - retval.setColumnWidth(( short ) 2275); - // was: retval.setOptions(( short ) 6); - retval.setOptions(( short ) 2); - retval.setXFIndex(( short ) 0x0f); - return retval; - } - - - public void setColumn(short column, Short xfIndex, Short width, Integer level, Boolean hidden, Boolean collapsed) - { - ColumnInfoRecord ci = null; - int k = 0; - - for (k = 0; k < records.size(); k++) - { - ci = ( ColumnInfoRecord ) records.get(k); - if ((ci.getFirstColumn() <= column) - && (column <= ci.getLastColumn())) - { - break; - } - ci = null; - } - - if (ci != null) - { - boolean styleChanged = xfIndex != null && ci.getXFIndex() != xfIndex.shortValue(); - boolean widthChanged = width != null && ci.getColumnWidth() != width.shortValue(); - boolean levelChanged = level != null && ci.getOutlineLevel() != level.intValue(); - boolean hiddenChanged = hidden != null && ci.getHidden() != hidden.booleanValue(); - boolean collapsedChanged = collapsed != null && ci.getCollapsed() != collapsed.booleanValue(); - boolean columnChanged = styleChanged || widthChanged || levelChanged || hiddenChanged || collapsedChanged; - if (!columnChanged) - { - // do nothing...nothing changed. - } - else if ((ci.getFirstColumn() == column) - && (ci.getLastColumn() == column)) - { // if its only for this cell then - setColumnInfoFields( ci, xfIndex, width, level, hidden, collapsed ); - } - else if ((ci.getFirstColumn() == column) - || (ci.getLastColumn() == column)) - { - // okay so the width is different but the first or last column == the column we'return setting - // we'll just divide the info and create a new one - if (ci.getFirstColumn() == column) - { - ci.setFirstColumn(( short ) (column + 1)); - } - else - { - ci.setLastColumn(( short ) (column - 1)); - } - ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo(); - - nci.setFirstColumn(column); - nci.setLastColumn(column); - nci.setOptions(ci.getOptions()); - nci.setXFIndex(ci.getXFIndex()); - setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed ); - - insertColumn(k, nci); - } - else - { - //split to 3 records - short lastcolumn = ci.getLastColumn(); - ci.setLastColumn(( short ) (column - 1)); - - ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo(); - nci.setFirstColumn(column); - nci.setLastColumn(column); - nci.setOptions(ci.getOptions()); - nci.setXFIndex(ci.getXFIndex()); - setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed ); - insertColumn(++k, nci); - - nci = ( ColumnInfoRecord ) createColInfo(); - nci.setFirstColumn((short)(column+1)); - nci.setLastColumn(lastcolumn); - nci.setOptions(ci.getOptions()); - nci.setXFIndex(ci.getXFIndex()); - nci.setColumnWidth(ci.getColumnWidth()); - insertColumn(++k, nci); - } - } - else - { - - // okay so there ISN'T a column info record that cover's this column so lets create one! - ColumnInfoRecord nci = ( ColumnInfoRecord ) createColInfo(); - - nci.setFirstColumn(column); - nci.setLastColumn(column); - setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed ); - insertColumn(k, nci); - } - } - - /** - * Sets all non null fields into the <code>ci</code> parameter. - */ - private void setColumnInfoFields( ColumnInfoRecord ci, Short xfStyle, Short width, Integer level, Boolean hidden, Boolean collapsed ) - { - if (xfStyle != null) - ci.setXFIndex(xfStyle.shortValue()); - if (width != null) - ci.setColumnWidth(width.shortValue()); - if (level != null) - ci.setOutlineLevel( level.shortValue() ); - if (hidden != null) - ci.setHidden( hidden.booleanValue() ); - if (collapsed != null) - ci.setCollapsed( collapsed.booleanValue() ); - } - - private int findColumnIdx(int column, int fromIdx) - { - if (column < 0) - throw new IllegalArgumentException( "column parameter out of range: " + column ); - if (fromIdx < 0) - throw new IllegalArgumentException( "fromIdx parameter out of range: " + fromIdx ); - - ColumnInfoRecord ci; - for (int k = fromIdx; k < records.size(); k++) - { - ci = getColInfo(k); - if ((ci.getFirstColumn() <= column) - && (column <= ci.getLastColumn())) - { - return k; - } - ci = null; - } - return -1; - } - - public void collapseColInfoRecords( int columnIdx ) - { - if (columnIdx == 0) - return; - ColumnInfoRecord previousCol = getColInfo( columnIdx - 1); - ColumnInfoRecord currentCol = getColInfo( columnIdx ); - boolean adjacentColumns = previousCol.getLastColumn() == currentCol.getFirstColumn() - 1; - if (!adjacentColumns) - return; - - boolean columnsMatch = - previousCol.getXFIndex() == currentCol.getXFIndex() && - previousCol.getOptions() == currentCol.getOptions() && - previousCol.getColumnWidth() == currentCol.getColumnWidth(); - - if (columnsMatch) - { - previousCol.setLastColumn( currentCol.getLastColumn() ); - records.remove( columnIdx ); - } - } - - /** - * Creates an outline group for the specified columns. - * @param fromColumn group from this column (inclusive) - * @param toColumn group to this column (inclusive) - * @param indent if true the group will be indented by one level, - * if false indenting will be removed by one level. - */ - public void groupColumnRange(short fromColumn, short toColumn, boolean indent) - { - - // Set the level for each column - int fromIdx = 0; - for (int i = fromColumn; i <= toColumn; i++) - { - int level = 1; - int columnIdx = findColumnIdx( i, Math.max(0,fromIdx) ); - if (columnIdx != -1) - { - level = getColInfo(columnIdx).getOutlineLevel(); - if (indent) level++; else level--; - level = Math.max(0, level); - level = Math.min(7, level); - fromIdx = columnIdx - 1; // subtract 1 just in case this column is collapsed later. - } - setColumn((short)i, null, null, new Integer(level), null, null); - columnIdx = findColumnIdx( i, Math.max(0, fromIdx ) ); - collapseColInfoRecords( columnIdx ); - } - - } - /** - * Finds the <tt>ColumnInfoRecord</tt> which contains the specified columnIndex - * @param columnIndex index of the column (not the index of the ColumnInfoRecord) - * @return <code>null</code> if no column info found for the specified column - */ + public ColumnInfoRecordsAggregate(RecordStream rs) { + this(); + + boolean isInOrder = true; + ColumnInfoRecord cirPrev = null; + while(rs.peekNextClass() == ColumnInfoRecord.class) { + ColumnInfoRecord cir = (ColumnInfoRecord) rs.getNext(); + records.add(cir); + if (cirPrev != null && CIRComparator.compareColInfos(cirPrev, cir) > 0) { + isInOrder = false; + } + cirPrev = cir; + } + if (records.size() < 1) { + throw new RuntimeException("No column info records found"); + } + if (!isInOrder) { + Collections.sort(records, CIRComparator.instance); + } + } + + /** + * Performs a deep clone of the record + */ + public Object clone() { + ColumnInfoRecordsAggregate rec = new ColumnInfoRecordsAggregate(); + for (int k = 0; k < records.size(); k++) { + ColumnInfoRecord ci = ( ColumnInfoRecord ) records.get(k); + rec.records.add(ci.clone()); + } + return rec; + } + + /** + * Inserts a column into the aggregate (at the end of the list). + */ + public void insertColumn(ColumnInfoRecord col) { + records.add(col); + Collections.sort(records, CIRComparator.instance); + } + + /** + * Inserts a column into the aggregate (at the position specified by + * <code>idx</code>. + */ + private void insertColumn(int idx, ColumnInfoRecord col) { + records.add(idx, col); + } + + /* package */ int getNumColumns() { + return records.size(); + } + + public void visitContainedRecords(RecordVisitor rv) { + int nItems = records.size(); + if (nItems < 1) { + return; + } + ColumnInfoRecord cirPrev = null; + for(int i=0; i<nItems; i++) { + ColumnInfoRecord cir = (ColumnInfoRecord)records.get(i); + rv.visitRecord(cir); + if (cirPrev != null && CIRComparator.compareColInfos(cirPrev, cir) > 0) { + // Excel probably wouldn't mind, but there is much logic in this class + // that assumes the column info records are kept in order + throw new RuntimeException("Column info records are out of order"); + } + cirPrev = cir; + } + } + + private int findStartOfColumnOutlineGroup(int pIdx) { + // Find the start of the group. + ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get(pIdx); + int level = columnInfo.getOutlineLevel(); + int idx = pIdx; + while (idx != 0) { + ColumnInfoRecord prevColumnInfo = (ColumnInfoRecord) records.get(idx - 1); + if (!prevColumnInfo.isAdjacentBefore(columnInfo)) { + break; + } + if (prevColumnInfo.getOutlineLevel() < level) { + break; + } + idx--; + columnInfo = prevColumnInfo; + } + + return idx; + } + + private int findEndOfColumnOutlineGroup(int colInfoIndex) { + // Find the end of the group. + ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get(colInfoIndex); + int level = columnInfo.getOutlineLevel(); + int idx = colInfoIndex; + while (idx < records.size() - 1) { + ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) records.get(idx + 1); + if (!columnInfo.isAdjacentBefore(nextColumnInfo)) { + break; + } + if (nextColumnInfo.getOutlineLevel() < level) { + break; + } + idx++; + columnInfo = nextColumnInfo; + } + return idx; + } + + private ColumnInfoRecord getColInfo(int idx) { + return (ColumnInfoRecord) records.get( idx ); + } + + /** + * 'Collapsed' state is stored in a single column col info record immediately after the outline group + * @param idx + * @return + */ + private boolean isColumnGroupCollapsed(int idx) { + int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup(idx); + int nextColInfoIx = endOfOutlineGroupIdx+1; + if (nextColInfoIx >= records.size()) { + return false; + } + ColumnInfoRecord nextColInfo = getColInfo(nextColInfoIx); + if (!getColInfo(endOfOutlineGroupIdx).isAdjacentBefore(nextColInfo)) { + return false; + } + return nextColInfo.getCollapsed(); + } + + + private boolean isColumnGroupHiddenByParent(int idx) { + // Look out outline details of end + int endLevel = 0; + boolean endHidden = false; + int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx ); + if (endOfOutlineGroupIdx < records.size()) { + ColumnInfoRecord nextInfo = getColInfo(endOfOutlineGroupIdx + 1); + if (getColInfo(endOfOutlineGroupIdx).isAdjacentBefore(nextInfo)) { + endLevel = nextInfo.getOutlineLevel(); + endHidden = nextInfo.getHidden(); + } + } + // Look out outline details of start + int startLevel = 0; + boolean startHidden = false; + int startOfOutlineGroupIdx = findStartOfColumnOutlineGroup( idx ); + if (startOfOutlineGroupIdx > 0) { + ColumnInfoRecord prevInfo = getColInfo(startOfOutlineGroupIdx - 1); + if (prevInfo.isAdjacentBefore(getColInfo(startOfOutlineGroupIdx))) { + startLevel = prevInfo.getOutlineLevel(); + startHidden = prevInfo.getHidden(); + } + } + if (endLevel > startLevel) { + return endHidden; + } + return startHidden; + } + + public void collapseColumn(int columnIndex) { + int colInfoIx = findColInfoIdx(columnIndex, 0); + if (colInfoIx == -1) { + return; + } + + // Find the start of the group. + int groupStartColInfoIx = findStartOfColumnOutlineGroup(colInfoIx); + ColumnInfoRecord columnInfo = getColInfo(groupStartColInfoIx); + + // Hide all the columns until the end of the group + int lastColIx = setGroupHidden(groupStartColInfoIx, columnInfo.getOutlineLevel(), true); + + // Write collapse field + setColumn(lastColIx + 1, null, null, null, null, Boolean.TRUE); + } + /** + * Sets all adjacent columns of the same outline level to the specified hidden status. + * @param pIdx the col info index of the start of the outline group + * @return the column index of the last column in the outline group + */ + private int setGroupHidden(int pIdx, int level, boolean hidden) { + int idx = pIdx; + ColumnInfoRecord columnInfo = getColInfo(idx); + while (idx < records.size()) { + columnInfo.setHidden(hidden); + if (idx + 1 < records.size()) { + ColumnInfoRecord nextColumnInfo = getColInfo(idx + 1); + if (!columnInfo.isAdjacentBefore(nextColumnInfo)) { + break; + } + if (nextColumnInfo.getOutlineLevel() < level) { + break; + } + columnInfo = nextColumnInfo; + } + idx++; + } + return columnInfo.getLastColumn(); + } + + + public void expandColumn(int columnIndex) { + int idx = findColInfoIdx(columnIndex, 0); + if (idx == -1) { + return; + } + + // If it is already expanded do nothing. + if (!isColumnGroupCollapsed(idx)) { + return; + } + + // Find the start/end of the group. + int startIdx = findStartOfColumnOutlineGroup(idx); + int endIdx = findEndOfColumnOutlineGroup(idx); + + // expand: + // colapsed bit must be unset + // hidden bit gets unset _if_ surrounding groups are expanded you can determine + // this by looking at the hidden bit of the enclosing group. You will have + // to look at the start and the end of the current group to determine which + // is the enclosing group + // hidden bit only is altered for this outline level. ie. don't uncollapse contained groups + ColumnInfoRecord columnInfo = getColInfo(endIdx); + if (!isColumnGroupHiddenByParent(idx)) { + int outlineLevel = columnInfo.getOutlineLevel(); + for (int i = startIdx; i <= endIdx; i++) { + ColumnInfoRecord ci = getColInfo(i); + if (outlineLevel == ci.getOutlineLevel()) + ci.setHidden(false); + } + } + + // Write collapse flag (stored in a single col info record after this outline group) + setColumn(columnInfo.getLastColumn() + 1, null, null, null, null, Boolean.FALSE); + } + + private static ColumnInfoRecord copyColInfo(ColumnInfoRecord ci) { + return (ColumnInfoRecord) ci.clone(); + } + + + public void setColumn(int targetColumnIx, Short xfIndex, Short width, + Integer level, Boolean hidden, Boolean collapsed) { + ColumnInfoRecord ci = null; + int k = 0; + + for (k = 0; k < records.size(); k++) { + ColumnInfoRecord tci = (ColumnInfoRecord) records.get(k); + if (tci.containsColumn(targetColumnIx)) { + ci = tci; + break; + } + if (tci.getFirstColumn() > targetColumnIx) { + // call column infos after k are for later columns + break; // exit now so k will be the correct insert pos + } + } + + if (ci == null) { + // okay so there ISN'T a column info record that covers this column so lets create one! + ColumnInfoRecord nci = new ColumnInfoRecord(); + + nci.setFirstColumn(targetColumnIx); + nci.setLastColumn(targetColumnIx); + setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed ); + insertColumn(k, nci); + attemptMergeColInfoRecords(k); + return; + } + + boolean styleChanged = xfIndex != null && ci.getXFIndex() != xfIndex.shortValue(); + boolean widthChanged = width != null && ci.getColumnWidth() != width.shortValue(); + boolean levelChanged = level != null && ci.getOutlineLevel() != level.intValue(); + boolean hiddenChanged = hidden != null && ci.getHidden() != hidden.booleanValue(); + boolean collapsedChanged = collapsed != null && ci.getCollapsed() != collapsed.booleanValue(); + + boolean columnChanged = styleChanged || widthChanged || levelChanged || hiddenChanged || collapsedChanged; + if (!columnChanged) { + // do nothing...nothing changed. + return; + } + + if (ci.getFirstColumn() == targetColumnIx && ci.getLastColumn() == targetColumnIx) { + // ColumnInfo ci for a single column, the target column + setColumnInfoFields(ci, xfIndex, width, level, hidden, collapsed); + attemptMergeColInfoRecords(k); + return; + } + + if (ci.getFirstColumn() == targetColumnIx || ci.getLastColumn() == targetColumnIx) { + // The target column is at either end of the multi-column ColumnInfo ci + // we'll just divide the info and create a new one + if (ci.getFirstColumn() == targetColumnIx) { + ci.setFirstColumn(targetColumnIx + 1); + } else { + ci.setLastColumn(targetColumnIx - 1); + k++; // adjust insert pos to insert after + } + ColumnInfoRecord nci = copyColInfo(ci); + + nci.setFirstColumn(targetColumnIx); + nci.setLastColumn(targetColumnIx); + setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed ); + + insertColumn(k, nci); + attemptMergeColInfoRecords(k); + } else { + //split to 3 records + ColumnInfoRecord ciStart = ci; + ColumnInfoRecord ciMid = copyColInfo(ci); + ColumnInfoRecord ciEnd = copyColInfo(ci); + int lastcolumn = ci.getLastColumn(); + + ciStart.setLastColumn(targetColumnIx - 1); + + ciMid.setFirstColumn(targetColumnIx); + ciMid.setLastColumn(targetColumnIx); + setColumnInfoFields(ciMid, xfIndex, width, level, hidden, collapsed); + insertColumn(++k, ciMid); + + ciEnd.setFirstColumn(targetColumnIx+1); + ciEnd.setLastColumn(lastcolumn); + insertColumn(++k, ciEnd); + // no need to attemptMergeColInfoRecords because we + // know both on each side are different + } + } + + /** + * Sets all non null fields into the <code>ci</code> parameter. + */ + private static void setColumnInfoFields( ColumnInfoRecord ci, Short xfStyle, Short width, + Integer level, Boolean hidden, Boolean collapsed ) { + if (xfStyle != null) { + ci.setXFIndex(xfStyle.shortValue()); + } + if (width != null) { + ci.setColumnWidth(width.shortValue()); + } + if (level != null) { + ci.setOutlineLevel( level.shortValue() ); + } + if (hidden != null) { + ci.setHidden( hidden.booleanValue() ); + } + if (collapsed != null) { + ci.setCollapsed( collapsed.booleanValue() ); + } + } + + private int findColInfoIdx(int columnIx, int fromColInfoIdx) { + if (columnIx < 0) { + throw new IllegalArgumentException( "column parameter out of range: " + columnIx ); + } + if (fromColInfoIdx < 0) { + throw new IllegalArgumentException( "fromIdx parameter out of range: " + fromColInfoIdx ); + } + + for (int k = fromColInfoIdx; k < records.size(); k++) { + ColumnInfoRecord ci = getColInfo(k); + if (ci.containsColumn(columnIx)) { + return k; + } + if (ci.getFirstColumn() > columnIx) { + break; + } + } + return -1; + } + + /** + * Attempts to merge the col info record at the specified index + * with either or both of its neighbours + */ + private void attemptMergeColInfoRecords(int colInfoIx) { + int nRecords = records.size(); + if (colInfoIx < 0 || colInfoIx >= nRecords) { + throw new IllegalArgumentException("colInfoIx " + colInfoIx + + " is out of range (0.." + (nRecords-1) + ")"); + } + ColumnInfoRecord currentCol = getColInfo(colInfoIx); + int nextIx = colInfoIx+1; + if (nextIx < nRecords) { + if (mergeColInfoRecords(currentCol, getColInfo(nextIx))) { + records.remove(nextIx); + } + } + if (colInfoIx > 0) { + if (mergeColInfoRecords(getColInfo(colInfoIx - 1), currentCol)) { + records.remove(colInfoIx); + } + } + } + /** + * merges two column info records (if they are adjacent and have the same formatting, etc) + * @return <code>false</code> if the two column records could not be merged + */ + private static boolean mergeColInfoRecords(ColumnInfoRecord ciA, ColumnInfoRecord ciB) { + if (ciA.isAdjacentBefore(ciB) && ciA.formatMatches(ciB)) { + ciA.setLastColumn(ciB.getLastColumn()); + return true; + } + return false; + } + /** + * Creates an outline group for the specified columns, by setting the level + * field for each col info record in the range. {@link ColumnInfoRecord}s + * may be created, split or merged as a result of this operation. + * + * @param fromColumnIx + * group from this column (inclusive) + * @param toColumnIx + * group to this column (inclusive) + * @param indent + * if <code>true</code> the group will be indented by one + * level, if <code>false</code> indenting will be decreased by + * one level. + */ + public void groupColumnRange(int fromColumnIx, int toColumnIx, boolean indent) { + + int colInfoSearchStartIdx = 0; // optimization to speed up the search for col infos + for (int i = fromColumnIx; i <= toColumnIx; i++) { + int level = 1; + int colInfoIdx = findColInfoIdx(i, colInfoSearchStartIdx); + if (colInfoIdx != -1) { + level = getColInfo(colInfoIdx).getOutlineLevel(); + if (indent) { + level++; + } else { + level--; + } + level = Math.max(0, level); + level = Math.min(7, level); + colInfoSearchStartIdx = Math.max(0, colInfoIdx - 1); // -1 just in case this column is collapsed later. + } + setColumn(i, null, null, new Integer(level), null, null); + } + } + /** + * Finds the <tt>ColumnInfoRecord</tt> which contains the specified columnIndex + * @param columnIndex index of the column (not the index of the ColumnInfoRecord) + * @return <code>null</code> if no column info found for the specified column + */ public ColumnInfoRecord findColumnInfo(int columnIndex) { int nInfos = records.size(); for(int i=0; i< nInfos; i++) { ColumnInfoRecord ci = getColInfo(i); - if (ci.getFirstColumn() <= columnIndex && columnIndex <= ci.getLastColumn()) { + if (ci.containsColumn(columnIndex)) { return ci; } } return null; } public int getMaxOutlineLevel() { - int result = 0; - int count=records.size(); - for (int i=0; i<count; i++) { - ColumnInfoRecord columnInfoRecord = getColInfo(i); - result = Math.max(columnInfoRecord.getOutlineLevel(), result); - } - return result; + int result = 0; + int count=records.size(); + for (int i=0; i<count; i++) { + ColumnInfoRecord columnInfoRecord = getColInfo(i); + result = Math.max(columnInfoRecord.getOutlineLevel(), result); + } + return result; } - - } diff --git a/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java index 68d5f453dc..06bb53a38d 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java @@ -20,6 +20,7 @@ package org.apache.poi.hssf.record.aggregates; import org.apache.poi.hssf.record.CellValueRecordInterface; import org.apache.poi.hssf.record.FormulaRecord; import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.RecordFormatException; import org.apache.poi.hssf.record.StringRecord; /** @@ -34,9 +35,9 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel private SharedValueManager _sharedValueManager; /** caches the calculated result of the formula */ private StringRecord _stringRecord; - + /** - * @param stringRec may be <code>null</code> if this formula does not have a cached text + * @param stringRec may be <code>null</code> if this formula does not have a cached text * value. * @param svm the {@link SharedValueManager} for the current sheet */ @@ -44,6 +45,14 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel if (svm == null) { throw new IllegalArgumentException("sfm must not be null"); } + boolean hasStringRec = stringRec != null; + boolean hasCachedStringFlag = formulaRec.hasCachedResultString(); + if (hasStringRec != hasCachedStringFlag) { + throw new RecordFormatException("String record was " + + (hasStringRec ? "": "not ") + " supplied but formula record flag is " + + (hasCachedStringFlag ? "" : "not ") + " set"); + } + if (formulaRec.isSharedFormula()) { svm.convertSharedFormulaRecord(formulaRec); } @@ -52,18 +61,18 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel _stringRecord = stringRec; } - public void setStringRecord(StringRecord stringRecord) { - _stringRecord = stringRecord; - } - public FormulaRecord getFormulaRecord() { return _formulaRecord; } + /** + * debug only + * TODO - encapsulate + */ public StringRecord getStringRecord() { return _stringRecord; } - + public short getXFIndex() { return _formulaRecord.getXFIndex(); } @@ -91,7 +100,7 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel public String toString() { return _formulaRecord.toString(); } - + public void visitContainedRecords(RecordVisitor rv) { rv.visitRecord(_formulaRecord); Record sharedFormulaRecord = _sharedValueManager.getRecordForFirstCell(_formulaRecord); @@ -102,11 +111,33 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel rv.visitRecord(_stringRecord); } } - + public String getStringValue() { if(_stringRecord==null) { return null; } return _stringRecord.getString(); } + + public void setCachedStringResult(String value) { + + // Save the string into a String Record, creating one if required + if(_stringRecord == null) { + _stringRecord = new StringRecord(); + } + _stringRecord.setString(value); + if (value.length() < 1) { + _formulaRecord.setCachedResultTypeEmptyString(); + } else { + _formulaRecord.setCachedResultTypeString(); + } + } + public void setCachedBooleanResult(boolean value) { + _stringRecord = null; + _formulaRecord.setCachedResultBoolean(value); + } + public void setCachedErrorResult(int errorCode) { + _stringRecord = null; + _formulaRecord.setCachedResultErrorCode(errorCode); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java index 2193854f47..68ae5b48c1 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java @@ -18,7 +18,7 @@ package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.LittleEndian;
/**
@@ -45,7 +45,7 @@ public abstract class Area2DPtgBase extends AreaPtgBase { public final int getSize() {
return SIZE;
}
- public final String toFormulaString(HSSFWorkbook book) {
+ public final String toFormulaString(Workbook book) {
return formatReferenceAsString();
}
public final String toString() {
diff --git a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java index 49994c0597..48acaac205 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java @@ -44,8 +44,11 @@ public final class ArrayPtg extends Ptg { * (not including the data which comes after all formula tokens) */ public static final int PLAIN_TOKEN_SIZE = 1+RESERVED_FIELD_LEN; + + private static final byte[] DEFAULT_RESERVED_DATA = new byte[RESERVED_FIELD_LEN]; + // TODO - fix up field visibility and subclasses - private byte[] field_1_reserved; + private final byte[] field_1_reserved; // data from these fields comes after the Ptg data of all tokens in current formula private short token_1_columns; @@ -59,8 +62,42 @@ public final class ArrayPtg extends Ptg { field_1_reserved[i] = in.readByte(); } } - public Object[] getTokenArrayValues() { - return (Object[]) token_3_arrayValues.clone(); + /** + * @param values2d array values arranged in rows + */ + public ArrayPtg(Object[][] values2d) { + int nColumns = values2d[0].length; + int nRows = values2d.length; + // convert 2-d to 1-d array (row by row according to getValueIndex()) + token_1_columns = (short) nColumns; + token_2_rows = (short) nRows; + + Object[] vv = new Object[token_1_columns * token_2_rows]; + for (int r=0; r<nRows; r++) { + Object[] rowData = values2d[r]; + for (int c=0; c<nColumns; c++) { + vv[getValueIndex(c, r)] = rowData[c]; + } + } + + token_3_arrayValues = vv; + field_1_reserved = DEFAULT_RESERVED_DATA; + } + /** + * @return 2-d array (inner index is rowIx, outer index is colIx) + */ + public Object[][] getTokenArrayValues() { + if (token_3_arrayValues == null) { + throw new IllegalStateException("array values not read yet"); + } + Object[][] result = new Object[token_2_rows][token_1_columns]; + for (int r = 0; r < token_2_rows; r++) { + Object[] rowData = result[r]; + for (int c = 0; c < token_1_columns; c++) { + rowData[c] = token_3_arrayValues[getValueIndex(c, r)]; + } + } + return result; } public boolean isBaseToken() { @@ -88,27 +125,21 @@ public final class ArrayPtg extends Ptg { token_3_arrayValues = ConstantValueParser.parse(in, totalCount); } - public String toString() - { - StringBuffer buffer = new StringBuffer("[ArrayPtg]\n"); + public String toString() { + StringBuffer sb = new StringBuffer("[ArrayPtg]\n"); - buffer.append("columns = ").append(getColumnCount()).append("\n"); - buffer.append("rows = ").append(getRowCount()).append("\n"); + sb.append("nRows = ").append(getRowCount()).append("\n"); + sb.append("nCols = ").append(getColumnCount()).append("\n"); if (token_3_arrayValues == null) { - buffer.append(" #values#uninitialised#\n"); + sb.append(" #values#uninitialised#\n"); } else { - for (int x=0;x<getColumnCount();x++) { - for (int y=0;y<getRowCount();y++) { - Object o = token_3_arrayValues[getValueIndex(x, y)]; - buffer.append("[").append(x).append("][").append(y).append("] = ").append(o).append("\n"); - } - } + sb.append(" ").append(formatAsString()); } - return buffer.toString(); + return sb.toString(); } /** - * Note - (2D) array elements are stored column by column + * Note - (2D) array elements are stored row by row * @return the index into the internal 1D array for the specified column and row */ /* package */ int getValueIndex(int colIx, int rowIx) { @@ -120,7 +151,7 @@ public final class ArrayPtg extends Ptg { throw new IllegalArgumentException("Specified rowIx (" + rowIx + ") is outside the allowed range (0.." + (token_2_rows-1) + ")"); } - return rowIx + token_2_rows * colIx; + return rowIx * token_1_columns + colIx; } public void writeBytes(byte[] data, int offset) { @@ -153,16 +184,15 @@ public final class ArrayPtg extends Ptg { + ConstantValueParser.getEncodedSize(token_3_arrayValues); } - public String toFormulaString(Workbook book) - { + public String formatAsString() { StringBuffer b = new StringBuffer(); b.append("{"); - for (int x=0;x<getColumnCount();x++) { - if (x > 0) { + for (int y=0;y<getRowCount();y++) { + if (y > 0) { b.append(";"); } - for (int y=0;y<getRowCount();y++) { - if (y > 0) { + for (int x=0;x<getColumnCount();x++) { + if (x > 0) { b.append(","); } Object o = token_3_arrayValues[getValueIndex(x, y)]; @@ -172,11 +202,14 @@ public final class ArrayPtg extends Ptg { b.append("}"); return b.toString(); } + public String toFormulaString(Workbook book) { + return formatAsString(); + } private static String getConstantText(Object o) { if (o == null) { - return ""; // TODO - how is 'empty value' represented in formulas? + throw new RuntimeException("Array item cannot be null"); } if (o instanceof UnicodeString) { return "\"" + ((UnicodeString)o).getString() + "\""; @@ -196,11 +229,4 @@ public final class ArrayPtg extends Ptg { public byte getDefaultOperandClass() { return Ptg.CLASS_ARRAY; } - - public Object clone() { - ArrayPtg ptg = (ArrayPtg) super.clone(); - ptg.field_1_reserved = (byte[]) field_1_reserved.clone(); - ptg.token_3_arrayValues = (Object[]) token_3_arrayValues.clone(); - return ptg; - } } diff --git a/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java b/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java index aef580d5e4..d94f2a3807 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ErrPtg.java @@ -24,12 +24,12 @@ import org.apache.poi.hssf.usermodel.HSSFErrorConstants; * @author Daniel Noll (daniel at nuix dot com dot au) */ public final class ErrPtg extends ScalarConstantPtg { - + // convenient access to namespace private static final HSSFErrorConstants EC = null; - + /** <b>#NULL!</b> - Intersection of two cell ranges is empty */ - public static final ErrPtg NULL_INTERSECTION = new ErrPtg(EC.ERROR_NULL); + public static final ErrPtg NULL_INTERSECTION = new ErrPtg(EC.ERROR_NULL); /** <b>#DIV/0!</b> - Division by zero */ public static final ErrPtg DIV_ZERO = new ErrPtg(EC.ERROR_DIV_0); /** <b>#VALUE!</b> - Wrong type of operand */ @@ -37,28 +37,28 @@ public final class ErrPtg extends ScalarConstantPtg { /** <b>#REF!</b> - Illegal or deleted cell reference */ public static final ErrPtg REF_INVALID = new ErrPtg(EC.ERROR_REF); /** <b>#NAME?</b> - Wrong function or range name */ - public static final ErrPtg NAME_INVALID = new ErrPtg(EC.ERROR_NAME); + public static final ErrPtg NAME_INVALID = new ErrPtg(EC.ERROR_NAME); /** <b>#NUM!</b> - Value range overflow */ public static final ErrPtg NUM_ERROR = new ErrPtg(EC.ERROR_NUM); /** <b>#N/A</b> - Argument or function not available */ public static final ErrPtg N_A = new ErrPtg(EC.ERROR_NA); - - + + public static final short sid = 0x1c; private static final int SIZE = 2; private final int field_1_error_code; /** Creates new ErrPtg */ - public ErrPtg(int errorCode) { + private ErrPtg(int errorCode) { if(!HSSFErrorConstants.isValidCode(errorCode)) { throw new IllegalArgumentException("Invalid error code (" + errorCode + ")"); } field_1_error_code = errorCode; } - - public ErrPtg(RecordInputStream in) { - this(in.readByte()); + + public static ErrPtg read(RecordInputStream in) { + return valueOf(in.readByte()); } public void writeBytes(byte [] array, int offset) @@ -78,4 +78,17 @@ public final class ErrPtg extends ScalarConstantPtg { public int getErrorCode() { return field_1_error_code; } + + public static ErrPtg valueOf(int code) { + switch(code) { + case HSSFErrorConstants.ERROR_DIV_0: return DIV_ZERO; + case HSSFErrorConstants.ERROR_NA: return N_A; + case HSSFErrorConstants.ERROR_NAME: return NAME_INVALID; + case HSSFErrorConstants.ERROR_NULL: return NULL_INTERSECTION; + case HSSFErrorConstants.ERROR_NUM: return NUM_ERROR; + case HSSFErrorConstants.ERROR_REF: return REF_INVALID; + case HSSFErrorConstants.ERROR_VALUE: return VALUE_INVALID; + } + throw new RuntimeException("Unexpected error code (" + code + ")"); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/Ptg.java b/src/java/org/apache/poi/hssf/record/formula/Ptg.java index 296b691cda..630803c3cb 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Ptg.java +++ b/src/java/org/apache/poi/hssf/record/formula/Ptg.java @@ -56,20 +56,21 @@ public abstract class Ptg implements Cloneable { /** * @deprecated - use readTokens() */ - public static Stack createParsedExpressionTokens(short size, RecordInputStream in) - { + public static Stack createParsedExpressionTokens(short size, RecordInputStream in) { Stack stack = new Stack(); int pos = 0; List arrayPtgs = null; - while ( pos < size ) - { + while (pos < size) { Ptg ptg = Ptg.createPtg( in ); if (ptg instanceof ArrayPtg) { - if (arrayPtgs == null) + if (arrayPtgs == null) { arrayPtgs = new ArrayList(5); + } arrayPtgs.add(ptg); - pos += 8; - } else pos += ptg.getSize(); + pos += ArrayPtg.PLAIN_TOKEN_SIZE; + } else { + pos += ptg.getSize(); + } stack.push( ptg ); } if(pos != size) { @@ -109,25 +110,25 @@ public abstract class Ptg implements Cloneable { int baseId = id & 0x1F | 0x20; switch (baseId) { - case ArrayPtg.sid: return new ArrayPtg(in); // 0x20, 0x40, 0x60 - case FuncPtg.sid: return new FuncPtg(in); // 0x21, 0x41, 0x61 - case FuncVarPtg.sid: return new FuncVarPtg(in); // 0x22, 0x42, 0x62 - case NamePtg.sid: return new NamePtg(in); // 0x23, 0x43, 0x63 - case RefPtg.sid: return new RefPtg(in); // 0x24, 0x44, 0x64 - case AreaPtg.sid: return new AreaPtg(in); // 0x25, 0x45, 0x65 - case MemAreaPtg.sid: return new MemAreaPtg(in); // 0x26, 0x46, 0x66 - case MemErrPtg.sid: return new MemErrPtg(in); // 0x27, 0x47, 0x67 - case MemFuncPtg.sid: return new MemFuncPtg(in); // 0x29, 0x49, 0x69 - case RefErrorPtg.sid: return new RefErrorPtg(in);// 0x2a, 0x4a, 0x6a - case AreaErrPtg.sid: return new AreaErrPtg(in); // 0x2b, 0x4b, 0x6b - case RefNPtg.sid: return new RefNPtg(in); // 0x2c, 0x4c, 0x6c - case AreaNPtg.sid: return new AreaNPtg(in); // 0x2d, 0x4d, 0x6d + case ArrayPtg.sid: return new ArrayPtg(in); // 0x20, 0x40, 0x60 + case FuncPtg.sid: return new FuncPtg(in); // 0x21, 0x41, 0x61 + case FuncVarPtg.sid: return new FuncVarPtg(in); // 0x22, 0x42, 0x62 + case NamePtg.sid: return new NamePtg(in); // 0x23, 0x43, 0x63 + case RefPtg.sid: return new RefPtg(in); // 0x24, 0x44, 0x64 + case AreaPtg.sid: return new AreaPtg(in); // 0x25, 0x45, 0x65 + case MemAreaPtg.sid: return new MemAreaPtg(in); // 0x26, 0x46, 0x66 + case MemErrPtg.sid: return new MemErrPtg(in); // 0x27, 0x47, 0x67 + case MemFuncPtg.sid: return new MemFuncPtg(in); // 0x29, 0x49, 0x69 + case RefErrorPtg.sid: return new RefErrorPtg(in); // 0x2a, 0x4a, 0x6a + case AreaErrPtg.sid: return new AreaErrPtg(in); // 0x2b, 0x4b, 0x6b + case RefNPtg.sid: return new RefNPtg(in); // 0x2c, 0x4c, 0x6c + case AreaNPtg.sid: return new AreaNPtg(in); // 0x2d, 0x4d, 0x6d - case NameXPtg.sid: return new NameXPtg(in); // 0x39, 0x49, 0x79 - case Ref3DPtg.sid: return new Ref3DPtg(in); // 0x3a, 0x5a, 0x7a - case Area3DPtg.sid: return new Area3DPtg(in); // 0x3b, 0x5b, 0x7b - case DeletedRef3DPtg.sid: return new DeletedRef3DPtg(in); // 0x3c, 0x5c, 0x7c - case DeletedArea3DPtg.sid: return new DeletedArea3DPtg(in); // 0x3d, 0x5d, 0x7d + case NameXPtg.sid: return new NameXPtg(in); // 0x39, 0x49, 0x79 + case Ref3DPtg.sid: return new Ref3DPtg(in); // 0x3a, 0x5a, 0x7a + case Area3DPtg.sid: return new Area3DPtg(in); // 0x3b, 0x5b, 0x7b + case DeletedRef3DPtg.sid: return new DeletedRef3DPtg(in); // 0x3c, 0x5c, 0x7c + case DeletedArea3DPtg.sid: return new DeletedArea3DPtg(in); // 0x3d, 0x5d, 0x7d } throw new UnsupportedOperationException(" Unknown Ptg in Formula: 0x"+ Integer.toHexString(id) + " (" + ( int ) id + ")"); @@ -162,7 +163,7 @@ public abstract class Ptg implements Cloneable { case StringPtg.sid: return new StringPtg(in); // 0x17 case AttrPtg.sid: case 0x1a: return new AttrPtg(in); // 0x19 - case ErrPtg.sid: return new ErrPtg(in); // 0x1c + case ErrPtg.sid: return ErrPtg.read(in); // 0x1c case BoolPtg.sid: return new BoolPtg(in); // 0x1d case IntPtg.sid: return new IntPtg(in); // 0x1e case NumberPtg.sid: return new NumberPtg(in); // 0x1f @@ -262,11 +263,14 @@ public abstract class Ptg implements Cloneable { ptg.writeBytes(array, pos + offset); if (ptg instanceof ArrayPtg) { - if (arrayPtgs == null) - arrayPtgs = new ArrayList(5); - arrayPtgs.add(ptg); - pos += 8; - } else pos += ptg.getSize(); + if (arrayPtgs == null) { + arrayPtgs = new ArrayList(5); + } + arrayPtgs.add(ptg); + pos += ArrayPtg.PLAIN_TOKEN_SIZE; + } else { + pos += ptg.getSize(); + } } if (arrayPtgs != null) { for (int i=0;i<arrayPtgs.size();i++) { diff --git a/src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java b/src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java index 87a6462b7d..efeb5c484b 100644 --- a/src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java +++ b/src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java @@ -24,15 +24,14 @@ import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; -import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; public final class AnalysisToolPak { private static final FreeRefFunction NotImplemented = new FreeRefFunction() { - public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, - Workbook workbook, Sheet sheet) { + public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, + int srcCellRow, int srcCellCol) { return ErrorEval.FUNCTION_NOT_IMPLEMENTED; } }; diff --git a/src/java/org/apache/poi/hssf/record/formula/atp/ParityFunction.java b/src/java/org/apache/poi/hssf/record/formula/atp/ParityFunction.java index 71cfa03591..b76ca96019 100644 --- a/src/java/org/apache/poi/hssf/record/formula/atp/ParityFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/atp/ParityFunction.java @@ -42,8 +42,8 @@ final class ParityFunction implements FreeRefFunction { _desiredParity = desiredParity; } - public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, - Sheet sheet) { + public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow, + int srcCellCol) { if (args.length != 1) { return ErrorEval.VALUE_INVALID; } @@ -58,8 +58,8 @@ final class ParityFunction implements FreeRefFunction { return BoolEval.valueOf(val == _desiredParity); } - private static int evaluateArgParity(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { - ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + private static int evaluateArgParity(Eval arg, int srcCellRow, int srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short)srcCellCol); double d = OperandResolver.coerceValueToDouble(ve); if (d < 0) { diff --git a/src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java b/src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java index f5bc3fe8f4..59c6aea89d 100644 --- a/src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java +++ b/src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java @@ -61,8 +61,8 @@ final class YearFrac implements FreeRefFunction { // enforce singleton } - public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, - Sheet sheet) { + public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow, + int srcCellCol) { double result; try { @@ -85,8 +85,8 @@ final class YearFrac implements FreeRefFunction { return new NumberEval(result); } - private static double evaluateDateArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { - ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + private static double evaluateDateArg(Eval arg, int srcCellRow, int srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short) srcCellCol); if (ve instanceof StringEval) { String strVal = ((StringEval) ve).getStringValue(); @@ -155,8 +155,8 @@ final class YearFrac implements FreeRefFunction { return cal; } - private static int evaluateIntArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { - ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + private static int evaluateIntArg(Eval arg, int srcCellRow, int srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, (short) srcCellCol); return OperandResolver.coerceValueToInt(ve); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/ConcatEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/ConcatEval.java index e54cd483f1..6755cfafef 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/ConcatEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/ConcatEval.java @@ -1,19 +1,19 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ package org.apache.poi.hssf.record.formula.eval; @@ -24,7 +24,7 @@ import org.apache.poi.hssf.record.formula.Ptg; * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ -public final class ConcatEval extends StringOperationEval { +public final class ConcatEval implements OperationEval { private ConcatPtg delegate; @@ -37,20 +37,23 @@ public final class ConcatEval extends StringOperationEval { return ErrorEval.VALUE_INVALID; } StringBuffer sb = new StringBuffer(); - for (int i = 0; i < 2; i++) { - - ValueEval ve = singleOperandEvaluate(args[i], srcRow, srcCol); - if (ve instanceof StringValueEval) { - StringValueEval sve = (StringValueEval) ve; - sb.append(sve.getStringValue()); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { // must be an error eval - return ve; - } - } + try { + for (int i = 0; i < 2; i++) { + + ValueEval ve = OperandResolver.getSingleValue(args[i], srcRow, srcCol); + if (ve instanceof StringValueEval) { + StringValueEval sve = (StringValueEval) ve; + sb.append(sve.getStringValue()); + } else if (ve == BlankEval.INSTANCE) { + // do nothing + } else { // must be an error eval + throw new RuntimeException("Unexpected value type (" + + ve.getClass().getName() + ")"); + } + } + } catch (EvaluationException e) { + return e.getErrorEval(); + } return new StringEval(sb.toString()); } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java b/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java index 95dd0b545b..3782c6c4de 100755 --- a/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java @@ -19,7 +19,6 @@ package org.apache.poi.hssf.record.formula.eval; import org.apache.poi.hssf.record.formula.atp.AnalysisToolPak; import org.apache.poi.hssf.record.formula.functions.FreeRefFunction; -import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; /** * @@ -31,7 +30,8 @@ import org.apache.poi.ss.usermodel.Workbook; */ final class ExternalFunction implements FreeRefFunction { - public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) { + public ValueEval evaluate(Eval[] args, Workbook workbook, + int srcCellSheet, int srcCellRow,int srcCellCol) { int nIncomingArgs = args.length; if(nIncomingArgs < 1) { @@ -55,7 +55,7 @@ final class ExternalFunction implements FreeRefFunction { int nOutGoingArgs = nIncomingArgs -1; Eval[] outGoingArgs = new Eval[nOutGoingArgs]; System.arraycopy(args, 1, outGoingArgs, 0, nOutGoingArgs); - return targetFunc.evaluate(outGoingArgs, srcCellRow, srcCellCol, workbook, sheet); + return targetFunc.evaluate(outGoingArgs, workbook, srcCellSheet, srcCellRow, srcCellCol); } private FreeRefFunction findExternalUserDefinedFunction(Workbook workbook, diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java index a1c7356fa9..e68b8ee67d 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java @@ -1,23 +1,20 @@ -/* -* 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 - * - */ +/* ==================================================================== + 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.HashMap; @@ -81,42 +78,42 @@ public abstract class FunctionEval implements OperationEval { retval[1] = new If(); // IF retval[2] = new IsNa(); // ISNA retval[3] = new IsError(); // ISERROR - retval[4] = new Sum(); // SUM - retval[5] = new Average(); // AVERAGE - retval[6] = new Min(); // MIN - retval[7] = new Max(); // MAX + retval[4] = AggregateFunction.SUM; + retval[5] = AggregateFunction.AVERAGE; + retval[6] = AggregateFunction.MIN; + retval[7] = AggregateFunction.MAX; retval[8] = new Row(); // ROW retval[9] = new Column(); // COLUMN retval[10] = new Na(); // NA retval[11] = new Npv(); // NPV - retval[12] = new Stdev(); // STDEV - retval[13] = NumericFunctionOneArg.DOLLAR; + retval[12] = AggregateFunction.STDEV; + retval[13] = NumericFunction.DOLLAR; retval[14] = new Fixed(); // FIXED - retval[15] = NumericFunctionOneArg.SIN; - retval[16] = NumericFunctionOneArg.COS; - retval[17] = NumericFunctionOneArg.TAN; - retval[18] = NumericFunctionOneArg.ATAN; + retval[15] = NumericFunction.SIN; + retval[16] = NumericFunction.COS; + retval[17] = NumericFunction.TAN; + retval[18] = NumericFunction.ATAN; retval[19] = new Pi(); // PI - retval[20] = NumericFunctionOneArg.SQRT; - retval[21] = NumericFunctionOneArg.EXP; - retval[22] = NumericFunctionOneArg.LN; - retval[23] = NumericFunctionOneArg.LOG10; - retval[24] = NumericFunctionOneArg.ABS; - retval[25] = NumericFunctionOneArg.INT; - retval[26] = NumericFunctionOneArg.SIGN; - retval[27] = new Round(); // ROUND + 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(); // LOOKUP retval[29] = new Index(); // INDEX retval[30] = new Rept(); // REPT - retval[31] = new Mid(); // MID - retval[32] = new Len(); // LEN + retval[31] = TextFunction.MID; + retval[32] = TextFunction.LEN; retval[33] = new Value(); // VALUE retval[34] = new True(); // TRUE retval[35] = new False(); // FALSE retval[36] = new And(); // AND retval[37] = new Or(); // OR retval[38] = new Not(); // NOT - retval[39] = new Mod(); // MOD + retval[39] = NumericFunction.MOD; retval[40] = new Dcount(); // DCOUNT retval[41] = new Dsum(); // DSUM retval[42] = new Daverage(); // DAVERAGE @@ -132,16 +129,16 @@ public abstract class FunctionEval implements OperationEval { retval[52] = new Growth(); // GROWTH retval[53] = new Goto(); // GOTO retval[54] = new Halt(); // HALT - retval[56] = new Pv(); // PV - retval[57] = new Fv(); // FV - retval[58] = new Nper(); // NPER - retval[59] = new Pmt(); // PMT + retval[56] = FinanceFunction.PV; + retval[57] = FinanceFunction.FV; + retval[58] = FinanceFunction.NPER; + retval[59] = FinanceFunction.PMT; retval[60] = new Rate(); // RATE retval[61] = new Mirr(); // MIRR retval[62] = new Irr(); // IRR retval[63] = new Rand(); // RAND retval[64] = new Match(); // MATCH - retval[65] = new Date(); // DATE + retval[65] = DateFunc.instance; // DATE retval[66] = new Time(); // TIME retval[67] = CalendarFieldFunction.DAY; // DAY retval[68] = CalendarFieldFunction.MONTH; // MONTH @@ -173,9 +170,9 @@ public abstract class FunctionEval implements OperationEval { retval[94] = new Activecell(); // ACTIVECELL retval[95] = new NotImplementedFunction(); // SELECTION retval[96] = new Result(); // RESULT - retval[97] = new Atan2(); // ATAN2 - retval[98] = NumericFunctionOneArg.ASIN; - retval[99] = NumericFunctionOneArg.ACOS; + retval[97] = NumericFunction.ATAN2; + retval[98] = NumericFunction.ASIN; + retval[99] = NumericFunction.ACOS; retval[100] = new Choose(); // CHOOSE retval[101] = new Hlookup(); // HLOOKUP retval[102] = new Vlookup(); // VLOOKUP @@ -185,16 +182,16 @@ public abstract class FunctionEval implements OperationEval { retval[106] = new NotImplementedFunction(); // GETFORMULA retval[107] = new NotImplementedFunction(); // GETNAME retval[108] = new Setvalue(); // SETVALUE - retval[109] = new Log(); // LOG + retval[109] = NumericFunction.LOG; retval[110] = new Exec(); // EXEC retval[111] = new Char(); // CHAR - retval[112] = new Lower(); // LOWER - retval[113] = new Upper(); // UPPER + retval[112] = TextFunction.LOWER; + retval[113] = TextFunction.UPPER; retval[114] = new Proper(); // PROPER - retval[115] = new Left(); // LEFT - retval[116] = new Right(); // RIGHT - retval[117] = new Exact(); // EXACT - retval[118] = new Trim(); // TRIM + retval[115] = TextFunction.LEFT; + retval[116] = TextFunction.RIGHT; + retval[117] = TextFunction.EXACT; + retval[118] = TextFunction.TRIM; retval[119] = new Replace(); // REPLACE retval[120] = new Substitute(); // SUBSTITUTE retval[121] = new Code(); // CODE @@ -255,8 +252,8 @@ public abstract class FunctionEval implements OperationEval { retval[180] = new NotImplementedFunction(); // RESTART retval[181] = new Help(); // HELP retval[182] = new NotImplementedFunction(); // GETBAR - retval[183] = new Product(); // PRODUCT - retval[184] = NumericFunctionOneArg.FACT; + retval[183] = AggregateFunction.PRODUCT; + retval[184] = NumericFunction.FACT; retval[185] = new NotImplementedFunction(); // GETCELL retval[186] = new NotImplementedFunction(); // GETWORKSPACE retval[187] = new NotImplementedFunction(); // GETWINDOW @@ -282,8 +279,8 @@ public abstract class FunctionEval implements OperationEval { retval[209] = new Rightb(); // RIGHTB retval[210] = new Midb(); // MIDB retval[211] = new Lenb(); // LENB - retval[212] = new Roundup(); // ROUNDUP - retval[213] = new Rounddown(); // ROUNDDOWN + retval[212] = NumericFunction.ROUNDUP; + retval[213] = NumericFunction.ROUNDDOWN; retval[214] = new Asc(); // ASC retval[215] = new Dbcs(); // DBCS retval[216] = new Rank(); // RANK @@ -291,14 +288,14 @@ public abstract class FunctionEval implements OperationEval { retval[220] = new Days360(); // DAYS360 retval[221] = new Today(); // TODAY retval[222] = new Vdb(); // VDB - retval[227] = new Median(); // MEDIAN + retval[227] = AggregateFunction.MEDIAN; retval[228] = new Sumproduct(); // SUMPRODUCT - retval[229] = NumericFunctionOneArg.SINH; - retval[230] = NumericFunctionOneArg.COSH; - retval[231] = NumericFunctionOneArg.TANH; - retval[232] = NumericFunctionOneArg.ASINH; - retval[233] = NumericFunctionOneArg.ACOSH; - retval[234] = NumericFunctionOneArg.ATANH; + 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[235] = new Dget(); // DGET retval[236] = new NotImplementedFunction(); // CREATEOBJECT retval[237] = new Volatile(); // VOLATILE @@ -331,14 +328,14 @@ public abstract class FunctionEval implements OperationEval { retval[266] = new NotImplementedFunction(); // PRESSTOOL retval[267] = new NotImplementedFunction(); // REGISTERID retval[268] = new NotImplementedFunction(); // GETWORKBOOK - retval[269] = new Avedev(); // AVEDEV + retval[269] = AggregateFunction.AVEDEV; retval[270] = new Betadist(); // BETADIST retval[271] = new Gammaln(); // GAMMALN retval[272] = new Betainv(); // BETAINV retval[273] = new Binomdist(); // BINOMDIST retval[274] = new Chidist(); // CHIDIST retval[275] = new Chiinv(); // CHIINV - retval[276] = new Combin(); // COMBIN + retval[276] = NumericFunction.COMBIN; retval[277] = new Confidence(); // CONFIDENCE retval[278] = new Critbinom(); // CRITBINOM retval[279] = new Even(); // EVEN @@ -347,10 +344,10 @@ public abstract class FunctionEval implements OperationEval { retval[282] = new Finv(); // FINV retval[283] = new Fisher(); // FISHER retval[284] = new Fisherinv(); // FISHERINV - retval[285] = new Floor(); // FLOOR + retval[285] = NumericFunction.FLOOR; retval[286] = new Gammadist(); // GAMMADIST retval[287] = new Gammainv(); // GAMMAINV - retval[288] = new Ceiling(); // CEILING + retval[288] = NumericFunction.CEILING; retval[289] = new Hypgeomdist(); // HYPGEOMDIST retval[290] = new Lognormdist(); // LOGNORMDIST retval[291] = new Loginv(); // LOGINV @@ -380,15 +377,15 @@ public abstract class FunctionEval implements OperationEval { retval[315] = new Slope(); // SLOPE retval[316] = new Ttest(); // TTEST retval[317] = new Prob(); // PROB - retval[318] = new Devsq(); // DEVSQ + retval[318] = AggregateFunction.DEVSQ; retval[319] = new Geomean(); // GEOMEAN retval[320] = new Harmean(); // HARMEAN - retval[321] = new Sumsq(); // SUMSQ + retval[321] = AggregateFunction.SUMSQ; retval[322] = new Kurt(); // KURT retval[323] = new Skew(); // SKEW retval[324] = new Ztest(); // ZTEST - retval[325] = new Large(); // LARGE - retval[326] = new Small(); // SMALL + retval[325] = AggregateFunction.LARGE; + retval[326] = AggregateFunction.SMALL; retval[327] = new Quartile(); // QUARTILE retval[328] = new Percentile(); // PERCENTILE retval[329] = new Percentrank(); // PERCENTRANK @@ -397,14 +394,14 @@ public abstract class FunctionEval implements OperationEval { retval[332] = new Tinv(); // TINV retval[334] = new NotImplementedFunction(); // MOVIECOMMAND retval[335] = new NotImplementedFunction(); // GETMOVIE - retval[336] = new Concatenate(); // CONCATENATE - retval[337] = new Power(); // POWER + retval[336] = TextFunction.CONCATENATE; + retval[337] = NumericFunction.POWER; retval[338] = new NotImplementedFunction(); // PIVOTADDDATA retval[339] = new NotImplementedFunction(); // GETPIVOTTABLE retval[340] = new NotImplementedFunction(); // GETPIVOTFIELD retval[341] = new NotImplementedFunction(); // GETPIVOTITEM - retval[342] = NumericFunctionOneArg.RADIANS; - retval[343] = NumericFunctionOneArg.DEGREES; + retval[342] = NumericFunction.RADIANS; + retval[343] = NumericFunction.DEGREES; retval[344] = new Subtotal(); // SUBTOTAL retval[345] = new Sumif(); // SUMIF retval[346] = new Countif(); // COUNTIF @@ -423,8 +420,8 @@ public abstract class FunctionEval implements OperationEval { retval[359] = new Hyperlink(); // HYPERLINK retval[360] = new NotImplementedFunction(); // PHONETIC retval[361] = new Averagea(); // AVERAGEA - retval[362] = new Maxa(); // MAXA - retval[363] = new Mina(); // MINA + retval[362] = MinaMaxa.MAXA; + retval[363] = MinaMaxa.MINA; retval[364] = new Stdevpa(); // STDEVPA retval[365] = new Varpa(); // VARPA retval[366] = new Stdeva(); // STDEVA diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java b/src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java index 87f45236bf..09be70477f 100755 --- a/src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java +++ b/src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java @@ -272,12 +272,7 @@ public final class OperandResolver { StringValueEval sve = (StringValueEval) ve; return sve.getStringValue(); } - if (ve instanceof NumberEval) { - NumberEval neval = (NumberEval) ve; - return neval.getStringValue(); - } - - if (ve instanceof BlankEval) { + if (ve == BlankEval.INSTANCE) { return ""; } throw new IllegalArgumentException("Unexpected eval class (" + ve.getClass().getName() + ")"); @@ -289,7 +284,7 @@ public final class OperandResolver { */ public static Boolean coerceValueToBoolean(ValueEval ve, boolean stringsAreBlanks) throws EvaluationException { - if (ve == null || ve instanceof BlankEval) { + if (ve == null || ve == BlankEval.INSTANCE) { // TODO - remove 've == null' condition once AreaEval is fixed return null; } @@ -297,7 +292,7 @@ public final class OperandResolver { return Boolean.valueOf(((BoolEval) ve).getBooleanValue()); } - if (ve instanceof BlankEval) { + if (ve == BlankEval.INSTANCE) { return null; } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java b/src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java deleted file mode 100644 index e4bb47a43d..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java +++ /dev/null @@ -1,53 +0,0 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package org.apache.poi.hssf.record.formula.eval; - -import org.apache.poi.hssf.record.formula.RefPtg; - -/** - * @author adeshmukh - * - */ -public final class Ref2DEval implements RefEval { - - private final ValueEval value; - private final RefPtg delegate; - - public Ref2DEval(RefPtg ptg, ValueEval ve) { - if(ve == null) { - throw new IllegalArgumentException("ve must not be null"); - } - if(false && ptg == null) { // TODO - fix dodgy code in MultiOperandNumericFunction - throw new IllegalArgumentException("ptg must not be null"); - } - value = ve; - delegate = ptg; - } - public ValueEval getInnerValueEval() { - return value; - } - public int getRow() { - return delegate.getRow(); - } - public int getColumn() { - return delegate.getColumn(); - } - public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) { - throw new RuntimeException("should not be called"); // TODO - delete this whole class - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java b/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java deleted file mode 100644 index 46aa0ddf69..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java +++ /dev/null @@ -1,179 +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 14, 2005 - * - */ -package org.apache.poi.hssf.record.formula.eval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public final class ValueEvalToNumericXlator { - - public static final int STRING_IS_PARSED = 0x0001; - public static final int BOOL_IS_PARSED = 0x0002; - public static final int BLANK_IS_PARSED = 0x0004; // => blanks are not ignored, converted to 0 - - public static final int REF_STRING_IS_PARSED = 0x0008; - public static final int REF_BOOL_IS_PARSED = 0x0010; - public static final int REF_BLANK_IS_PARSED = 0x0020; - - public static final int STRING_IS_INVALID_VALUE = 0x0800; - - private final int flags; - - - public ValueEvalToNumericXlator(int flags) { - - if (false) { // uncomment to see who is using this class - System.err.println(new Throwable().getStackTrace()[1].getClassName() + "\t0x" - + Integer.toHexString(flags).toUpperCase()); - } - this.flags = flags; - } - - /** - * returned value can be either A NumericValueEval, BlankEval or ErrorEval. - * The params can be either NumberEval, BoolEval, StringEval, or - * RefEval - * @param eval - */ - public ValueEval attemptXlateToNumeric(ValueEval eval) { - - if (eval == null) { - throw new IllegalArgumentException("eval must not be null"); - } - - // most common case - least worries :) - if (eval instanceof NumberEval) { - return eval; - } - - if (eval instanceof BoolEval) { - return ((flags & BOOL_IS_PARSED) > 0) - ? (NumericValueEval) eval - : xlateBlankEval(BLANK_IS_PARSED); - } - - if (eval instanceof StringEval) { - return xlateStringEval((StringEval) eval); // TODO: recursive call needed - } - - if (eval instanceof RefEval) { - return xlateRefEval((RefEval) eval); - } - - if (eval instanceof ErrorEval) { - return eval; - } - - if (eval instanceof BlankEval) { - return xlateBlankEval(BLANK_IS_PARSED); - } - - // probably AreaEval? then not acceptable. - throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass()); - } - - /** - * no args are required since BlankEval has only one - * instance. If flag is set, a zero - * valued numbereval is returned, else BlankEval.INSTANCE - * is returned. - */ - private ValueEval xlateBlankEval(int flag) { - return ((flags & flag) > 0) - ? (ValueEval) NumberEval.ZERO - : BlankEval.INSTANCE; - } - - /** - * uses the relevant flags to decode the supplied RefVal - * @param eval - */ - private ValueEval xlateRefEval(RefEval reval) { - ValueEval eval = reval.getInnerValueEval(); - - // most common case - least worries :) - if (eval instanceof NumberEval) { - return eval; - } - - if (eval instanceof BoolEval) { - return ((flags & REF_BOOL_IS_PARSED) > 0) - ? (ValueEval) eval - : BlankEval.INSTANCE; - } - - if (eval instanceof StringEval) { - return xlateRefStringEval((StringEval) eval); - } - - if (eval instanceof ErrorEval) { - return eval; - } - - if (eval instanceof BlankEval) { - return xlateBlankEval(REF_BLANK_IS_PARSED); - } - - throw new RuntimeException("Invalid ValueEval type passed for conversion: (" - + eval.getClass().getName() + ")"); - } - - /** - * uses the relevant flags to decode the StringEval - * @param eval - */ - private ValueEval xlateStringEval(StringEval eval) { - - if ((flags & STRING_IS_PARSED) > 0) { - String s = eval.getStringValue(); - Double d = OperandResolver.parseDouble(s); - if(d == null) { - return ErrorEval.VALUE_INVALID; - } - return new NumberEval(d.doubleValue()); - } - // strings are errors? - if ((flags & STRING_IS_INVALID_VALUE) > 0) { - return ErrorEval.VALUE_INVALID; - } - - // ignore strings - return xlateBlankEval(BLANK_IS_PARSED); - } - - /** - * uses the relevant flags to decode the StringEval - * @param eval - */ - private ValueEval xlateRefStringEval(StringEval sve) { - if ((flags & REF_STRING_IS_PARSED) > 0) { - String s = sve.getStringValue(); - Double d = OperandResolver.parseDouble(s); - if(d == null) { - return ErrorEval.VALUE_INVALID; - } - return new NumberEval(d.doubleValue()); - } - // strings are blanks - return BlankEval.INSTANCE; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java new file mode 100644 index 0000000000..0495ee3fc4 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java @@ -0,0 +1,113 @@ +/* ==================================================================== + 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; + +/** + * @author Amol S. Deshmukh < amolweb at ya hoo dot com > + * + */ +public abstract class AggregateFunction extends MultiOperandNumericFunction { + + 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 AggregateFunction() { + protected double evaluate(double[] ops) throws EvaluationException { + if (ops.length < 2) { + throw new EvaluationException(ErrorEval.NUM_ERROR); + } + double[] values = new double[ops.length-1]; + int k = (int) ops[ops.length-1]; + System.arraycopy(ops, 0, values, 0, values.length); + return StatsLib.kthLargest(values, k); + } + }; + 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 AggregateFunction() { + protected double evaluate(double[] ops) throws EvaluationException { + if (ops.length < 2) { + throw new EvaluationException(ErrorEval.NUM_ERROR); + } + double[] values = new double[ops.length-1]; + int k = (int) ops[ops.length-1]; + System.arraycopy(ops, 0, values, 0, values.length); + return StatsLib.kthSmallest(values, k); + } + }; + 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); + } + }; +} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Atan2.java b/src/java/org/apache/poi/hssf/record/formula/functions/Atan2.java deleted file mode 100644 index a0ff7b00c0..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Atan2.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 6, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Atan2 extends NumericFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double d0 = 0; - double d1 = 0; - ValueEval retval = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 2: - ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d0 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - - if (retval == null) { - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d1 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - } - } - - if (retval == null) { - double d = (d0 == d1 && d1 == 0) - ? Double.NaN - : Math.atan2(d1, d0); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - return retval; - } - -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Avedev.java b/src/java/org/apache/poi/hssf/record/formula/functions/Avedev.java deleted file mode 100644 index bf888b97df..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Avedev.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Avedev extends MultiOperandNumericFunction { - - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - )); - - /** - * this is the default impl for the factory method getXlator - * of the super class NumericFunction. Subclasses can override this method - * if they desire to return a different ValueEvalToNumericXlator instance - * than the default. - */ - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = StatsLib.avedev(values); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Average.java b/src/java/org/apache/poi/hssf/record/formula/functions/Average.java deleted file mode 100644 index 4043040713..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Average.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Average extends MultiOperandNumericFunction { - - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - )); - - /** - * this is the default impl for the factory method getXlator - * of the super class NumericFunction. Subclasses can override this method - * if they desire to return a different ValueEvalToNumericXlator instance - * than the default. - */ - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = MathX.average(values); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Ceiling.java b/src/java/org/apache/poi/hssf/record/formula/functions/Ceiling.java deleted file mode 100644 index a158b856bc..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Ceiling.java +++ /dev/null @@ -1,82 +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 15, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Ceiling extends NumericFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double d0 = 0; - double d1 = 0; - ValueEval retval = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 2: - ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d0 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - - if (retval == null) { - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d1 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - } - } - - if (retval == null) { - double d = MathX.ceiling(d0, d1); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - return retval; - } - -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Combin.java b/src/java/org/apache/poi/hssf/record/formula/functions/Combin.java deleted file mode 100644 index 72d6d4691e..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Combin.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Combin extends NumericFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double d0 = 0; - double d1 = 0; - ValueEval retval = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 2: - ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d0 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - - if (retval == null) { - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d1 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - } - } - - if (retval == null) { - if (d0 > Integer.MAX_VALUE || d1 > Integer.MAX_VALUE) { - retval = ErrorEval.NUM_ERROR; - } - else { - double d = MathX.nChooseK((int) d0, (int) d1); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - } - return retval; - } - -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java b/src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java deleted file mode 100644 index be961c151e..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.StringValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Concatenate extends TextFunction { - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - StringBuffer sb = new StringBuffer(); - - for (int i=0, iSize=operands.length; i<iSize; i++) { - ValueEval ve = singleOperandEvaluate(operands[i], srcCellRow, srcCellCol); - if (ve instanceof StringValueEval) { - StringValueEval sve = (StringValueEval) ve; - sb.append(sve.getStringValue()); - } - else if (ve instanceof BlankEval) {} - else { - retval = ErrorEval.VALUE_INVALID; - break; - } - } - - if (retval == null) { - retval = new StringEval(sb.toString()); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Date.java b/src/java/org/apache/poi/hssf/record/formula/functions/Date.java deleted file mode 100644 index 6861a02524..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Date.java +++ /dev/null @@ -1,114 +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.usermodel.HSSFDateUtil; - -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.RefEval; -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.ValueEval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; - -/** - * @author Pavel Krupets (pkrupets at palmtreebusiness dot com) - */ -public class Date extends NumericFunction { - /** - * @see org.apache.poi.hssf.record.formula.functions.Function#evaluate(org.apache.poi.hssf.record.formula.eval.Eval[], int, short) - */ - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - if (operands.length == 3) { - ValueEval ve[] = new ValueEval[3]; - - ve[0] = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol); - ve[1] = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol); - ve[2] = singleOperandEvaluate(operands[2], srcCellRow, srcCellCol); - - if (validValues(ve)) { - int year = getYear(ve[0]); - int month = (int) ((NumericValueEval) ve[1]).getNumberValue() - 1; - int day = (int) ((NumericValueEval) ve[2]).getNumberValue(); - - if (year < 0 || month < 0 || day < 0) { - return ErrorEval.VALUE_INVALID; - } - - if (year == 1900 && month == Calendar.FEBRUARY && day == 29) { - return new NumberEval(60.0); - } - - 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 new NumberEval(HSSFDateUtil.getExcelDate(c.getTime(), false)); // XXX fix 1900/1904 problem - } - } - - return ErrorEval.VALUE_INVALID; - } - - private int getYear(ValueEval ve) { - int year = (int) ((NumericValueEval) ve).getNumberValue(); - - if (year < 0) { - return -1; - } - - return year < 1900 ? 1900 + year : year; - } - - private boolean validValues(ValueEval[] values) { - for (int i = 0; i < values.length; i++) { - ValueEval value = values[i]; - - if (value instanceof RefEval) { - RefEval re = (RefEval) value; - ValueEval ive = re.getInnerValueEval(); - - if (ive instanceof BlankEval) { - value = new NumberEval(0); - } else if (ive instanceof NumericValueEval) { - value = ive; - } else { - return false; - } - } - - if (!(value instanceof NumericValueEval)) { - return false; - } - } - - return true; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/DateFunc.java b/src/java/org/apache/poi/hssf/record/formula/functions/DateFunc.java new file mode 100644 index 0000000000..6cbe8414fe --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/functions/DateFunc.java @@ -0,0 +1,76 @@ +/* ==================================================================== + 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.usermodel.HSSFDateUtil; + +/** + * @author Pavel Krupets (pkrupets at palmtreebusiness dot com) + */ +public final class DateFunc extends NumericFunction.MultiArg { + + public static final Function instance = new DateFunc(); + + private DateFunc() { + super(3,3); + } + + protected double evaluate(double[] ds) throws EvaluationException { + int year = getYear(ds[0]); + int month = (int) ds[1] - 1; + int day = (int) ds[2]; + + if (year < 0 || month < 0 || day < 0) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + + if (year == 1900 && month == Calendar.FEBRUARY && day == 29) { + return 60.0; + } + + 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 HSSFDateUtil.getExcelDate(c.getTime(), false); // XXX 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; + } +} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Devsq.java b/src/java/org/apache/poi/hssf/record/formula/functions/Devsq.java deleted file mode 100644 index a198ff8398..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Devsq.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Devsq extends MultiOperandNumericFunction { - - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) (0 - | ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED - )); - - /** - * this is the default impl for the factory method getXlator - * of the super class NumericFunction. Subclasses can override this method - * if they desire to return a different ValueEvalToNumericXlator instance - * than the default. - */ - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = StatsLib.devsq(values); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Even.java b/src/java/org/apache/poi/hssf/record/formula/functions/Even.java index 272863908c..eaeed89247 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Even.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Even.java @@ -22,7 +22,7 @@ package org.apache.poi.hssf.record.formula.functions; * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ -public final class Even extends NumericFunctionOneArg { +public final class Even extends NumericFunction.OneArg { private static final long PARITY_MASK = 0xFFFFFFFFFFFFFFFEL; diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Exact.java b/src/java/org/apache/poi/hssf/record/formula/functions/Exact.java deleted file mode 100644 index 63dc18d771..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Exact.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.StringValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Exact extends TextFunction { - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - String s0 = null; - String s1 = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 2: - ValueEval ve = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol); - if (ve instanceof StringValueEval) { - StringValueEval sve = (StringValueEval) ve; - s0 = sve.getStringValue(); - } - else if (ve instanceof BlankEval) { - s0 = StringEval.EMPTY_INSTANCE.getStringValue(); - } - else { - retval = ErrorEval.VALUE_INVALID; - break; - } - - if (retval == null) { - ve = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol); - if (ve instanceof StringValueEval) { - StringValueEval sve = (StringValueEval) ve; - s1 = sve.getStringValue(); - } - else if (ve instanceof BlankEval) { - s1 = StringEval.EMPTY_INSTANCE.getStringValue(); - } - else { - retval = ErrorEval.VALUE_INVALID; - break; - } - } - } - - if (retval == null) { - boolean b = s0.equals(s1); - retval = b ? BoolEval.TRUE : BoolEval.FALSE; - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java index c054c6dac4..01fcf9ef0b 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java @@ -1,71 +1,77 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at -package org.apache.poi.hssf.record.formula.functions; + 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. +==================================================================== */ -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator; +package org.apache.poi.hssf.record.formula.functions; +import org.apache.poi.hssf.record.formula.eval.EvaluationException; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * Super class for all Evals for financial function evaluation. - * + * */ -public abstract class FinanceFunction extends NumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) (0 - | ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - | ValueEvalToNumericXlator.BLANK_IS_PARSED - | ValueEvalToNumericXlator.REF_BLANK_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - )); - - /** - * this is the default impl of the factory(ish) method getXlator. - * Subclasses can override this method - * if they desire to return a different ValueEvalToNumericXlator instance - * than the default. - */ - protected final ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - protected final ValueEval singleOperandNumericAsBoolean(Eval eval, int srcRow, short srcCol) { - ValueEval retval = null; - retval = singleOperandEvaluate(eval, srcRow, srcCol); - if (retval instanceof NumericValueEval) { - NumericValueEval nve = (NumericValueEval) retval; - retval = (nve.getNumberValue() == 0) - ? BoolEval.FALSE - : BoolEval.TRUE; - } - else { - retval = ErrorEval.VALUE_INVALID; - } - return retval; - } +public abstract class FinanceFunction extends NumericFunction.MultiArg { + + protected FinanceFunction() { + super (3, 5); + } + + 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); + } + }; } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Floor.java b/src/java/org/apache/poi/hssf/record/formula/functions/Floor.java deleted file mode 100644 index 8698ce9f23..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Floor.java +++ /dev/null @@ -1,82 +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 15, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Floor extends NumericFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double d0 = 0; - double d1 = 0; - ValueEval retval = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 2: - ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d0 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - - if (retval == null) { - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d1 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - } - } - - if (retval == null) { - double d = MathX.floor(d0, d1); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - return retval; - } - -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/FreeRefFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/FreeRefFunction.java index 2163d8f5ed..3a3478ca52 100755 --- a/src/java/org/apache/poi/hssf/record/formula/functions/FreeRefFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/FreeRefFunction.java @@ -45,13 +45,13 @@ 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 srcCellSheet zero based sheet index of the cell containing the currently evaluating formula * @param srcCellRow zero based row index of the cell containing the currently evaluating formula * @param srcCellCol zero based column index of the cell containing the currently evaluating formula * @param workbook is the workbook containing the formula/cell being evaluated - * @param sheet is the sheet containing the formula/cell being evaluated * @return never <code>null</code>. Possibly an instance of <tt>ErrorEval</tt> in the case of * a specified Excel error (Exceptions are never thrown to represent Excel errors). * */ - ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet); + ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol); } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Fv.java b/src/java/org/apache/poi/hssf/record/formula/functions/Fv.java deleted file mode 100644 index fd0335c388..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Fv.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -package org.apache.poi.hssf.record.formula.functions; - -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -public class Fv extends FinanceFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double rate = 0, nper = 0, pmt = 0, pv = 0, d = 0; - boolean type = false; - ValueEval retval = null; - ValueEval ve = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 5: - ve = singleOperandNumericAsBoolean(operands[4], srcRow, srcCol); - if (ve instanceof ErrorEval) { retval = ErrorEval.VALUE_INVALID; break; } - type = ((BoolEval) ve).getBooleanValue(); - case 4: - ve = singleOperandEvaluate(operands[3], srcRow, srcCol); - if (ve instanceof NumericValueEval) pv = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - case 3: - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) nper = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - - ve = singleOperandEvaluate(operands[2], srcRow, srcCol); - if (ve instanceof NumericValueEval) pmt = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - - ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) rate = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - } - - if (retval == null) { - d = FinanceLib.fv(rate, nper, pmt, pv, type); - retval = (Double.isNaN(d)) - ? (ValueEval) ErrorEval.VALUE_INVALID - : (Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - return retval; - } - -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Index.java b/src/java/org/apache/poi/hssf/record/formula/functions/Index.java index 3c93c0846b..1c149dbdfc 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Index.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Index.java @@ -22,6 +22,7 @@ import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.EvaluationException; import org.apache.poi.hssf.record.formula.eval.OperandResolver; +import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; /** @@ -51,6 +52,10 @@ public final class Index implements Function { return ErrorEval.VALUE_INVALID; } Eval firstArg = args[0]; + if (firstArg instanceof RefEval) { + // convert to area ref for simpler code in getValueFromArea() + firstArg = ((RefEval)firstArg).offset(0, 0, 0, 0); + } if(!(firstArg instanceof AreaEval)) { // else the other variation of this function takes an array as the first argument @@ -84,16 +89,63 @@ public final class Index implements Function { // too many arguments return ErrorEval.VALUE_INVALID; } - return getValueFromArea(reference, rowIx, columnIx); + return getValueFromArea(reference, rowIx, columnIx, nArgs); } catch (EvaluationException e) { return e.getErrorEval(); } } - private static ValueEval getValueFromArea(AreaEval ae, int rowIx, int columnIx) throws EvaluationException { + /** + * @param nArgs - needed because error codes are slightly different when only 2 args are passed + */ + private static ValueEval getValueFromArea(AreaEval ae, int pRowIx, int pColumnIx, int nArgs) throws EvaluationException { + int rowIx; + int columnIx; + + // when the area ref is a single row or a single column, + // there are special rules for conversion of rowIx and columnIx + if (ae.isRow()) { + if (ae.isColumn()) { + rowIx = pRowIx == -1 ? 0 : pRowIx; + columnIx = pColumnIx == -1 ? 0 : pColumnIx; + } else { + if (nArgs == 2) { + rowIx = 0; + columnIx = pRowIx; + } else { + rowIx = pRowIx == -1 ? 0 : pRowIx; + columnIx = pColumnIx; + } + } + if (rowIx < -1 || columnIx < -1) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + } else if (ae.isColumn()) { + if (nArgs == 2) { + rowIx = pRowIx; + columnIx = 0; + } else { + rowIx = pRowIx; + columnIx = pColumnIx == -1 ? 0 : pColumnIx; + } + if (rowIx < -1 || columnIx < -1) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + } else { + if (nArgs == 2) { + // always an error with 2-D area refs + if (pRowIx < -1) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + throw new EvaluationException(ErrorEval.REF_INVALID); + } + // Normal case - area ref is 2-D, and both index args were provided + rowIx = pRowIx; + columnIx = pColumnIx; + } + int width = ae.getWidth(); int height = ae.getHeight(); - // Slightly irregular logic for bounds checking errors if (rowIx >= height || columnIx >= width) { throw new EvaluationException(ErrorEval.REF_INVALID); diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java b/src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java index 8d7d4463e5..be63d70275 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java @@ -41,7 +41,7 @@ import org.apache.poi.ss.usermodel.Workbook; */ public final class Indirect implements FreeRefFunction { - public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) { + public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol) { // TODO - implement INDIRECT() return ErrorEval.FUNCTION_NOT_IMPLEMENTED; } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Large.java b/src/java/org/apache/poi/hssf/record/formula/functions/Large.java deleted file mode 100644 index 3b9aed9768..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Large.java +++ /dev/null @@ -1,81 +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 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Large extends MultiOperandNumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) (0 - | ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED - //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED - //| ValueEvalToNumericXlator.BLANK_IS_PARSED - )); - - /** - * this is the default impl for the factory method getXlator - * of the super class NumericFunction. Subclasses can override this method - * if they desire to return a different ValueEvalToNumericXlator instance - * than the default. - */ - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] ops = getNumberArray(operands, srcCellRow, srcCellCol); - if (ops == null || ops.length < 2) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double[] values = new double[ops.length-1]; - int k = (int) ops[ops.length-1]; - System.arraycopy(ops, 0, values, 0, values.length); - double d = StatsLib.kthLargest(values, k); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Left.java b/src/java/org/apache/poi/hssf/record/formula/functions/Left.java deleted file mode 100644 index ecb14a7008..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Left.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -package org.apache.poi.hssf.record.formula.functions; - -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.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; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Left extends TextFunction { - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - Eval retval = ErrorEval.VALUE_INVALID; - int index = 1; - switch (operands.length) { - default: - break; - case 2: - Eval indexEval = operands[1]; - index = evaluateAsInteger(indexEval); - if (index < 0) { - break; - } - case 1: - ValueEval veval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol); - String str = null; - if (veval instanceof StringEval) { - StringEval stringEval = (StringEval) veval; - str = stringEval.getStringValue(); - } - else if (veval instanceof BoolEval) { - BoolEval beval = (BoolEval) veval; - str = beval.getBooleanValue() ? "TRUE" : "FALSE"; - } - else if (veval instanceof NumberEval) { - NumberEval neval = (NumberEval) veval; - str = neval.getStringValue(); - } - if (null != str) { - str = str.substring(0, Math.min(str.length(), index)); - retval = new StringEval(str); - } - } - return retval; - } - - protected int evaluateAsInteger(Eval eval) { - int numval = -1; - if (eval instanceof NumberEval) { - NumberEval neval = (NumberEval) eval; - double d = neval.getNumberValue(); - numval = (int) d; - } - else if (eval instanceof StringEval) { - StringEval seval = (StringEval) eval; - String s = seval.getStringValue(); - try { - double d = Double.parseDouble(s); - numval = (int) d; - } - catch (Exception e) { - } - } - else if (eval instanceof BoolEval) { - BoolEval beval = (BoolEval) eval; - numval = beval.getBooleanValue() ? 1 : 0; - } - else if (eval instanceof RefEval) { - numval = evaluateAsInteger(xlateRefEval((RefEval) eval)); - } - return numval; - } - - protected Eval xlateRefEval(RefEval reval) { - Eval retval = reval.getInnerValueEval(); - - if (retval instanceof RefEval) { - retval = xlateRefEval((RefEval) retval); - } - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Len.java b/src/java/org/apache/poi/hssf/record/formula/functions/Len.java deleted file mode 100644 index 0bc49b4070..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Len.java +++ /dev/null @@ -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.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.EvaluationException; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.OperandResolver; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public final class Len extends TextFunction { - - public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { - - if(args.length != 1) { - return ErrorEval.VALUE_INVALID; - } - - try { - ValueEval veval = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol); - - String str = OperandResolver.coerceValueToString(veval); - - return new NumberEval(str.length()); - } catch (EvaluationException e) { - return e.getErrorEval(); - } - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Log.java b/src/java/org/apache/poi/hssf/record/formula/functions/Log.java deleted file mode 100644 index 7727c35275..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Log.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 6, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * Log: LOG(number,[base]) - */ -public class Log extends NumericFunction { - - private static final double DEFAULT_BASE = 10; - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double d = 0; - double base = DEFAULT_BASE; - double num = 0; - ValueEval retval = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 2: // second arg is base - ValueEval ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - base = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - - case 1: // first arg is number - if (retval == null) { - ValueEval vev = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (vev instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) vev; - num = ne.getNumberValue(); - } - else if (vev instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - } - } - - if (retval == null) { - d = (base == E) - ? Math.log(num) - : Math.log(num) / Math.log(base); - retval = (Double.isNaN(d) || Double.isInfinite(d)) ? (ValueEval) ErrorEval.VALUE_INVALID : new NumberEval(d); - } - return retval; - } - -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Lower.java b/src/java/org/apache/poi/hssf/record/formula/functions/Lower.java deleted file mode 100644 index edf7e279ea..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Lower.java +++ /dev/null @@ -1,65 +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 15, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.StringValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Lower extends TextFunction { - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - String s = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 1: - ValueEval ve = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol); - if (ve instanceof StringValueEval) { - StringValueEval sve = (StringValueEval) ve; - s = sve.getStringValue(); - } - else if (ve instanceof BlankEval) {} - else { - retval = ErrorEval.VALUE_INVALID; - break; - } - } - - if (retval == null) { - s = (s == null) ? EMPTY_STRING : s; - retval = new StringEval(s.toLowerCase()); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Max.java b/src/java/org/apache/poi/hssf/record/formula/functions/Max.java deleted file mode 100644 index d5b3971494..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Max.java +++ /dev/null @@ -1,68 +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 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Max extends MultiOperandNumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - )); - - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = values.length > 0 ? MathX.max(values) : 0; - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Maxa.java b/src/java/org/apache/poi/hssf/record/formula/functions/Maxa.java deleted file mode 100644 index e25db7b746..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Maxa.java +++ /dev/null @@ -1,66 +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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public final class Maxa extends MultiOperandNumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - | ValueEvalToNumericXlator.BLANK_IS_PARSED - | ValueEvalToNumericXlator.REF_BLANK_IS_PARSED - )); - - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = values.length > 0 ? MathX.max(values) : 0; - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Median.java b/src/java/org/apache/poi/hssf/record/formula/functions/Median.java deleted file mode 100644 index 57a18b3314..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Median.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Median extends MultiOperandNumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) (0 - | ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED - //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED - //| ValueEvalToNumericXlator.BLANK_IS_PARSED - )); - - /** - * this is the default impl for the factory method getXlator - * of the super class NumericFunction. Subclasses can override this method - * if they desire to return a different ValueEvalToNumericXlator instance - * than the default. - */ - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = StatsLib.median(values); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Mid.java b/src/java/org/apache/poi/hssf/record/formula/functions/Mid.java deleted file mode 100644 index b9d679d3d3..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Mid.java +++ /dev/null @@ -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 org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.EvaluationException; -import org.apache.poi.hssf.record.formula.eval.OperandResolver; -import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * An implementation of the MID function<br/> MID returns a specific number of - * characters from a text string, starting at the specified position.<p/> - * - * <b>Syntax<b>:<br/> <b>MID</b>(<b>text</b>, <b>start_num</b>, - * <b>num_chars</b>)<br/> - * - * @author Manda Wilson < wilson at c bio dot msk cc dot org > - */ -public class Mid implements Function { - /** - * Returns a specific number of characters from a text string, starting at - * the position you specify, based on the number of characters you specify. - * - * @see org.apache.poi.hssf.record.formula.eval.Eval - */ - public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { - if (args.length != 3) { - return ErrorEval.VALUE_INVALID; - } - - String text; - int startIx; // zero based - int numChars; - - try { - ValueEval evText = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol); - text = OperandResolver.coerceValueToString(evText); - int startCharNum = evaluateNumberArg(args[1], srcCellRow, srcCellCol); - numChars = evaluateNumberArg(args[2], srcCellRow, srcCellCol); - startIx = startCharNum - 1; // convert to zero-based - } catch (EvaluationException e) { - return e.getErrorEval(); - } - - int len = text.length(); - if (startIx < 0) { - return ErrorEval.VALUE_INVALID; - } - if (numChars < 0) { - return ErrorEval.VALUE_INVALID; - } - if (numChars < 0 || startIx > len) { - return new StringEval(""); - } - int endIx = startIx + numChars; - if (endIx > len) { - endIx = len; - } - String result = text.substring(startIx, endIx); - return new StringEval(result); - - } - - private static int evaluateNumberArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { - ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); - // Note - for start_num arg, blank/zero causes error(#VALUE!), - // but for num_chars causes empty string to be returned. - return OperandResolver.coerceValueToInt(ev); - } -}
\ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Min.java b/src/java/org/apache/poi/hssf/record/formula/functions/Min.java deleted file mode 100644 index 8cd896783c..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Min.java +++ /dev/null @@ -1,68 +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 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Min extends MultiOperandNumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - )); - - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = values.length > 0 ? MathX.min(values) : 0; - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Mina.java b/src/java/org/apache/poi/hssf/record/formula/functions/Mina.java deleted file mode 100644 index 21ba47b569..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Mina.java +++ /dev/null @@ -1,66 +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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Mina extends MultiOperandNumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - | ValueEvalToNumericXlator.REF_BLANK_IS_PARSED - | ValueEvalToNumericXlator.BLANK_IS_PARSED - )); - - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = values.length > 0 ? MathX.min(values) : 0; - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/MinaMaxa.java b/src/java/org/apache/poi/hssf/record/formula/functions/MinaMaxa.java new file mode 100644 index 0000000000..70e5e7ff1b --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/formula/functions/MinaMaxa.java @@ -0,0 +1,40 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.record.formula.functions; + +/** + * @author Amol S. Deshmukh < amolweb at ya hoo dot com > + * + */ +public abstract class MinaMaxa extends MultiOperandNumericFunction { + + protected MinaMaxa() { + super(true, true); + } + + public static final Function MAXA = new MinaMaxa() { + protected double evaluate(double[] values) { + return values.length > 0 ? MathX.max(values) : 0; + } + }; + public static final Function MINA = new MinaMaxa() { + protected double evaluate(double[] values) { + return values.length > 0 ? MathX.min(values) : 0; + } + }; +} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Mod.java b/src/java/org/apache/poi/hssf/record/formula/functions/Mod.java deleted file mode 100644 index ebef231544..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Mod.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Mod extends NumericFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double d0 = 0; - double d1 = 0; - ValueEval retval = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 2: - ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d0 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - - if (retval == null) { - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d1 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - } - } - - if (retval == null) { - if (d1 == 0) { - retval = ErrorEval.DIV_ZERO; - } - else { - double d = MathX.mod(d0, d1); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - } - return retval; - } - -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Mode.java b/src/java/org/apache/poi/hssf/record/formula/functions/Mode.java index 724b6e6543..c236ccee62 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Mode.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Mode.java @@ -1,78 +1,134 @@ -/* -* 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 15, 2005 - * - */ +/* ==================================================================== + 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.AreaEval; +import org.apache.poi.hssf.record.formula.eval.BlankEval; +import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; +import org.apache.poi.hssf.record.formula.eval.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.hssf.record.formula.eval.ValueEvalToNumericXlator; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * + * */ -public class Mode extends MultiOperandNumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) (0 - //| ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED - //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED - //| ValueEvalToNumericXlator.BLANK_IS_PARSED - )); - - /** - * this is the default impl for the factory method getXlator - * of the super class NumericFunction. Subclasses can override this method - * if they desire to return a different ValueEvalToNumericXlator instance - * than the default. - */ - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } +public 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 Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { + double result; + try { + List temp = new ArrayList(); + 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] = ((Double) temp.get(i)).doubleValue(); + } + result = evaluate(values); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + return new NumberEval(result); + } + + private static void collectValues(Eval arg, List temp) throws EvaluationException { + if (arg instanceof AreaEval) { + AreaEval ae = (AreaEval) 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.getRelativeValue(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); + } - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = StatsLib.mode(values); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } + private static void collectValue(Eval arg, List 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() + ")"); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java index a9e98ae313..b3feac6a07 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java @@ -1,30 +1,33 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ package org.apache.poi.hssf.record.formula.functions; import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.BlankEval; +import org.apache.poi.hssf.record.formula.eval.BoolEval; +import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.Ref2DEval; +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.eval.ValueEvalToNumericXlator; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > @@ -32,175 +35,166 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator; * classes that take variable number of operands, and * where the order of operands does not matter */ -public abstract class MultiOperandNumericFunction extends NumericFunction { - 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; - } - - public void add(double[] values) { - int addLen = values.length; - ensureCapacity(_count + addLen); - System.arraycopy(values, 0, _array, _count, addLen); - _count += addLen; - } - - 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; - - protected abstract ValueEvalToNumericXlator getXlator(); - - /** - * 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>. - * - * @param operands - * @param srcRow - * @param srcCol - */ - protected double[] getNumberArray(Eval[] operands, int srcRow, short srcCol) { - if (operands.length > getMaxNumOperands()) { - return null; - } - DoubleList retval = new DoubleList(); - - for (int i=0, iSize=operands.length; i<iSize; i++) { - double[] temp = getNumberArray(operands[i], srcRow, srcCol); - if (temp == null) { - return null; // error occurred. - } - retval.add(temp); - } - return retval.toArray(); - } - - /** - * Same as getNumberArray(Eval[], int, short) except that this - * takes Eval instead of Eval[]. - * @param operand - * @param srcRow - * @param srcCol - */ - protected double[] getNumberArray(Eval operand, int srcRow, short srcCol) { - - if (operand instanceof AreaEval) { - AreaEval ae = (AreaEval) operand; - DoubleList retval = new DoubleList(); - 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.getRelativeValue(rrIx, rcIx); - /* - * TODO: For an AreaEval, we are constructing a RefEval - * per element. - * For now this is a tempfix solution since this may - * require a more generic fix at the level of - * HSSFFormulaEvaluator where we store an array - * of RefEvals as the "values" array. - */ - RefEval re = new Ref2DEval(null, ve1); - ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol); - - if (ve instanceof NumericValueEval) { - NumericValueEval nve = (NumericValueEval) ve; - retval.add(nve.getNumberValue()); - } - else if (ve instanceof BlankEval) { - // note - blanks are ignored, so returned array will be smaller. - } - else { - return null; // indicate to calling subclass that error occurred - } - } - } - return retval.toArray(); - } - - // for ValueEvals other than AreaEval - ValueEval ve = singleOperandEvaluate(operand, srcRow, srcCol); - - if (ve instanceof NumericValueEval) { - NumericValueEval nve = (NumericValueEval) ve; - return new double[] { nve.getNumberValue(), }; - } - - if (ve instanceof BlankEval) { - // ignore blanks - return EMPTY_DOUBLE_ARRAY; - } - return null; - } - - /** - * Ensures that a two dimensional array has all sub-arrays present and the same length - * @return <code>false</code> if any sub-array is missing, or is of different length - */ - protected static final boolean areSubArraysConsistent(double[][] values) { - - if (values == null || values.length < 1) { - // TODO this doesn't seem right. Fix or add comment. - return true; - } - - if (values[0] == null) { - return false; - } - int outerMax = values.length; - int innerMax = values[0].length; - for (int i=1; i<outerMax; i++) { // note - 'i=1' start at second sub-array - double[] subArr = values[i]; - if (subArr == null) { - return false; - } - if (innerMax != subArr.length) { - return false; - } - } - return true; - } - - - +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 Eval evaluate(Eval[] args, int srcCellRow, short 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(Eval[] 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(Eval operand, DoubleList temp) throws EvaluationException { + + if (operand instanceof AreaEval) { + AreaEval ae = (AreaEval) 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.getRelativeValue(rrIx, rcIx); + collectValue(ve, true, temp); + } + } + return; + } + if (operand instanceof RefEval) { + RefEval re = (RefEval) operand; + collectValue(re.getInnerValueEval(), true, temp); + return; + } + collectValue((ValueEval)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() + ")"); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Nper.java b/src/java/org/apache/poi/hssf/record/formula/functions/Nper.java deleted file mode 100644 index 95411edb77..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Nper.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -package org.apache.poi.hssf.record.formula.functions; - -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -public class Nper extends FinanceFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double rate = 0, fv = 0, pmt = 0, pv = 0, d = 0; - boolean type = false; - ValueEval retval = null; - ValueEval ve = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 5: - ve = singleOperandNumericAsBoolean(operands[4], srcRow, srcCol); - if (ve instanceof ErrorEval) { retval = ErrorEval.VALUE_INVALID; break; } - type = ((BoolEval) ve).getBooleanValue(); - case 4: - ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) rate = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) pmt = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - - ve = singleOperandEvaluate(operands[2], srcRow, srcCol); - if (ve instanceof NumericValueEval) pv = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - - ve = singleOperandEvaluate(operands[3], srcRow, srcCol); - if (ve instanceof NumericValueEval) fv = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - } - - if (retval == null) { - d = FinanceLib.nper(rate, pmt, pv, fv, type); - retval = (Double.isNaN(d)) - ? (ValueEval) ErrorEval.VALUE_INVALID - : (Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - return retval; - } - -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java index fd96f1495a..9a702ee6a3 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java @@ -1,98 +1,316 @@ -/* -* 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 14, 2005 - * - */ +/* ==================================================================== + 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.Eval; +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.eval.ValueEvalToNumericXlator; /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * */ public abstract class NumericFunction implements Function { - - protected static final double E = Math.E; - protected static final double PI = Math.PI; - - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - | ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - )); - - private static final int DEFAULT_MAX_NUM_OPERANDS = 30; - - /** - * this is the default impl of the factory(ish) method getXlator. - * Subclasses can override this method - * if they desire to return a different ValueEvalToNumericXlator instance - * than the default. - */ - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) { - ValueEval retval; - if (eval instanceof AreaEval) { - AreaEval ae = (AreaEval) eval; - if (ae.contains(srcRow, srcCol)) { // circular ref! - retval = ErrorEval.CIRCULAR_REF_ERROR; - } - else if (ae.isRow()) { - if (ae.containsColumn(srcCol)) { - ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol); - ve = getXlator().attemptXlateToNumeric(ve); - retval = getXlator().attemptXlateToNumeric(ve); - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else if (ae.isColumn()) { - if (ae.containsRow(srcRow)) { - ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn()); - retval = getXlator().attemptXlateToNumeric(ve); - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else { - retval = getXlator().attemptXlateToNumeric((ValueEval) eval); - } - return retval; - } + 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(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + double result = OperandResolver.coerceValueToDouble(ve); + checkValue(result); + return result; + } + + private static final void checkValue(double result) throws EvaluationException { + if (Double.isNaN(result) || Double.isInfinite(result)) { + throw new EvaluationException(ErrorEval.NUM_ERROR); + } + } + + public final Eval evaluate(Eval[] args, int srcCellRow, short 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(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException; + + /* -------------------------------------------------------------------------- */ + // intermediate sub-classes (one-arg, two-arg and multi-arg + + + public static abstract class OneArg extends NumericFunction { + protected OneArg() { + // no fields to initialise + } + protected final double eval(Eval[] args, int srcCellRow, short 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 NumericFunction { + protected TwoArg() { + // no fields to initialise + } + protected final double eval(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException { + if (args.length != 2) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + double d0 = singleOperandEvaluate(args[0], srcCellRow, srcCellCol); + double d1 = singleOperandEvaluate(args[1], srcCellRow, srcCellCol); + return evaluate(d0, d1); + } + protected abstract double evaluate(double d0, double d1) throws EvaluationException; + } + + public static abstract class MultiArg extends NumericFunction { + private final int _minArgs; + private final int _maxArgs; + protected MultiArg(int minArgs, int maxArgs) { + _minArgs = minArgs; + _maxArgs = maxArgs; + } + protected final double eval(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException { + int nArgs = args.length; + if (nArgs < _minArgs || nArgs > _maxArgs) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + double[] ds = new double[nArgs]; + for(int i=0; i<nArgs; i++) { + ds[i] = singleOperandEvaluate(args[i], srcCellRow, srcCellCol); + } + return evaluate(ds); + } + protected abstract double evaluate(double[] ds) 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); + } + }; + public static final Function DOLLAR = new OneArg() { + protected double evaluate(double d) { + return d; + } + }; + 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); + } + }; + + /* -------------------------------------------------------------------------- */ + + public static final Function LOG = new MultiArg(1,2) { + protected double evaluate(double[] ds) { + + double logE = Math.log(ds[0]); + if (ds.length == 1) { + return logE / LOG_10_TO_BASE_e; + } + double base = ds[1]; + if (base == Math.E) { + return logE; + } + return logE / Math.log(base); + } + }; } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunctionOneArg.java b/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunctionOneArg.java deleted file mode 100644 index 0012013949..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/NumericFunctionOneArg.java +++ /dev/null @@ -1,172 +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.Eval; -import org.apache.poi.hssf.record.formula.eval.EvaluationException; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.OperandResolver; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public abstract class NumericFunctionOneArg implements Function { - - public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { - if (args.length != 1) { - return ErrorEval.VALUE_INVALID; - } - try { - ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol); - double d = OperandResolver.coerceValueToDouble(ve); - if (Double.isNaN(d) || Double.isInfinite(d)) { - return ErrorEval.NUM_ERROR; - } - double result = evaluate(d); - if (Double.isNaN(result) || Double.isInfinite(result)) { - return ErrorEval.NUM_ERROR; - } - return new NumberEval(result); - } catch (EvaluationException e) { - return e.getErrorEval(); - } - } - - protected abstract double evaluate(double d); - - public static final Function ABS = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.abs(d); - } - }; - public static final Function ACOS = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.acos(d); - } - }; - public static final Function ACOSH = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return MathX.acosh(d); - } - }; - public static final Function ASIN = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.asin(d); - } - }; - public static final Function ASINH = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return MathX.asinh(d); - } - }; - public static final Function ATAN = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.atan(d); - } - }; - public static final Function ATANH = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return MathX.atanh(d); - } - }; - public static final Function COS = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.cos(d); - } - }; - public static final Function COSH = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return MathX.cosh(d); - } - }; - public static final Function DEGREES = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.toDegrees(d); - } - }; - public static final Function DOLLAR = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return d; - } - }; - public static final Function EXP = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.pow(Math.E, d); - } - }; - public static final Function FACT = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return MathX.factorial((int)d); - } - }; - public static final Function INT = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.round(d-0.5); - } - }; - public static final Function LN = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.log(d); - } - }; - static final double LOG_10_TO_BASE_e = Math.log(10); - public static final Function LOG10 = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.log(d) / LOG_10_TO_BASE_e; - } - }; - public static final Function RADIANS = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.toRadians(d); - } - }; - public static final Function SIGN = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return MathX.sign(d); - } - }; - public static final Function SIN = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.sin(d); - } - }; - public static final Function SINH = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return MathX.sinh(d); - } - }; - public static final Function SQRT = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.sqrt(d); - } - }; - - public static final Function TAN = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return Math.tan(d); - } - }; - public static final Function TANH = new NumericFunctionOneArg() { - protected double evaluate(double d) { - return MathX.tanh(d); - } - }; -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Odd.java b/src/java/org/apache/poi/hssf/record/formula/functions/Odd.java index 4cb8c700d1..74eddac2de 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Odd.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Odd.java @@ -22,7 +22,7 @@ package org.apache.poi.hssf.record.formula.functions; * @author Amol S. Deshmukh < amolweb at ya hoo dot com > * */ -public final class Odd extends NumericFunctionOneArg { +public final class Odd extends NumericFunction.OneArg { private static final long PARITY_MASK = 0xFFFFFFFFFFFFFFFEL; protected double evaluate(double d) { diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java b/src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java deleted file mode 100644 index 68a8d43dce..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java +++ /dev/null @@ -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.functions; - -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.EvaluationException; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * Implementation for the PMT() Excel function.<p/> - * - * <b>Syntax:</b><br/> - * <b>PMT</b>(<b>rate</b>, <b>nper</b>, <b>pv</b>, fv, type)<p/> - * - * Returns the constant repayment amount required for a loan assuming a constant interest rate.<p/> - * - * <b>rate</b> the loan interest rate.<br/> - * <b>nper</b> the number of loan repayments.<br/> - * <b>pv</b> the present value of the future payments (or principle).<br/> - * <b>fv</b> the future value (default zero) surplus cash at the end of the loan lifetime.<br/> - * <b>type</b> whether payments are due at the beginning(1) or end(0 - default) of each payment period.<br/> - * - */ -public final class Pmt extends FinanceFunction { - - public Eval evaluate(Eval[] args, int srcRow, short srcCol) { - - if(args.length < 3 || args.length > 5) { - return ErrorEval.VALUE_INVALID; - } - - try { - // evaluate first three (always present) args - double rate = evalArg(args[0], srcRow, srcCol); - double nper = evalArg(args[1], srcRow, srcCol); - double pv = evalArg(args[2], srcRow, srcCol); - double fv = 0; - boolean arePaymentsAtPeriodBeginning = false; - - switch (args.length) { - case 5: - ValueEval ve = singleOperandNumericAsBoolean(args[4], srcRow, srcCol); - if (ve instanceof ErrorEval) { - return ve; - } - arePaymentsAtPeriodBeginning = ((BoolEval) ve).getBooleanValue(); - case 4: - fv = evalArg(args[3], srcRow, srcCol); - } - double d = FinanceLib.pmt(rate, nper, pv, fv, arePaymentsAtPeriodBeginning); - if (Double.isNaN(d)) { - return ErrorEval.VALUE_INVALID; - } - if (Double.isInfinite(d)) { - return ErrorEval.NUM_ERROR; - } - return new NumberEval(d); - } catch (EvaluationException e) { - return e.getErrorEval(); - } - } - - private double evalArg(Eval arg, int srcRow, short srcCol) throws EvaluationException { - ValueEval ve = singleOperandEvaluate(arg, srcRow, srcCol); - if(ve instanceof ErrorEval) { - throw new EvaluationException((ErrorEval) ve); - } - if (ve instanceof NumericValueEval) { - return ((NumericValueEval) ve).getNumberValue(); - } - throw new EvaluationException(ErrorEval.VALUE_INVALID); - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Power.java b/src/java/org/apache/poi/hssf/record/formula/functions/Power.java deleted file mode 100644 index 1327e80061..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Power.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 6, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Power extends NumericFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double d0 = 0; - double d1 = 0; - ValueEval retval = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 2: - ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d0 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - - if (retval == null) { - ValueEval vev = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (vev instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) vev; - d1 = ne.getNumberValue(); - } - else if (vev instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - } - } - - if (retval == null) { - double d = Math.pow(d0, d1); - retval = (Double.isNaN(d) || Double.isNaN(d)) ? (ValueEval) ErrorEval.VALUE_INVALID : new NumberEval(d); - } - return retval; - } - -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Product.java b/src/java/org/apache/poi/hssf/record/formula/functions/Product.java deleted file mode 100644 index a88a3245d6..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Product.java +++ /dev/null @@ -1,68 +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 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Product extends MultiOperandNumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - )); - - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = MathX.product(values); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Pv.java b/src/java/org/apache/poi/hssf/record/formula/functions/Pv.java deleted file mode 100644 index 1d54bee93d..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Pv.java +++ /dev/null @@ -1,74 +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 15, 2005 - * - */ -package org.apache.poi.hssf.record.formula.functions; - -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -public class Pv extends FinanceFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double rate = 0, fv = 0, nper = 0, pmt = 0, d = 0; - boolean type = false; - ValueEval retval = null; - ValueEval ve = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 5: - ve = singleOperandNumericAsBoolean(operands[4], srcRow, srcCol); - if (ve instanceof ErrorEval) { retval = ErrorEval.VALUE_INVALID; break; } - type = ((BoolEval) ve).getBooleanValue(); - case 4: - ve = singleOperandEvaluate(operands[3], srcRow, srcCol); - if (ve instanceof NumericValueEval) fv = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - case 3: - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) nper = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - - ve = singleOperandEvaluate(operands[2], srcRow, srcCol); - if (ve instanceof NumericValueEval) pmt = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - - ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) rate = ((NumericValueEval) ve).getNumberValue(); - else { retval = ErrorEval.VALUE_INVALID; break; } - } - - if (retval == null) { - d = FinanceLib.pv(rate, nper, pmt, fv, type); - retval = (Double.isNaN(d)) - ? (ValueEval) ErrorEval.VALUE_INVALID - : (Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Replace.java b/src/java/org/apache/poi/hssf/record/formula/functions/Replace.java index 95413f0823..1dfc4f8f05 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Replace.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Replace.java @@ -1,112 +1,70 @@ -/* -* 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 15, 2005 - * - */ +/* ==================================================================== + 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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; +import org.apache.poi.hssf.record.formula.eval.EvaluationException; import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.StringValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; /** - * An implementation of the REPLACE function: - * Replaces part of a text string based on the number of characters - * you specify, with another text string. + * An implementation of the Excel REPLACE() function<p/>: + * Replaces part of a text string based on the number of characters + * you specify, with another text string.<br/> + * + * <b>Syntax</b>:<br/> + * <b>REPLACE</b>(<b>oldText</b>, <b>startNum</b>, <b>numChars</b>, <b>newText</b>)<p/> + * + * <b>oldText</b> The text string containing characters to replace<br/> + * <b>startNum</b> The position of the first character to replace (1-based)<br/> + * <b>numChars</b> The number of characters to replace<br/> + * <b>newText</b> The new text value to replace the removed section<br/> + * * @author Manda Wilson < wilson at c bio dot msk cc dot org > */ -public class Replace extends TextFunction { +public final class Replace extends TextFunction { + + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + if (args.length != 4) { + return ErrorEval.VALUE_INVALID; + } - /** - * Replaces part of a text string based on the number of characters - * you specify, with another text string. - * - * @see org.apache.poi.hssf.record.formula.eval.Eval - */ - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - Eval retval = null; - String oldStr = null; - String newStr = null; - int startNum = 0; - int numChars = 0; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - case 4: - // first operand is text string containing characters to replace - // second operand is position of first character to replace - // third operand is the number of characters in the old string - // you want to replace with new string - // fourth operand is the new string - ValueEval firstveval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol); - ValueEval secondveval = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol); - ValueEval thirdveval = singleOperandEvaluate(operands[2], srcCellRow, srcCellCol); - ValueEval fourthveval = singleOperandEvaluate(operands[3], srcCellRow, srcCellCol); - if (firstveval instanceof StringValueEval - && secondveval instanceof NumericValueEval - && thirdveval instanceof NumericValueEval - && fourthveval instanceof StringValueEval) { - - StringValueEval oldStrEval = (StringValueEval) firstveval; - oldStr = oldStrEval.getStringValue(); - - NumericValueEval startNumEval = (NumericValueEval) secondveval; - // NOTE: it is safe to cast to int here - // because in Excel =REPLACE("task", 2.7, 3, "est") - // returns test - // so 2.7 must be truncated to 2 - // and =REPLACE("task", 1, 1.9, "") returns ask - // so 1.9 must be truncated to 1 - startNum = (int) startNumEval.getNumberValue(); - - NumericValueEval numCharsEval = (NumericValueEval) thirdveval; - numChars = (int) numCharsEval.getNumberValue(); - - StringValueEval newStrEval = (StringValueEval) fourthveval; - newStr = newStrEval.getStringValue(); - } else { - retval = ErrorEval.VALUE_INVALID; - } - } - - if (retval == null) { - if (startNum < 1 || numChars < 0) { - retval = ErrorEval.VALUE_INVALID; - } else { - StringBuffer strBuff = new StringBuffer(oldStr); - // remove any characters that should be replaced - if (startNum <= oldStr.length() && numChars != 0) { - strBuff.delete(startNum - 1, startNum - 1 + numChars); - } - // now insert (or append) newStr - if (startNum > strBuff.length()) { - strBuff.append(newStr); - } else { - strBuff.insert(startNum - 1, newStr); - } - retval = new StringEval(strBuff.toString()); - } - } - return retval; - } + String oldStr = evaluateStringArg(args[0], srcCellRow, srcCellCol); + int startNum = evaluateIntArg(args[1], srcCellRow, srcCellCol); + int numChars = evaluateIntArg(args[2], srcCellRow, srcCellCol); + String newStr = evaluateStringArg(args[3], srcCellRow, srcCellCol); + if (startNum < 1 || numChars < 0) { + return ErrorEval.VALUE_INVALID; + } + StringBuffer strBuff = new StringBuffer(oldStr); + // remove any characters that should be replaced + if (startNum <= oldStr.length() && numChars != 0) { + strBuff.delete(startNum - 1, startNum - 1 + numChars); + } + // now insert (or append) newStr + if (startNum > strBuff.length()) { + strBuff.append(newStr); + } else { + strBuff.insert(startNum - 1, newStr); + } + return new StringEval(strBuff.toString()); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Right.java b/src/java/org/apache/poi/hssf/record/formula/functions/Right.java deleted file mode 100644 index 771d045734..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Right.java +++ /dev/null @@ -1,108 +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 15, 2005 - * - */ -package org.apache.poi.hssf.record.formula.functions; - -import org.apache.poi.hssf.record.formula.eval.BoolEval; -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.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; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Right extends TextFunction { - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - Eval retval = ErrorEval.VALUE_INVALID; - int index = 1; - switch (operands.length) { - default: - break; - case 2: - Eval indexEval = operands[1]; - index = evaluateAsInteger(indexEval); - if (index < 0) { - break; - } - case 1: - ValueEval veval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol); - String str = null; - if (veval instanceof StringEval) { - StringEval stringEval = (StringEval) veval; - str = stringEval.getStringValue(); - } - else if (veval instanceof BoolEval) { - BoolEval beval = (BoolEval) veval; - str = beval.getBooleanValue() ? "TRUE" : "FALSE"; - } - else if (veval instanceof NumberEval) { - NumberEval neval = (NumberEval) veval; - str = neval.getStringValue(); - } - if (null != str) { - int strlen = str.length(); - str = str.substring(Math.max(0, strlen-index)); - retval = new StringEval(str); - } - } - return retval; - } - - protected int evaluateAsInteger(Eval eval) { - int numval = -1; - if (eval instanceof NumberEval) { - NumberEval neval = (NumberEval) eval; - double d = neval.getNumberValue(); - numval = (int) d; - } - else if (eval instanceof StringEval) { - StringEval seval = (StringEval) eval; - String s = seval.getStringValue(); - try { - double d = Double.parseDouble(s); - numval = (int) d; - } - catch (Exception e) { - } - } - else if (eval instanceof BoolEval) { - BoolEval beval = (BoolEval) eval; - numval = beval.getBooleanValue() ? 1 : 0; - } - else if (eval instanceof RefEval) { - numval = evaluateAsInteger(xlateRefEval((RefEval) eval)); - } - return numval; - } - - protected Eval xlateRefEval(RefEval reval) { - Eval retval = reval.getInnerValueEval(); - - if (retval instanceof RefEval) { - retval = xlateRefEval((RefEval) retval); - } - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Round.java b/src/java/org/apache/poi/hssf/record/formula/functions/Round.java deleted file mode 100644 index db4f1db0ee..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Round.java +++ /dev/null @@ -1,83 +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 15, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -public class Round extends NumericFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double d0 = 0; - double d1 = 0; - ValueEval retval = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 2: - ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d0 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - - if (retval == null) { - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d1 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - } - } - - if (retval == null) { - double d; - if (d0 > Integer.MAX_VALUE) { - d = (Double.isNaN(d0) || Double.isInfinite(d0)) - ? Double.NaN - : 0; - } - else { - d = MathX.round(d0, (int) d1); - } - retval = (Double.isNaN(d) || Double.isInfinite(d)) ? (ValueEval) ErrorEval.VALUE_INVALID : new NumberEval(d); - } - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Rounddown.java b/src/java/org/apache/poi/hssf/record/formula/functions/Rounddown.java deleted file mode 100644 index 13522294fd..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Rounddown.java +++ /dev/null @@ -1,86 +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 15, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -public class Rounddown extends NumericFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double d0 = 0; - double d1 = 0; - ValueEval retval = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 2: - ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if(ve instanceof ErrorEval) { - return ve; - } - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d0 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - - if (retval == null) { - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d1 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - } - } - - if (retval == null) { - double d; - if (d0 > Integer.MAX_VALUE) { - d = (Double.isInfinite(d0)) - ? Double.NaN - : 0; - } - else { - d = MathX.roundDown(d0, (int) d1); - } - retval = (Double.isNaN(d) || Double.isInfinite(d)) ? (ValueEval) ErrorEval.VALUE_INVALID : new NumberEval(d); - } - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Roundup.java b/src/java/org/apache/poi/hssf/record/formula/functions/Roundup.java deleted file mode 100644 index 4dae76d981..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Roundup.java +++ /dev/null @@ -1,88 +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 15, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -public class Roundup extends NumericFunction { - - public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { - double d0 = 0; - double d1 = 0; - ValueEval retval = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 2: - ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); - if(ve instanceof ErrorEval) { - return ve; - } - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d0 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - - if (retval == null) { - ve = singleOperandEvaluate(operands[1], srcRow, srcCol); - if (ve instanceof NumericValueEval) { - NumericValueEval ne = (NumericValueEval) ve; - d1 = ne.getNumberValue(); - } - else if (ve instanceof BlankEval) { - // do nothing - } - else { - retval = ErrorEval.NUM_ERROR; - } - } - } - - if (retval == null) { - double d; - if (d0 > Integer.MAX_VALUE) { - d = (Double.isNaN(d0)) - ? Double.NaN - : 0; - } - else { - d = MathX.roundUp(d0, (int) d1); - } - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Small.java b/src/java/org/apache/poi/hssf/record/formula/functions/Small.java deleted file mode 100644 index 9fcdc1045e..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Small.java +++ /dev/null @@ -1,81 +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 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Small extends MultiOperandNumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) (0 - | ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED - //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED - //| ValueEvalToNumericXlator.BLANK_IS_PARSED - )); - - /** - * this is the default impl for the factory method getXlator - * of the super class NumericFunction. Subclasses can override this method - * if they desire to return a different ValueEvalToNumericXlator instance - * than the default. - */ - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] ops = getNumberArray(operands, srcCellRow, srcCellCol); - if (ops == null || ops.length < 2) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double[] values = new double[ops.length-1]; - int k = (int) ops[ops.length-1]; - System.arraycopy(ops, 0, values, 0, values.length); - double d = StatsLib.kthSmallest(values, k); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/StatsLib.java b/src/java/org/apache/poi/hssf/record/formula/functions/StatsLib.java index 8ebccfd950..e78f38caaa 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/StatsLib.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/StatsLib.java @@ -60,36 +60,6 @@ public final class StatsLib { return r; } - /** - * 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 mode(double[] v) { - double r = Double.NaN; - - // very naive impl, may need to be optimized - if (v!=null && v.length > 1) { - 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]; - } - } - r = (maxc > 1) ? maxv : Double.NaN; // "no-dups" check - } - return r; - } public static double median(double[] v) { double r = Double.NaN; diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Stdev.java b/src/java/org/apache/poi/hssf/record/formula/functions/Stdev.java deleted file mode 100644 index 7995e66c34..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Stdev.java +++ /dev/null @@ -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. -*/ -/* - * Created on May 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Stdev extends MultiOperandNumericFunction { - - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - )); - - /** - * this is the default impl for the factory method getXlator - * of the super class NumericFunction. Subclasses can override this method - * if they desire to return a different ValueEvalToNumericXlator instance - * than the default. - */ - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = StatsLib.stdev(values); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java b/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java index 9d2e9ce361..b00d7510e3 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java @@ -1,30 +1,26 @@ -/* -* 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 15, 2005 - * - */ +/* ==================================================================== + 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.Eval; -import org.apache.poi.hssf.record.formula.eval.NumericValueEval; +import org.apache.poi.hssf.record.formula.eval.EvaluationException; import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.StringValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; /** @@ -32,86 +28,75 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; * Substitutes text in a text string with new text, some number of times. * @author Manda Wilson < wilson at c bio dot msk cc dot org > */ -public class Substitute extends TextFunction { - private static final int REPLACE_ALL = -1; - - /** - *Substitutes text in a text string with new text, some number of times. - * - * @see org.apache.poi.hssf.record.formula.eval.Eval - */ - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - Eval retval = null; - String oldStr = null; - String searchStr = null; - String newStr = null; - int numToReplace = REPLACE_ALL; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - case 4: - ValueEval fourthveval = singleOperandEvaluate(operands[3], srcCellRow, srcCellCol); - if (fourthveval instanceof NumericValueEval) { - NumericValueEval numToReplaceEval = (NumericValueEval) fourthveval; - // NOTE: it is safe to cast to int here - // because in Excel =SUBSTITUTE("teststr","t","T",1.9) - // returns Teststr - // so 1.9 must be truncated to 1 - numToReplace = (int) numToReplaceEval.getNumberValue(); - } else { - retval = ErrorEval.VALUE_INVALID; - } - case 3: - // first operand is text string containing characters to replace - // second operand is text to find - // third operand is replacement text - ValueEval firstveval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol); - ValueEval secondveval = singleOperandEvaluate(operands[1], srcCellRow, srcCellCol); - ValueEval thirdveval = singleOperandEvaluate(operands[2], srcCellRow, srcCellCol); - if (firstveval instanceof StringValueEval - && secondveval instanceof StringValueEval - && thirdveval instanceof StringValueEval) { - - StringValueEval oldStrEval = (StringValueEval) firstveval; - oldStr = oldStrEval.getStringValue(); - - StringValueEval searchStrEval = (StringValueEval) secondveval; - searchStr = searchStrEval.getStringValue(); - - StringValueEval newStrEval = (StringValueEval) thirdveval; - newStr = newStrEval.getStringValue(); - } else { - retval = ErrorEval.VALUE_INVALID; - } - } - - if (retval == null) { - if (numToReplace != REPLACE_ALL && numToReplace < 1) { - retval = ErrorEval.VALUE_INVALID; - } else if (searchStr.length() == 0) { - retval = new StringEval(oldStr); - } else { - StringBuffer strBuff = new StringBuffer(); - int startIndex = 0; - int nextMatch = -1; - for (int leftToReplace = numToReplace; - (leftToReplace > 0 || numToReplace == REPLACE_ALL) - && (nextMatch = oldStr.indexOf(searchStr, startIndex)) != -1; - leftToReplace--) { - // store everything from end of last match to start of this match - strBuff.append(oldStr.substring(startIndex, nextMatch)); - strBuff.append(newStr); - startIndex = nextMatch + searchStr.length(); +public final class Substitute extends TextFunction { + + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + if (args.length < 3 || args.length > 4) { + return ErrorEval.VALUE_INVALID; + } + + String oldStr = evaluateStringArg(args[0], srcCellRow, srcCellCol); + String searchStr = evaluateStringArg(args[1], srcCellRow, srcCellCol); + String newStr = evaluateStringArg(args[2], srcCellRow, srcCellCol); + + String result; + switch (args.length) { + default: + throw new IllegalStateException("Cannot happen"); + case 4: + int instanceNumber = evaluateIntArg(args[3], srcCellRow, srcCellCol); + if (instanceNumber < 1) { + return ErrorEval.VALUE_INVALID; } + result = replaceOneOccurrence(oldStr, searchStr, newStr, instanceNumber); + break; + case 3: + result = replaceAllOccurrences(oldStr, searchStr, newStr); + } + return new StringEval(result); + } + + private static String replaceAllOccurrences(String oldStr, String searchStr, String newStr) { + StringBuffer sb = new StringBuffer(); + int startIndex = 0; + int nextMatch = -1; + while (true) { + nextMatch = oldStr.indexOf(searchStr, startIndex); + if (nextMatch < 0) { // store everything from end of last match to end of string - if (startIndex < oldStr.length()) { - strBuff.append(oldStr.substring(startIndex)); - } - retval = new StringEval(strBuff.toString()); + sb.append(oldStr.substring(startIndex)); + return sb.toString(); + } + // store everything from end of last match to start of this match + sb.append(oldStr.substring(startIndex, nextMatch)); + sb.append(newStr); + startIndex = nextMatch + searchStr.length(); + } + } + + private static String replaceOneOccurrence(String oldStr, String searchStr, String newStr, int instanceNumber) { + if (searchStr.length() < 1) { + return oldStr; + } + int startIndex = 0; + int nextMatch = -1; + int count=0; + while (true) { + nextMatch = oldStr.indexOf(searchStr, startIndex); + if (nextMatch < 0) { + // not enough occurrences found - leave unchanged + return oldStr; + } + count++; + if (count == instanceNumber) { + StringBuffer sb = new StringBuffer(oldStr.length() + newStr.length()); + sb.append(oldStr.substring(0, nextMatch)); + sb.append(newStr); + sb.append(oldStr.substring(nextMatch + searchStr.length())); + return sb.toString(); } - } - return retval; - } - + startIndex = nextMatch + searchStr.length(); + } + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Sum.java b/src/java/org/apache/poi/hssf/record/formula/functions/Sum.java deleted file mode 100644 index d3dcd9b202..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Sum.java +++ /dev/null @@ -1,68 +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 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Sum extends MultiOperandNumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - )); - - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = values.length > 0 ? MathX.sum(values) : 0; - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Sumsq.java b/src/java/org/apache/poi/hssf/record/formula/functions/Sumsq.java deleted file mode 100644 index b74b4161ac..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Sumsq.java +++ /dev/null @@ -1,70 +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 15, 2005 - * - */ -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.Eval; -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.eval.ValueEvalToNumericXlator; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Sumsq extends MultiOperandNumericFunction { - private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = - new ValueEvalToNumericXlator((short) ( - ValueEvalToNumericXlator.BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED - | ValueEvalToNumericXlator.STRING_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED - //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED - //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE - //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED - //| ValueEvalToNumericXlator.BLANK_IS_PARSED - )); - - protected ValueEvalToNumericXlator getXlator() { - return DEFAULT_NUM_XLATOR; - } - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - double[] values = getNumberArray(operands, srcCellRow, srcCellCol); - if (values == null) { - retval = ErrorEval.VALUE_INVALID; - } - else { - double d = MathX.sumsq(values); - retval = (Double.isNaN(d) || Double.isInfinite(d)) - ? (ValueEval) ErrorEval.NUM_ERROR - : new NumberEval(d); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java b/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java index 9da1776127..df7db7ad32 100644 --- a/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java +++ b/src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java @@ -1,31 +1,29 @@ -/* -* 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 22, 2005 - * - */ +/* ==================================================================== + 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.Eval; -import org.apache.poi.hssf.record.formula.eval.RefEval; -import org.apache.poi.hssf.record.formula.eval.StringValueEval; +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; /** @@ -33,75 +31,164 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; * */ public abstract class TextFunction implements Function { - - protected static final String EMPTY_STRING = ""; - - protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) { - ValueEval retval; - if (eval instanceof AreaEval) { - AreaEval ae = (AreaEval) eval; - if (ae.contains(srcRow, srcCol)) { // circular ref! - retval = ErrorEval.CIRCULAR_REF_ERROR; - } - else if (ae.isRow()) { - if (ae.containsColumn(srcCol)) { - ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol); - retval = attemptXlateToText(ve); - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else if (ae.isColumn()) { - if (ae.containsRow(srcRow)) { - ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn()); - retval = attemptXlateToText(ve); - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else { - retval = attemptXlateToText((ValueEval) eval); - } - return retval; - } - - - /** - * converts from Different ValueEval types to StringEval. - * Note: AreaEvals are not handled, if arg is an AreaEval, - * the returned value is ErrorEval.VALUE_INVALID - * @param ve - */ - protected ValueEval attemptXlateToText(ValueEval ve) { - ValueEval retval; - if (ve instanceof StringValueEval) { - retval = ve; - } - else if (ve instanceof RefEval) { - RefEval re = (RefEval) ve; - ValueEval ive = re.getInnerValueEval(); - if (ive instanceof StringValueEval) { - retval = ive; - } - else if (ive instanceof BlankEval) { - retval = ive; - } - else { - retval = ErrorEval.VALUE_INVALID; - } - } - else if (ve instanceof BlankEval) { - retval = ve; - } - else { - retval = ErrorEval.VALUE_INVALID; - } - return retval; - } + + protected static final String EMPTY_STRING = ""; + + protected static final String evaluateStringArg(Eval eval, int srcRow, short srcCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(eval, srcRow, srcCol); + return OperandResolver.coerceValueToString(ve); + } + protected static final int evaluateIntArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException { + ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol); + return OperandResolver.coerceValueToInt(ve); + } + + public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { + try { + return evaluateFunc(args, srcCellRow, srcCellCol); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + } + + protected abstract ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) throws EvaluationException; + + /* ---------------------------------------------------------------------- */ + + private static abstract class SingleArgTextFunc extends TextFunction { + + protected SingleArgTextFunc() { + // no fields to initialise + } + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + if (args.length != 1) { + return ErrorEval.VALUE_INVALID; + } + String arg = evaluateStringArg(args[0], srcCellRow, srcCellCol); + return evaluate(arg); + } + protected abstract ValueEval evaluate(String arg); + } + + public static final Function LEN = new SingleArgTextFunc() { + protected ValueEval evaluate(String arg) { + return new NumberEval(arg.length()); + } + }; + public static final Function LOWER = new SingleArgTextFunc() { + protected ValueEval evaluate(String arg) { + return new StringEval(arg.toLowerCase()); + } + }; + public static final Function UPPER = new SingleArgTextFunc() { + protected ValueEval evaluate(String arg) { + return new StringEval(arg.toUpperCase()); + } + }; + /** + * An implementation of the TRIM function: + * Removes leading and trailing spaces from value if evaluated operand + * value is string. + * @author Manda Wilson < wilson at c bio dot msk cc dot org > + */ + public static final Function TRIM = new SingleArgTextFunc() { + protected ValueEval evaluate(String arg) { + return new StringEval(arg.trim()); + } + }; + + /** + * An implementation of the MID function<br/> + * MID returns a specific number of + * characters from a text string, starting at the specified position.<p/> + * + * <b>Syntax<b>:<br/> <b>MID</b>(<b>text</b>, <b>start_num</b>, + * <b>num_chars</b>)<br/> + * + * @author Manda Wilson < wilson at c bio dot msk cc dot org > + */ + public static final Function MID = new TextFunction() { + + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + if (args.length != 3) { + return ErrorEval.VALUE_INVALID; + } + + String text = evaluateStringArg(args[0], srcCellRow, srcCellCol); + int startCharNum = evaluateIntArg(args[1], srcCellRow, srcCellCol); + int numChars = evaluateIntArg(args[2], srcCellRow, srcCellCol); + int startIx = startCharNum - 1; // convert to zero-based + + // Note - for start_num arg, blank/zero causes error(#VALUE!), + // but for num_chars causes empty string to be returned. + if (startIx < 0) { + return ErrorEval.VALUE_INVALID; + } + if (numChars < 0) { + return ErrorEval.VALUE_INVALID; + } + int len = text.length(); + if (numChars < 0 || startIx > len) { + return new StringEval(""); + } + int endIx = Math.min(startIx + numChars, len); + String result = text.substring(startIx, endIx); + return new StringEval(result); + } + }; + + private static final class LeftRight extends TextFunction { + + private final boolean _isLeft; + protected LeftRight(boolean isLeft) { + _isLeft = isLeft; + } + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + if (args.length != 2) { + return ErrorEval.VALUE_INVALID; + } + String arg = evaluateStringArg(args[0], srcCellRow, srcCellCol); + int index = evaluateIntArg(args[1], srcCellRow, srcCellCol); + + String result; + if (_isLeft) { + result = arg.substring(0, Math.min(arg.length(), index)); + } else { + result = arg.substring(Math.max(0, arg.length()-index)); + } + return new StringEval(result); + } + } + + public static final Function LEFT = new LeftRight(true); + public static final Function RIGHT = new LeftRight(false); + + public static final Function CONCATENATE = new TextFunction() { + + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + StringBuffer sb = new StringBuffer(); + for (int i=0, iSize=args.length; i<iSize; i++) { + sb.append(evaluateStringArg(args[i], srcCellRow, srcCellCol)); + } + return new StringEval(sb.toString()); + } + }; + + public static final Function EXACT = new TextFunction() { + + protected ValueEval evaluateFunc(Eval[] args, int srcCellRow, short srcCellCol) + throws EvaluationException { + if (args.length != 2) { + return ErrorEval.VALUE_INVALID; + } + + String s0 = evaluateStringArg(args[0], srcCellRow, srcCellCol); + String s1 = evaluateStringArg(args[1], srcCellRow, srcCellCol); + return BoolEval.valueOf(s0.equals(s1)); + } + }; } diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Trim.java b/src/java/org/apache/poi/hssf/record/formula/functions/Trim.java deleted file mode 100644 index 87e29ee34d..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Trim.java +++ /dev/null @@ -1,53 +0,0 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -package org.apache.poi.hssf.record.formula.functions; - -import org.apache.poi.hssf.record.formula.eval.ErrorEval; -import org.apache.poi.hssf.record.formula.eval.Eval; -import org.apache.poi.hssf.record.formula.eval.EvaluationException; -import org.apache.poi.hssf.record.formula.eval.OperandResolver; -import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * An implementation of the TRIM function: - * Removes leading and trailing spaces from value if evaluated operand - * value is string. - * @author Manda Wilson < wilson at c bio dot msk cc dot org > - */ -public final class Trim extends TextFunction { - - public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { - - if(args.length != 1) { - return ErrorEval.VALUE_INVALID; - } - - try { - ValueEval veval = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol); - - String str = OperandResolver.coerceValueToString(veval); - str = str.trim(); - if(str.length() < 1) { - return StringEval.EMPTY_INSTANCE; - } - return new StringEval(str); - } catch (EvaluationException e) { - return e.getErrorEval(); - } - } -} diff --git a/src/java/org/apache/poi/hssf/record/formula/functions/Upper.java b/src/java/org/apache/poi/hssf/record/formula/functions/Upper.java deleted file mode 100644 index 5146a6641c..0000000000 --- a/src/java/org/apache/poi/hssf/record/formula/functions/Upper.java +++ /dev/null @@ -1,65 +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 15, 2005 - * - */ -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.Eval; -import org.apache.poi.hssf.record.formula.eval.StringEval; -import org.apache.poi.hssf.record.formula.eval.StringValueEval; -import org.apache.poi.hssf.record.formula.eval.ValueEval; - -/** - * @author Amol S. Deshmukh < amolweb at ya hoo dot com > - * - */ -public class Upper extends TextFunction { - - - public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { - ValueEval retval = null; - String s = null; - - switch (operands.length) { - default: - retval = ErrorEval.VALUE_INVALID; - break; - case 1: - ValueEval ve = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol); - if (ve instanceof StringValueEval) { - StringValueEval sve = (StringValueEval) ve; - s = sve.getStringValue(); - } - else if (ve instanceof BlankEval) {} - else { - retval = ErrorEval.VALUE_INVALID; - break; - } - } - - if (retval == null) { - s = (s == null) ? EMPTY_STRING : s; - retval = new StringEval(s.toUpperCase()); - } - - return retval; - } -} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java index 21fb5707e6..1f88cce2bd 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java @@ -23,6 +23,7 @@ import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import org.apache.poi.hssf.model.FormulaParser; import org.apache.poi.hssf.model.Sheet; @@ -60,8 +61,8 @@ import org.apache.poi.ss.usermodel.RichTextString; * Cells can be numeric, formula-based or string-based (text). The cell type * specifies this. String cells cannot conatin numbers and numeric cells cannot * contain strings (at least according to our model). Client apps should do the - * conversions themselves. Formula cells have the formula string, as well as - * the formula result, which can be numeric or string. + * conversions themselves. Formula cells have the formula string, as well as + * the formula result, which can be numeric or string. * <p> * Cells should have their number (0 based) before being added to a row. Only * cells that have values should be added. @@ -86,14 +87,15 @@ public class HSSFCell implements Cell { public final static int CELL_TYPE_BOOLEAN = 4; /** Error Cell type (5) @see #setCellType(int) @see #getCellType() */ public final static int CELL_TYPE_ERROR = 5; - + public final static short ENCODING_UNCHANGED = -1; public final static short ENCODING_COMPRESSED_UNICODE = 0; public final static short ENCODING_UTF_16 = 1; + + private final HSSFWorkbook book; + private final HSSFSheet sheet; private int cellType; private HSSFRichTextString stringValue; - private HSSFWorkbook book; - private Sheet sheet; private CellValueRecordInterface record; private HSSFComment comment; @@ -113,7 +115,7 @@ public class HSSFCell implements Cell { * * @see org.apache.poi.hssf.usermodel.HSSFRow#createCell(short) */ - protected HSSFCell(HSSFWorkbook book, Sheet sheet, int row, short col) + protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, int row, short col) { checkBounds(col); stringValue = null; @@ -123,9 +125,12 @@ public class HSSFCell implements Cell { // Relying on the fact that by default the cellType is set to 0 which // is different to CELL_TYPE_BLANK hence the following method call correctly // creates a new blank cell. - short xfindex = sheet.getXFIndexForColAt(col); + short xfindex = sheet.getSheet().getXFIndexForColAt(col); setCellType(CELL_TYPE_BLANK, false, row, col,xfindex); } + public HSSFSheet getSheet() { + return sheet; + } /** * Creates new Cell - Should only be called by HSSFRow. This creates a cell @@ -140,7 +145,7 @@ public class HSSFCell implements Cell { * Type of cell * @see org.apache.poi.hssf.usermodel.HSSFRow#createCell(short,int) */ - protected HSSFCell(HSSFWorkbook book, Sheet sheet, int row, short col, + protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, int row, short col, int type) { checkBounds(col); @@ -148,8 +153,8 @@ public class HSSFCell implements Cell { stringValue = null; this.book = book; this.sheet = sheet; - - short xfindex = sheet.getXFIndexForColAt(col); + + short xfindex = sheet.getSheet().getXFIndexForColAt(col); setCellType(type,false,row,col,xfindex); } @@ -161,7 +166,7 @@ public class HSSFCell implements Cell { * @param sheet - Sheet record of the sheet containing this cell * @param cval - the Cell Value Record we wish to represent */ - protected HSSFCell(HSSFWorkbook book, Sheet sheet, CellValueRecordInterface cval) { + protected HSSFCell(HSSFWorkbook book, HSSFSheet sheet, CellValueRecordInterface cval) { record = cval; cellType = determineType(cval); stringValue = null; @@ -190,10 +195,10 @@ public class HSSFCell implements Cell { * used internally -- given a cell value record, figure out its type */ private static int determineType(CellValueRecordInterface cval) { - if (cval instanceof FormulaRecordAggregate) { - return HSSFCell.CELL_TYPE_FORMULA; - } - // all others are plain BIFF records + if (cval instanceof FormulaRecordAggregate) { + return HSSFCell.CELL_TYPE_FORMULA; + } + // all others are plain BIFF records Record record = ( Record ) cval; switch (record.getSid()) { @@ -209,13 +214,13 @@ public class HSSFCell implements Cell { } throw new RuntimeException("Bad cell value rec (" + cval.getClass().getName() + ")"); } - + /** * Returns the Workbook that this Cell is bound to * @return */ protected Workbook getBoundWorkbook() { - return book.getWorkbook(); + return book.getWorkbook(); } /** @@ -233,7 +238,7 @@ public class HSSFCell implements Cell { { record.setColumn(num); } - + /** * Updates the cell record's idea of what * column it belongs in (0 based) @@ -241,7 +246,7 @@ public class HSSFCell implements Cell { */ protected void updateCellNum(short num) { - record.setColumn(num); + record.setColumn(num); } /** @@ -295,7 +300,7 @@ public class HSSFCell implements Cell { FormulaRecordAggregate frec; if (cellType != this.cellType) { - frec = sheet.createFormula(row, col); + frec = sheet.getSheet().createFormula(row, col); } else { frec = (FormulaRecordAggregate) record; frec.setRow(row); @@ -421,17 +426,17 @@ public class HSSFCell implements Cell { errRec.setColumn(col); if (setValue) { - errRec.setValue(getErrorCellValue()); + errRec.setValue((byte)HSSFErrorConstants.ERROR_VALUE); } errRec.setXFIndex(styleIndex); errRec.setRow(row); record = errRec; break; } - if (cellType != this.cellType && + if (cellType != this.cellType && this.cellType!=-1 ) // Special Value to indicate an uninitialized Cell { - sheet.replaceValueRecord(record); + sheet.getSheet().replaceValueRecord(record); } this.cellType = cellType; } @@ -457,21 +462,20 @@ public class HSSFCell implements Cell { * precalculated value, for numerics we'll set its value. For other types we * will change the cell to a numeric cell and set its value. */ - public void setCellValue(double value) - { + public void setCellValue(double value) { int row=record.getRow(); short col=record.getColumn(); short styleIndex=record.getXFIndex(); - if ((cellType != CELL_TYPE_NUMERIC) && (cellType != CELL_TYPE_FORMULA)) - { - setCellType(CELL_TYPE_NUMERIC, false, row, col, styleIndex); - } - - // Save into the appropriate record - if(record instanceof FormulaRecordAggregate) { - (( FormulaRecordAggregate ) record).getFormulaRecord().setValue(value); - } else { - (( NumberRecord ) record).setValue(value); + + switch (cellType) { + default: + setCellType(CELL_TYPE_NUMERIC, false, row, col, styleIndex); + case CELL_TYPE_ERROR: + (( NumberRecord ) record).setValue(value); + break; + case CELL_TYPE_FORMULA: + ((FormulaRecordAggregate)record).getFormulaRecord().setValue(value); + break; } } @@ -491,7 +495,7 @@ public class HSSFCell implements Cell { /** * set a date value for the cell. Excel treats dates as numeric so you will need to format the cell as * a date. - * + * * This will set the cell value based on the Calendar's timezone. As Excel * does not support timezones this means that both 20:00+03:00 and * 20:00-03:00 will be reported as the same value (20:00) even that there @@ -547,31 +551,20 @@ public class HSSFCell implements Cell { return; } if (cellType == CELL_TYPE_FORMULA) { - // Set the 'pre-evaluated result' for the formula + // Set the 'pre-evaluated result' for the formula // note - formulas do not preserve text formatting. FormulaRecordAggregate fr = (FormulaRecordAggregate) record; - - // Save the string into a String Record, creating - // one if required - StringRecord sr = fr.getStringRecord(); - if(sr == null) { - // Wasn't a string before, need a new one - sr = new StringRecord(); - fr.setStringRecord(sr); - } - - // Save, loosing the formatting - sr.setString(hvalue.getString()); + fr.setCachedStringResult(hvalue.getString()); // Update our local cache to the un-formatted version - stringValue = new HSSFRichTextString(sr.getString()); - + stringValue = new HSSFRichTextString(value.getString()); + // All done return; } // If we get here, we're not dealing with a formula, // so handle things as a normal rich text cell - + if (cellType != CELL_TYPE_STRING) { setCellType(CELL_TYPE_STRING, false, row, col, styleIndex); } @@ -599,95 +592,95 @@ public class HSSFCell implements Cell { FormulaRecord frec = rec.getFormulaRecord(); frec.setOptions((short) 2); frec.setValue(0); - + //only set to default if there is no extended format index already set if (rec.getXFIndex() == (short)0) { - rec.setXFIndex((short) 0x0f); - } + rec.setXFIndex((short) 0x0f); + } Ptg[] ptgs = FormulaParser.parse(formula, book); frec.setParsedExpression(ptgs); } + /* package */ void setFormulaOnly(Ptg[] ptgs) { + if (ptgs == null) { + throw new IllegalArgumentException("ptgs must not be null"); + } + ((FormulaRecordAggregate)record).getFormulaRecord().setParsedExpression(ptgs); + } public String getCellFormula() { return FormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression()); } + /** + * Used to help format error messages + */ + private static String getCellTypeName(int cellTypeCode) { + switch (cellTypeCode) { + case CELL_TYPE_BLANK: return "blank"; + case CELL_TYPE_STRING: return "text"; + case CELL_TYPE_BOOLEAN: return "boolean"; + case CELL_TYPE_ERROR: return "error"; + case CELL_TYPE_NUMERIC: return "numeric"; + case CELL_TYPE_FORMULA: return "formula"; + } + return "#unknown cell type (" + cellTypeCode + ")#"; + } + + private static RuntimeException typeMismatch(int expectedTypeCode, int actualTypeCode, boolean isFormulaCell) { + String msg = "Cannot get a " + + getCellTypeName(expectedTypeCode) + " value from a " + + getCellTypeName(actualTypeCode) + " " + (isFormulaCell ? "formula " : "") + "cell"; + return new IllegalStateException(msg); + } + private static void checkFormulaCachedValueType(int expectedTypeCode, FormulaRecord fr) { + int cachedValueType = fr.getCachedResultType(); + if (cachedValueType != expectedTypeCode) { + throw typeMismatch(expectedTypeCode, cachedValueType, true); + } + } /** - * Get the value of the cell as a number. + * Get the value of the cell as a number. * For strings we throw an exception. * For blank cells we return a 0. * See {@link HSSFDataFormatter} for turning this * number into a string similar to that which - * Excel would render this number as. + * Excel would render this number as. */ - public double getNumericCellValue() - { - if (cellType == CELL_TYPE_BLANK) - { - return 0; - } - if (cellType == CELL_TYPE_STRING) - { - throw new NumberFormatException( - "You cannot get a numeric value from a String based cell"); - } - if (cellType == CELL_TYPE_BOOLEAN) - { - throw new NumberFormatException( - "You cannot get a numeric value from a boolean cell"); - } - if (cellType == CELL_TYPE_ERROR) - { - throw new NumberFormatException( - "You cannot get a numeric value from an error cell"); - } - if(cellType == CELL_TYPE_NUMERIC) - { - return ((NumberRecord)record).getValue(); - } - if(cellType == CELL_TYPE_FORMULA) - { - return ((FormulaRecordAggregate)record).getFormulaRecord().getValue(); + public double getNumericCellValue() { + + switch(cellType) { + case CELL_TYPE_BLANK: + return 0.0; + case CELL_TYPE_NUMERIC: + return ((NumberRecord)record).getValue(); + default: + throw typeMismatch(CELL_TYPE_NUMERIC, cellType, false); + case CELL_TYPE_FORMULA: + break; } - throw new NumberFormatException("Unknown Record Type in Cell:"+cellType); + FormulaRecord fr = ((FormulaRecordAggregate)record).getFormulaRecord(); + checkFormulaCachedValueType(CELL_TYPE_NUMERIC, fr); + return fr.getValue(); } /** - * Get the value of the cell as a date. + * Get the value of the cell as a date. * For strings we throw an exception. * For blank cells we return a null. * See {@link HSSFDataFormatter} for formatting * this date into a string similar to how excel does. */ - public Date getDateCellValue() - { - if (cellType == CELL_TYPE_BLANK) - { + public Date getDateCellValue() { + + if (cellType == CELL_TYPE_BLANK) { return null; } - if (cellType == CELL_TYPE_STRING) - { - throw new NumberFormatException( - "You cannot get a date value from a String based cell"); - } - if (cellType == CELL_TYPE_BOOLEAN) - { - throw new NumberFormatException( - "You cannot get a date value from a boolean cell"); - } - if (cellType == CELL_TYPE_ERROR) - { - throw new NumberFormatException( - "You cannot get a date value from an error cell"); - } - double value=this.getNumericCellValue(); + double value = getNumericCellValue(); if (book.getWorkbook().isUsing1904DateWindowing()) { - return HSSFDateUtil.getJavaDate(value,true); - } - else { - return HSSFDateUtil.getJavaDate(value,false); + return HSSFDateUtil.getJavaDate(value, true); } + return HSSFDateUtil.getJavaDate(value, false); } /** @@ -708,33 +701,22 @@ public class HSSFCell implements Cell { * For blank cells we return an empty string. * For formulaCells that are not string Formulas, we return empty String */ + public HSSFRichTextString getRichStringCellValue() { - public HSSFRichTextString getRichStringCellValue() - { - if (cellType == CELL_TYPE_BLANK) - { - return new HSSFRichTextString(""); - } - if (cellType == CELL_TYPE_NUMERIC) - { - throw new NumberFormatException( - "You cannot get a string value from a numeric cell"); - } - if (cellType == CELL_TYPE_BOOLEAN) - { - throw new NumberFormatException( - "You cannot get a string value from a boolean cell"); - } - if (cellType == CELL_TYPE_ERROR) - { - throw new NumberFormatException( - "You cannot get a string value from an error cell"); - } - if (cellType == CELL_TYPE_FORMULA) - { - if (stringValue==null) return new HSSFRichTextString(""); + switch(cellType) { + case CELL_TYPE_BLANK: + return new HSSFRichTextString(""); + case CELL_TYPE_STRING: + return stringValue; + default: + throw typeMismatch(CELL_TYPE_STRING, cellType, false); + case CELL_TYPE_FORMULA: + break; } - return stringValue; + FormulaRecordAggregate fra = ((FormulaRecordAggregate)record); + checkFormulaCachedValueType(CELL_TYPE_STRING, fra.getFormulaRecord()); + String strVal = fra.getStringValue(); + return new HSSFRichTextString(strVal == null ? "" : strVal); } /** @@ -745,47 +727,56 @@ public class HSSFCell implements Cell { * will change the cell to a boolean cell and set its value. */ - public void setCellValue(boolean value) - { + public void setCellValue(boolean value) { int row=record.getRow(); short col=record.getColumn(); short styleIndex=record.getXFIndex(); - if ((cellType != CELL_TYPE_BOOLEAN ) && ( cellType != CELL_TYPE_FORMULA)) - { - setCellType(CELL_TYPE_BOOLEAN, false, row, col, styleIndex); + + switch (cellType) { + default: + setCellType(CELL_TYPE_BOOLEAN, false, row, col, styleIndex); + case CELL_TYPE_ERROR: + (( BoolErrRecord ) record).setValue(value); + break; + case CELL_TYPE_FORMULA: + ((FormulaRecordAggregate)record).getFormulaRecord().setCachedResultBoolean(value); + break; } - (( BoolErrRecord ) record).setValue(value); } /** * set a error value for the cell * - * @param value the error value to set this cell to. For formulas we'll set the - * precalculated value ??? IS THIS RIGHT??? , for errors we'll set + * @param errorCode the error value to set this cell to. For formulas we'll set the + * precalculated value , for errors we'll set * its value. For other types we will change the cell to an error * cell and set its value. */ - - public void setCellErrorValue(byte value) - { + public void setCellErrorValue(byte errorCode) { int row=record.getRow(); short col=record.getColumn(); short styleIndex=record.getXFIndex(); - if (cellType != CELL_TYPE_ERROR) { - setCellType(CELL_TYPE_ERROR, false, row, col, styleIndex); + switch (cellType) { + default: + setCellType(CELL_TYPE_ERROR, false, row, col, styleIndex); + case CELL_TYPE_ERROR: + (( BoolErrRecord ) record).setValue(errorCode); + break; + case CELL_TYPE_FORMULA: + ((FormulaRecordAggregate)record).getFormulaRecord().setCachedResultErrorCode(errorCode); + break; } - (( BoolErrRecord ) record).setValue(value); } /** * Chooses a new boolean value for the cell when its type is changing.<p/> - * - * Usually the caller is calling setCellType() with the intention of calling + * + * Usually the caller is calling setCellType() with the intention of calling * setCellValue(boolean) straight afterwards. This method only exists to give * the cell a somewhat reasonable value until the setCellValue() call (if at all). * TODO - perhaps a method like setCellTypeAndValue(int, Object) should be introduced to avoid this */ private boolean convertCellValueToBoolean() { - + switch (cellType) { case CELL_TYPE_BOOLEAN: return (( BoolErrRecord ) record).getBooleanValue(); @@ -796,11 +787,11 @@ public class HSSFCell implements Cell { // All other cases convert to false // These choices are not well justified. - case CELL_TYPE_FORMULA: + case CELL_TYPE_FORMULA: // should really evaluate, but HSSFCell can't call HSSFFormulaEvaluator case CELL_TYPE_ERROR: case CELL_TYPE_BLANK: - return false; + return false; } throw new RuntimeException("Unexpected cell type (" + cellType + ")"); } @@ -809,38 +800,39 @@ public class HSSFCell implements Cell { * get the value of the cell as a boolean. For strings, numbers, and errors, we throw an exception. * For blank cells we return a false. */ + public boolean getBooleanCellValue() { - public boolean getBooleanCellValue() - { - if (cellType == CELL_TYPE_BOOLEAN) - { - return (( BoolErrRecord ) record).getBooleanValue(); - } - if (cellType == CELL_TYPE_BLANK) - { - return false; + switch(cellType) { + case CELL_TYPE_BLANK: + return false; + case CELL_TYPE_BOOLEAN: + return (( BoolErrRecord ) record).getBooleanValue(); + default: + throw typeMismatch(CELL_TYPE_BOOLEAN, cellType, false); + case CELL_TYPE_FORMULA: + break; } - throw new NumberFormatException( - "You cannot get a boolean value from a non-boolean cell"); + FormulaRecord fr = ((FormulaRecordAggregate)record).getFormulaRecord(); + checkFormulaCachedValueType(CELL_TYPE_BOOLEAN, fr); + return fr.getCachedBooleanValue(); } /** * get the value of the cell as an error code. For strings, numbers, and booleans, we throw an exception. * For blank cells we return a 0. */ - - public byte getErrorCellValue() - { - if (cellType == CELL_TYPE_ERROR) - { - return (( BoolErrRecord ) record).getErrorValue(); - } - if (cellType == CELL_TYPE_BLANK) - { - return ( byte ) 0; + public byte getErrorCellValue() { + switch(cellType) { + case CELL_TYPE_ERROR: + return (( BoolErrRecord ) record).getErrorValue(); + default: + throw typeMismatch(CELL_TYPE_ERROR, cellType, false); + case CELL_TYPE_FORMULA: + break; } - throw new NumberFormatException( - "You cannot get an error value from a non-error cell"); + FormulaRecord fr = ((FormulaRecordAggregate)record).getFormulaRecord(); + checkFormulaCachedValueType(CELL_TYPE_ERROR, fr); + return (byte) fr.getCachedErrorValue(); } /** @@ -897,7 +889,7 @@ public class HSSFCell implements Cell { throw new RuntimeException("You cannot reference columns with an index of less then 0."); } } - + /** * Sets this cell as the active cell for the worksheet */ @@ -905,45 +897,45 @@ public class HSSFCell implements Cell { { int row=record.getRow(); short col=record.getColumn(); - this.sheet.setActiveCellRow(row); - this.sheet.setActiveCellCol(col); + this.sheet.getSheet().setActiveCellRow(row); + this.sheet.getSheet().setActiveCellCol(col); } - + /** * Returns a string representation of the cell - * - * This method returns a simple representation, + * + * This method returns a simple representation, * anthing more complex should be in user code, with - * knowledge of the semantics of the sheet being processed. - * - * Formula cells return the formula string, - * rather than the formula result. + * knowledge of the semantics of the sheet being processed. + * + * Formula cells return the formula string, + * rather than the formula result. * Dates are displayed in dd-MMM-yyyy format * Errors are displayed as #ERR<errIdx> */ public String toString() { - switch (getCellType()) { - case CELL_TYPE_BLANK: - return ""; - case CELL_TYPE_BOOLEAN: - return getBooleanCellValue()?"TRUE":"FALSE"; - case CELL_TYPE_ERROR: - return ErrorEval.getText((( BoolErrRecord ) record).getErrorValue()); - case CELL_TYPE_FORMULA: - return getCellFormula(); - case CELL_TYPE_NUMERIC: - //TODO apply the dataformat for this cell - if (HSSFDateUtil.isCellDateFormatted(this)) { - DateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy"); - return sdf.format(getDateCellValue()); - } else { - return getNumericCellValue() + ""; - } - case CELL_TYPE_STRING: - return getStringCellValue(); - default: - return "Unknown Cell Type: " + getCellType(); - } + switch (getCellType()) { + case CELL_TYPE_BLANK: + return ""; + case CELL_TYPE_BOOLEAN: + return getBooleanCellValue()?"TRUE":"FALSE"; + case CELL_TYPE_ERROR: + return ErrorEval.getText((( BoolErrRecord ) record).getErrorValue()); + case CELL_TYPE_FORMULA: + return getCellFormula(); + case CELL_TYPE_NUMERIC: + //TODO apply the dataformat for this cell + if (HSSFDateUtil.isCellDateFormatted(this)) { + DateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy"); + return sdf.format(getDateCellValue()); + } else { + return getNumericCellValue() + ""; + } + case CELL_TYPE_STRING: + return getStringCellValue(); + default: + return "Unknown Cell Type: " + getCellType(); + } } /** @@ -954,14 +946,14 @@ public class HSSFCell implements Cell { * @param comment comment associated with this cell */ public void setCellComment(Comment comment){ - if(comment == null) { - removeCellComment(); - return; - } - - this.comment = (HSSFComment) comment; - this.comment.setRow((short)record.getRow()); - this.comment.setColumn(record.getColumn()); + if(comment == null) { + removeCellComment(); + return; + } + + comment.setRow((short)record.getRow()); + comment.setColumn(record.getColumn()); + this.comment = (HSSFComment)comment; } /** @@ -971,11 +963,11 @@ public class HSSFCell implements Cell { */ public HSSFComment getCellComment(){ if (comment == null) { - comment = findCellComment(sheet, record.getRow(), record.getColumn()); + comment = findCellComment(sheet.getSheet(), record.getRow(), record.getColumn()); } return comment; } - + /** * Removes the comment for this cell, if * there is one. @@ -983,40 +975,41 @@ public class HSSFCell implements Cell { * all comments after performing this action! */ public void removeCellComment() { - HSSFComment comment = findCellComment(sheet, record.getRow(), record.getColumn()); - this.comment = null; - - if(comment == null) { - // Nothing to do - return; - } - - // Zap the underlying NoteRecord - sheet.getRecords().remove(comment.getNoteRecord()); - - // If we have a TextObjectRecord, is should - // be proceeed by: - // MSODRAWING with container - // OBJ - // MSODRAWING with EscherTextboxRecord - if(comment.getTextObjectRecord() != null) { - TextObjectRecord txo = comment.getTextObjectRecord(); - int txoAt = sheet.getRecords().indexOf(txo); - - if(sheet.getRecords().get(txoAt-3) instanceof DrawingRecord && - sheet.getRecords().get(txoAt-2) instanceof ObjRecord && - sheet.getRecords().get(txoAt-1) instanceof DrawingRecord) { - // Zap these, in reverse order - sheet.getRecords().remove(txoAt-1); - sheet.getRecords().remove(txoAt-2); - sheet.getRecords().remove(txoAt-3); - } else { - throw new IllegalStateException("Found the wrong records before the TextObjectRecord, can't remove comment"); - } - - // Now remove the text record - sheet.getRecords().remove(txo); - } + HSSFComment comment = findCellComment(sheet.getSheet(), record.getRow(), record.getColumn()); + this.comment = null; + + if(comment == null) { + // Nothing to do + return; + } + + // Zap the underlying NoteRecord + List sheetRecords = sheet.getSheet().getRecords(); + sheetRecords.remove(comment.getNoteRecord()); + + // If we have a TextObjectRecord, is should + // be proceeed by: + // MSODRAWING with container + // OBJ + // MSODRAWING with EscherTextboxRecord + if(comment.getTextObjectRecord() != null) { + TextObjectRecord txo = comment.getTextObjectRecord(); + int txoAt = sheetRecords.indexOf(txo); + + if(sheetRecords.get(txoAt-3) instanceof DrawingRecord && + sheetRecords.get(txoAt-2) instanceof ObjRecord && + sheetRecords.get(txoAt-1) instanceof DrawingRecord) { + // Zap these, in reverse order + sheetRecords.remove(txoAt-1); + sheetRecords.remove(txoAt-2); + sheetRecords.remove(txoAt-3); + } else { + throw new IllegalStateException("Found the wrong records before the TextObjectRecord, can't remove comment"); + } + + // Now remove the text record + sheetRecords.remove(txo); + } } /** @@ -1070,7 +1063,7 @@ public class HSSFCell implements Cell { * @return hyperlink associated with this cell or null if not found */ public HSSFHyperlink getHyperlink(){ - for (Iterator it = sheet.getRecords().iterator(); it.hasNext(); ) { + for (Iterator it = sheet.getSheet().getRecords().iterator(); it.hasNext(); ) { RecordBase rec = (RecordBase) it.next(); if (rec instanceof HyperlinkRecord){ HyperlinkRecord link = (HyperlinkRecord)rec; @@ -1108,7 +1101,19 @@ public class HSSFCell implements Cell { break; } - int eofLoc = sheet.findFirstRecordLocBySid( EOFRecord.sid ); - sheet.getRecords().add( eofLoc, link.record ); + int eofLoc = sheet.getSheet().findFirstRecordLocBySid( EOFRecord.sid ); + sheet.getSheet().getRecords().add( eofLoc, link.record ); + } + /** + * Only valid for formula cells + * @return one of ({@link #CELL_TYPE_NUMERIC}, {@link #CELL_TYPE_STRING}, + * {@link #CELL_TYPE_BOOLEAN}, {@link #CELL_TYPE_ERROR}) depending + * on the cached value of the formula + */ + public int getCachedFormulaResultType() { + if (this.cellType != CELL_TYPE_FORMULA) { + throw new IllegalStateException("Only formula cells have cached results"); + } + return ((FormulaRecordAggregate)record).getFormulaRecord().getCachedResultType(); } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java index 4fb31606ea..64ec2fd211 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java @@ -31,6 +31,9 @@ public class HSSFFormulaEvaluator extends FormulaEvaluator { public HSSFFormulaEvaluator(HSSFSheet sheet, HSSFWorkbook workbook) { super(sheet, workbook); } + public HSSFFormulaEvaluator(HSSFWorkbook workbook) { + super(workbook); + } /** * Returns an underlying FormulaParser, for the specified diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java index 4e351e6ade..641c07467c 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java @@ -20,7 +20,6 @@ package org.apache.poi.hssf.usermodel; import java.util.Iterator; import java.util.NoSuchElementException; -import org.apache.poi.hssf.model.Sheet; import org.apache.poi.hssf.record.CellValueRecordInterface; import org.apache.poi.hssf.record.RowRecord; import org.apache.poi.ss.usermodel.Cell; @@ -55,7 +54,7 @@ public final class HSSFRow implements Comparable, Row { /** * reference to containing Sheet */ - private Sheet sheet; + private HSSFSheet sheet; /** * Creates new HSSFRow from scratch. Only HSSFSheet should do this. @@ -65,7 +64,7 @@ public final class HSSFRow implements Comparable, Row { * @param rowNum the row number of this row (0 based) * @see org.apache.poi.hssf.usermodel.HSSFSheet#createRow(int) */ - HSSFRow(HSSFWorkbook book, Sheet sheet, int rowNum) + HSSFRow(HSSFWorkbook book, HSSFSheet sheet, int rowNum) { this.rowNum = rowNum; this.book = book; @@ -84,7 +83,7 @@ public final class HSSFRow implements Comparable, Row { * @param record the low level api object this row should represent * @see org.apache.poi.hssf.usermodel.HSSFSheet#createRow(int) */ - HSSFRow(HSSFWorkbook book, Sheet sheet, RowRecord record) + HSSFRow(HSSFWorkbook book, HSSFSheet sheet, RowRecord record) { this.book = book; this.sheet = sheet; @@ -139,7 +138,7 @@ public final class HSSFRow implements Comparable, Row { HSSFCell cell = new HSSFCell(book, sheet, getRowNum(), shortCellNum, type); addCell(cell); - sheet.addValueRecord(getRowNum(), cell.getCellValueRecord()); + sheet.getSheet().addValueRecord(getRowNum(), cell.getCellValueRecord()); return cell; } @@ -166,7 +165,7 @@ public final class HSSFRow implements Comparable, Row { if(alsoRemoveRecords) { CellValueRecordInterface cval = cell.getCellValueRecord(); - sheet.removeValueRecord(getRowNum(), cval); + sheet.getSheet().removeValueRecord(getRowNum(), cval); } if (cell.getCellNum()+1 == row.getLastCol()) { @@ -465,7 +464,7 @@ public final class HSSFRow implements Comparable, Row { //The low-order 15 bits contain the row height. //The 0x8000 bit indicates that the row is standard height (optional) - if ((height & 0x8000) != 0) height = sheet.getDefaultRowHeight(); + if ((height & 0x8000) != 0) height = sheet.getSheet().getDefaultRowHeight(); else height &= 0x7FFF; return height; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 6670b20414..4d6a1f7253 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -207,7 +207,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet */ public HSSFRow createRow(int rownum) { - HSSFRow row = new HSSFRow(workbook, sheet, rownum); + HSSFRow row = new HSSFRow(workbook, this, rownum); addRow(row, true); return row; @@ -222,7 +222,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet private HSSFRow createRowFromRecord(RowRecord row) { - HSSFRow hrow = new HSSFRow(workbook, sheet, row); + HSSFRow hrow = new HSSFRow(workbook, this, row); addRow(hrow, false); return hrow; @@ -390,7 +390,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet for(int index=0; index<records.size(); index++) { if(records.get(index) instanceof DVRecord) { - dvRecords.add(records.get(index)); + dvRecords.add(records.get(index)); } } return dvRecords; @@ -1301,9 +1301,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet // If any references were changed, then // re-create the formula string if(changed) { - c.setCellFormula( - FormulaParser.toFormulaString(workbook, ptgs) - ); + c.setFormulaOnly(ptgs); } } } @@ -1602,14 +1600,32 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet } /** + * @deprecated (Sep 2008) use {@link #setColumnGroupCollapsed(int, boolean)} + */ + public void setColumnGroupCollapsed(short columnNumber, boolean collapsed) { + setColumnGroupCollapsed(columnNumber & 0xFFFF, collapsed); + } + /** + * @deprecated (Sep 2008) use {@link #groupColumn(int, int)} + */ + public void groupColumn(short fromColumn, short toColumn) { + groupColumn(fromColumn & 0xFFFF, toColumn & 0xFFFF); + } + /** + * @deprecated (Sep 2008) use {@link #ungroupColumn(int, int)} + */ + public void ungroupColumn(short fromColumn, short toColumn) { + ungroupColumn(fromColumn & 0xFFFF, toColumn & 0xFFFF); + } + + /** * Expands or collapses a column group. * * @param columnNumber One of the columns in the group. * @param collapsed true = collapse group, false = expand group. */ - public void setColumnGroupCollapsed( short columnNumber, boolean collapsed ) - { - sheet.setColumnGroupCollapsed( columnNumber, collapsed ); + public void setColumnGroupCollapsed(int columnNumber, boolean collapsed) { + sheet.setColumnGroupCollapsed(columnNumber, collapsed); } /** @@ -1618,14 +1634,12 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet * @param fromColumn beginning of the column range. * @param toColumn end of the column range. */ - public void groupColumn(short fromColumn, short toColumn) - { - sheet.groupColumnRange( fromColumn, toColumn, true ); + public void groupColumn(int fromColumn, int toColumn) { + sheet.groupColumnRange(fromColumn, toColumn, true); } - public void ungroupColumn( short fromColumn, short toColumn ) - { - sheet.groupColumnRange( fromColumn, toColumn, false ); + public void ungroupColumn(int fromColumn, int toColumn) { + sheet.groupColumnRange(fromColumn, toColumn, false); } public void groupRow(int fromRow, int toRow) diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 54962e7f20..1d24ad3921 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -661,6 +661,16 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm return -1; } + /* package */ int findSheetIndex(Sheet sheet) { + for(int i=0; i<_sheets.size(); i++) { + HSSFSheet hSheet = (HSSFSheet) _sheets.get(i); + if(hSheet.getSheet() == sheet) { + return i; + } + } + throw new IllegalArgumentException("Specified sheet not found in this workbook"); + } + /** * Returns the external sheet index of the sheet * with the given internal index, creating one diff --git a/src/java/org/apache/poi/hssf/usermodel/LazyAreaEval.java b/src/java/org/apache/poi/hssf/usermodel/LazyAreaEval.java new file mode 100644 index 0000000000..0a195c8754 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/LazyAreaEval.java @@ -0,0 +1,29 @@ +/* ==================================================================== + 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.usermodel; +import org.apache.poi.hssf.record.formula.AreaI; + +/** + * + * @author Josh Micich + */ +final class LazyAreaEval extends org.apache.poi.ss.usermodel.LazyAreaEval { + public LazyAreaEval(AreaI ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) { + super(ptg, sheet, evaluator); + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/LazyRefEval.java b/src/java/org/apache/poi/hssf/usermodel/LazyRefEval.java new file mode 100644 index 0000000000..3b591bbcc0 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/LazyRefEval.java @@ -0,0 +1,33 @@ +/* ====================================================================
+ 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.usermodel;
+import org.apache.poi.hssf.record.formula.Ref3DPtg;
+import org.apache.poi.hssf.record.formula.RefPtg;
+
+/**
+*
+* @author Josh Micich
+*/
+final class LazyRefEval extends org.apache.poi.ss.usermodel.LazyRefEval {
+ public LazyRefEval(RefPtg ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) {
+ super(ptg, sheet, evaluator);
+ }
+ public LazyRefEval(Ref3DPtg ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) {
+ super(ptg, sheet, evaluator);
+ }
+}
diff --git a/src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetector.java b/src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetector.java index 9cf3aa982f..30c6d31b1e 100755 --- a/src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetector.java +++ b/src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetector.java @@ -20,12 +20,9 @@ package org.apache.poi.ss.usermodel; import java.util.ArrayList; import java.util.List; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; - /** * Instances of this class keep track of multiple dependent cell evaluations due - * to recursive calls to <tt>HSSFFormulaEvaluator.internalEvaluate()</tt>. + * to recursive calls to <tt>FormulaEvaluator.internalEvaluate()</tt>. * The main purpose of this class is to detect an attempt to evaluate a cell * that is already being evaluated. In other words, it detects circular * references in spreadsheet formulas. @@ -40,19 +37,19 @@ final class EvaluationCycleDetector { private static final class CellEvaluationFrame { private final Workbook _workbook; - private final Sheet _sheet; + private final int _sheetIndex; private final int _srcRowNum; private final int _srcColNum; - public CellEvaluationFrame(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) { + public CellEvaluationFrame(Workbook workbook, int sheetIndex, int srcRowNum, int srcColNum) { if (workbook == null) { throw new IllegalArgumentException("workbook must not be null"); } - if (sheet == null) { - throw new IllegalArgumentException("sheet must not be null"); + if (sheetIndex < 0) { + throw new IllegalArgumentException("sheetIndex must not be negative"); } _workbook = workbook; - _sheet = sheet; + _sheetIndex = sheetIndex; _srcRowNum = srcRowNum; _srcColNum = srcColNum; } @@ -62,7 +59,7 @@ final class EvaluationCycleDetector { if (_workbook != other._workbook) { return false; } - if (_sheet != other._sheet) { + if (_sheetIndex != other._sheetIndex) { return false; } if (_srcRowNum != other._srcRowNum) { @@ -78,7 +75,7 @@ final class EvaluationCycleDetector { * @return human readable string for debug purposes */ public String formatAsString() { - return "R=" + _srcRowNum + " C=" + _srcColNum + " ShIx=" + _workbook.getSheetIndex(_sheet); + return "R=" + _srcRowNum + " C=" + _srcColNum + " ShIx=" + _sheetIndex; } public String toString() { @@ -111,8 +108,8 @@ final class EvaluationCycleDetector { * @return <code>true</code> if the specified cell has not been visited yet in the current * evaluation. <code>false</code> if the specified cell is already being evaluated. */ - public boolean startEvaluate(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) { - CellEvaluationFrame cef = new CellEvaluationFrame(workbook, sheet, srcRowNum, srcColNum); + public boolean startEvaluate(Workbook workbook, int sheetIndex, int srcRowNum, int srcColNum) { + CellEvaluationFrame cef = new CellEvaluationFrame(workbook, sheetIndex, srcRowNum, srcColNum); if (_evaluationFrames.contains(cef)) { return false; } @@ -132,7 +129,7 @@ final class EvaluationCycleDetector { * required. However, they have been included to assert correct behaviour, * and form more meaningful error messages. */ - public void endEvaluate(Workbook workbook, Sheet sheet, int srcRowNum, int srcColNum) { + public void endEvaluate(Workbook workbook, int sheetIndex, int srcRowNum, int srcColNum) { int nFrames = _evaluationFrames.size(); if (nFrames < 1) { throw new IllegalStateException("Call to endEvaluate without matching call to startEvaluate"); @@ -140,7 +137,7 @@ final class EvaluationCycleDetector { nFrames--; CellEvaluationFrame cefExpected = (CellEvaluationFrame) _evaluationFrames.get(nFrames); - CellEvaluationFrame cefActual = new CellEvaluationFrame(workbook, sheet, srcRowNum, srcColNum); + CellEvaluationFrame cefActual = new CellEvaluationFrame(workbook, sheetIndex, srcRowNum, srcColNum); if (!cefActual.equals(cefExpected)) { throw new RuntimeException("Wrong cell specified. " + "Corresponding startEvaluate() call was for cell {" diff --git a/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java b/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java index 30e78c33e7..0f1b7bc47c 100644 --- a/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java +++ b/src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java @@ -20,9 +20,6 @@ package org.apache.poi.ss.usermodel; import java.util.Iterator; import java.util.Stack; -import org.apache.poi.ss.util.AreaReference; -import org.apache.poi.ss.util.CellReference; - import org.apache.poi.hssf.model.FormulaParser; import org.apache.poi.hssf.record.NameRecord; import org.apache.poi.hssf.record.formula.Area3DPtg; @@ -49,8 +46,6 @@ import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.FunctionEval; -import org.apache.poi.hssf.record.formula.eval.LazyAreaEval; -import org.apache.poi.hssf.record.formula.eval.LazyRefEval; import org.apache.poi.hssf.record.formula.eval.NameEval; import org.apache.poi.hssf.record.formula.eval.NameXEval; import org.apache.poi.hssf.record.formula.eval.NumberEval; @@ -58,6 +53,7 @@ import org.apache.poi.hssf.record.formula.eval.OperationEval; 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.util.CellReference; /** * Evaluates formula cells.<p/> @@ -70,29 +66,37 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval; * @author Josh Micich */ public class FormulaEvaluator { - /** - * used to track the number of evaluations - */ + + /** + * used to track the number of evaluations + */ private static final class Counter { public int value; + public int depth; public Counter() { value = 0; } } - protected Sheet _sheet; - protected Workbook _workbook; + protected final Workbook _workbook; private final EvaluationCache _cache; private Counter _evaluationCounter; - + /** + * @deprecated (Sep 2008) Sheet parameter is ignored + */ public FormulaEvaluator(Sheet sheet, Workbook workbook) { - this(sheet, workbook, new EvaluationCache(), new Counter()); + this(workbook); + if (false) { + sheet.toString(); // suppress unused parameter compiler warning + } + } + public FormulaEvaluator(Workbook workbook) { + this(workbook, new EvaluationCache(), new Counter()); } - - private FormulaEvaluator(Sheet sheet, Workbook workbook, EvaluationCache cache, Counter evaluationCounter) { - _sheet = sheet; + + private FormulaEvaluator(Workbook workbook, EvaluationCache cache, Counter evaluationCounter) { _workbook = workbook; _cache = cache; _evaluationCounter = evaluationCounter; @@ -140,7 +144,6 @@ public class FormulaEvaluator { _cache.clear(); } - /** * If cell contains a formula, the formula is evaluated and returned, * else the CellValue simply copies the appropriate cell value from @@ -150,34 +153,23 @@ public class FormulaEvaluator { * @param cell */ public CellValue evaluate(Cell cell) { - CellValue retval = null; - if (cell != null) { - switch (cell.getCellType()) { - case Cell.CELL_TYPE_BLANK: - retval = new CellValue(Cell.CELL_TYPE_BLANK, _workbook.getCreationHelper()); - break; + if (cell == null) { + return null; + } + + switch (cell.getCellType()) { case Cell.CELL_TYPE_BOOLEAN: - retval = new CellValue(Cell.CELL_TYPE_BOOLEAN, _workbook.getCreationHelper()); - retval.setBooleanValue(cell.getBooleanCellValue()); - break; + return CellValue.valueOf(cell.getBooleanCellValue()); case Cell.CELL_TYPE_ERROR: - retval = new CellValue(Cell.CELL_TYPE_ERROR, _workbook.getCreationHelper()); - retval.setErrorValue(cell.getErrorCellValue()); - break; + return CellValue.getError(cell.getErrorCellValue()); case Cell.CELL_TYPE_FORMULA: - retval = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper()); - break; + return evaluateFormulaCellValue(cell); case Cell.CELL_TYPE_NUMERIC: - retval = new CellValue(Cell.CELL_TYPE_NUMERIC, _workbook.getCreationHelper()); - retval.setNumberValue(cell.getNumericCellValue()); - break; + return new CellValue(cell.getNumericCellValue(), _workbook.getCreationHelper()); case Cell.CELL_TYPE_STRING: - retval = new CellValue(Cell.CELL_TYPE_STRING, _workbook.getCreationHelper()); - retval.setRichTextStringValue(cell.getRichStringCellValue()); - break; - } + return new CellValue(cell.getRichStringCellValue().getString(), _workbook.getCreationHelper()); } - return retval; + throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")"); } @@ -200,32 +192,13 @@ public class FormulaEvaluator { * @return The type of the formula result (the cell's type remains as Cell.CELL_TYPE_FORMULA however) */ public int evaluateFormulaCell(Cell cell) { - if (cell != null) { - switch (cell.getCellType()) { - case Cell.CELL_TYPE_FORMULA: - CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper()); - switch (cv.getCellType()) { - case Cell.CELL_TYPE_BOOLEAN: - cell.setCellValue(cv.getBooleanValue()); - break; - case Cell.CELL_TYPE_ERROR: - cell.setCellValue(cv.getErrorValue()); - break; - case Cell.CELL_TYPE_NUMERIC: - cell.setCellValue(cv.getNumberValue()); - break; - case Cell.CELL_TYPE_STRING: - cell.setCellValue(cv.getRichTextStringValue()); - break; - case Cell.CELL_TYPE_BLANK: - break; - case Cell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula - break; - } - return cv.getCellType(); - } + if (cell == null || cell.getCellType() != Cell.CELL_TYPE_FORMULA) { + return -1; } - return -1; + CellValue cv = evaluateFormulaCellValue(cell); + // cell remains a formula cell, but the cached value is changed + setCellValue(cell, cv); + return cv.getCellType(); } /** @@ -245,35 +218,56 @@ public class FormulaEvaluator { * @param cell */ public Cell evaluateInCell(Cell cell) { - if (cell != null) { - switch (cell.getCellType()) { - case Cell.CELL_TYPE_FORMULA: - CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper()); - switch (cv.getCellType()) { - case Cell.CELL_TYPE_BOOLEAN: - cell.setCellType(Cell.CELL_TYPE_BOOLEAN); - cell.setCellValue(cv.getBooleanValue()); - break; - case Cell.CELL_TYPE_ERROR: - cell.setCellErrorValue(cv.getErrorValue()); - break; - case Cell.CELL_TYPE_NUMERIC: - cell.setCellType(Cell.CELL_TYPE_NUMERIC); - cell.setCellValue(cv.getNumberValue()); - break; - case Cell.CELL_TYPE_STRING: - cell.setCellType(Cell.CELL_TYPE_STRING); - cell.setCellValue(cv.getRichTextStringValue()); - break; - case Cell.CELL_TYPE_BLANK: - break; - case Cell.CELL_TYPE_FORMULA: // this will never happen, we have already evaluated the formula - break; - } - } + if (cell == null) { + return null; + } + if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) { + CellValue cv = evaluateFormulaCellValue(cell); + setCellType(cell, cv); // cell will no longer be a formula cell + setCellValue(cell, cv); } return cell; } + private static void setCellType(Cell cell, CellValue cv) { + int cellType = cv.getCellType(); + switch (cellType) { + case Cell.CELL_TYPE_BOOLEAN: + case Cell.CELL_TYPE_ERROR: + case Cell.CELL_TYPE_NUMERIC: + case Cell.CELL_TYPE_STRING: + cell.setCellType(cellType); + return; + case Cell.CELL_TYPE_BLANK: + // never happens - blanks eventually get translated to zero + case Cell.CELL_TYPE_FORMULA: + // this will never happen, we have already evaluated the formula + } + throw new IllegalStateException("Unexpected cell value type (" + cellType + ")"); + } + + private static void setCellValue(Cell cell, CellValue cv) { + int cellType = cv.getCellType(); + switch (cellType) { + case Cell.CELL_TYPE_BOOLEAN: + cell.setCellValue(cv.getBooleanValue()); + break; + case Cell.CELL_TYPE_ERROR: + cell.setCellErrorValue(cv.getErrorValue()); + break; + case Cell.CELL_TYPE_NUMERIC: + cell.setCellValue(cv.getNumberValue()); + break; + case Cell.CELL_TYPE_STRING: + cell.setCellValue(cv.getRichTextStringValue()); + break; + case Cell.CELL_TYPE_BLANK: + // never happens - blanks eventually get translated to zero + case Cell.CELL_TYPE_FORMULA: + // this will never happen, we have already evaluated the formula + default: + throw new IllegalStateException("Unexpected cell value type (" + cellType + ")"); + } + } /** * Loops over all cells in all sheets of the supplied @@ -287,9 +281,9 @@ public class FormulaEvaluator { * cells, and calling evaluateFormulaCell on each one. */ public static void evaluateAllFormulaCells(Workbook wb) { + FormulaEvaluator evaluator = new FormulaEvaluator(wb); for(int i=0; i<wb.getNumberOfSheets(); i++) { Sheet sheet = wb.getSheetAt(i); - FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb); for (Iterator rit = sheet.rowIterator(); rit.hasNext();) { Row r = (Row)rit.next(); @@ -303,72 +297,61 @@ public class FormulaEvaluator { } } - /** * Returns a CellValue wrapper around the supplied ValueEval instance. * @param eval */ - private static CellValue getCellValueForEval(ValueEval eval, CreationHelper cHelper) { - CellValue retval = null; - if (eval != null) { - if (eval instanceof NumberEval) { - NumberEval ne = (NumberEval) eval; - retval = new CellValue(Cell.CELL_TYPE_NUMERIC, cHelper); - retval.setNumberValue(ne.getNumberValue()); - } - else if (eval instanceof BoolEval) { - BoolEval be = (BoolEval) eval; - retval = new CellValue(Cell.CELL_TYPE_BOOLEAN, cHelper); - retval.setBooleanValue(be.getBooleanValue()); - } - else if (eval instanceof StringEval) { - StringEval ne = (StringEval) eval; - retval = new CellValue(Cell.CELL_TYPE_STRING, cHelper); - retval.setStringValue(ne.getStringValue()); - } - else if (eval instanceof BlankEval) { - retval = new CellValue(Cell.CELL_TYPE_BLANK, cHelper); - } - else if (eval instanceof ErrorEval) { - retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper); - retval.setErrorValue((byte)((ErrorEval)eval).getErrorCode()); -// retval.setRichTextStringValue(new RichTextString("#An error occurred. check cell.getErrorCode()")); - } - else { - retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper); - } + private CellValue evaluateFormulaCellValue(Cell cell) { + ValueEval eval = internalEvaluate(cell); + if (eval instanceof NumberEval) { + NumberEval ne = (NumberEval) eval; + return new CellValue(ne.getNumberValue(), _workbook.getCreationHelper()); + } + if (eval instanceof BoolEval) { + BoolEval be = (BoolEval) eval; + return CellValue.valueOf(be.getBooleanValue()); } - return retval; + if (eval instanceof StringEval) { + StringEval ne = (StringEval) eval; + return new CellValue(ne.getStringValue(), _workbook.getCreationHelper()); + } + if (eval instanceof ErrorEval) { + return CellValue.getError(((ErrorEval)eval).getErrorCode()); + } + throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")"); } /** * Dev. Note: Internal evaluate must be passed only a formula cell * else a runtime exception will be thrown somewhere inside the method. * (Hence this is a private method.) + * @return never <code>null</code>, never {@link BlankEval} */ - private ValueEval internalEvaluate(Cell srcCell, Sheet sheet) { + private ValueEval internalEvaluate(Cell srcCell) { int srcRowNum = srcCell.getRowIndex(); int srcColNum = srcCell.getCellNum(); ValueEval result; - int sheetIndex = _workbook.getSheetIndex(sheet); + int sheetIndex = _workbook.getSheetIndex(srcCell.getSheet()); result = _cache.getValue(sheetIndex, srcRowNum, srcColNum); if (result != null) { - return result; + return result; } _evaluationCounter.value++; + _evaluationCounter.depth++; EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker(); - if(!tracker.startEvaluate(_workbook, sheet, srcRowNum, srcColNum)) { + if(!tracker.startEvaluate(_workbook, sheetIndex, srcRowNum, srcColNum)) { return ErrorEval.CIRCULAR_REF_ERROR; } try { - result = evaluateCell(srcRowNum, (short)srcColNum, srcCell.getCellFormula()); + result = evaluateCell(sheetIndex, srcRowNum, (short)srcColNum, srcCell.getCellFormula()); } finally { - tracker.endEvaluate(_workbook, sheet, srcRowNum, srcColNum); + tracker.endEvaluate(_workbook, sheetIndex, srcRowNum, srcColNum); _cache.setValue(sheetIndex, srcRowNum, srcColNum, result); + _evaluationCounter.depth--; } if (isDebugLogEnabled()) { String sheetName = _workbook.getSheetName(sheetIndex); @@ -377,7 +360,7 @@ public class FormulaEvaluator { } return result; } - private ValueEval evaluateCell(int srcRowNum, short srcColNum, String cellFormulaText) { + private ValueEval evaluateCell(int sheetIndex, int srcRowNum, short srcColNum, String cellFormulaText) { Ptg[] ptgs = FormulaParser.parse(cellFormulaText, _workbook); @@ -392,8 +375,8 @@ public class FormulaEvaluator { } if (ptg instanceof MemErrPtg) { continue; } if (ptg instanceof MissingArgPtg) { - // TODO - might need to push BlankEval or MissingArgEval - continue; + // TODO - might need to push BlankEval or MissingArgEval + continue; } Eval opResult; if (ptg instanceof OperationPtg) { @@ -411,15 +394,15 @@ public class FormulaEvaluator { Eval p = (Eval) stack.pop(); ops[j] = p; } - logDebug("invoke " + operation + " (nAgs=" + numops + ")"); - opResult = invokeOperation(operation, ops, srcRowNum, srcColNum, _workbook, _sheet); +// logDebug("invoke " + operation + " (nAgs=" + numops + ")"); + opResult = invokeOperation(operation, ops, _workbook, sheetIndex, srcRowNum, srcColNum); } else { - opResult = getEvalForPtg(ptg, _sheet); + opResult = getEvalForPtg(ptg, sheetIndex); } if (opResult == null) { throw new RuntimeException("Evaluation result must not be null"); } - logDebug("push " + opResult); +// logDebug("push " + opResult); stack.push(opResult); } @@ -428,7 +411,7 @@ public class FormulaEvaluator { throw new IllegalStateException("evaluation stack not empty"); } value = dereferenceValue(value, srcRowNum, srcColNum); - if (value instanceof BlankEval) { + if (value == BlankEval.INSTANCE) { // Note Excel behaviour here. A blank final final value is converted to zero. return NumberEval.ZERO; // Formulas _never_ evaluate to blank. If a formula appears to have evaluated to @@ -464,24 +447,21 @@ public class FormulaEvaluator { return evaluationResult; } - private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum, - Workbook workbook, Sheet sheet) { + private static Eval invokeOperation(OperationEval operation, Eval[] ops, + Workbook workbook, int sheetIndex, int srcRowNum, int srcColNum) { if(operation instanceof FunctionEval) { FunctionEval fe = (FunctionEval) operation; if(fe.isFreeRefFunction()) { - return fe.getFreeRefFunction().evaluate(ops, srcRowNum, srcColNum, workbook, sheet); + return fe.getFreeRefFunction().evaluate(ops, workbook, sheetIndex, srcRowNum, srcColNum); } } - return operation.evaluate(ops, srcRowNum, srcColNum); + return operation.evaluate(ops, srcRowNum, (short)srcColNum); } private Sheet getOtherSheet(int externSheetIndex) { return _workbook.getSheetAt(_workbook.getSheetIndexFromExternSheetIndex(externSheetIndex)); } - private FormulaEvaluator createEvaluatorForAnotherSheet(Sheet sheet) { - return new FormulaEvaluator(sheet, _workbook, _cache, _evaluationCounter); - } /** * returns an appropriate Eval impl instance for the Ptg. The Ptg must be @@ -489,7 +469,7 @@ public class FormulaEvaluator { * StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be * passed here! */ - private Eval getEvalForPtg(Ptg ptg, Sheet sheet) { + private Eval getEvalForPtg(Ptg ptg, int sheetIndex) { if (ptg instanceof NamePtg) { // named ranges, macro functions NamePtg namePtg = (NamePtg) ptg; @@ -499,41 +479,23 @@ public class FormulaEvaluator { throw new RuntimeException("Bad name index (" + nameIndex + "). Allowed range is (0.." + (numberOfNames-1) + ")"); } - if(_workbook instanceof org.apache.poi.hssf.usermodel.HSSFWorkbook) { - org.apache.poi.hssf.usermodel.HSSFWorkbook hssfWb = - (org.apache.poi.hssf.usermodel.HSSFWorkbook)_workbook; - NameRecord nameRecord = hssfWb.getNameRecord(nameIndex); + if(_workbook instanceof org.apache.poi.hssf.usermodel.HSSFWorkbook) { + NameRecord nameRecord = ((org.apache.poi.hssf.usermodel.HSSFWorkbook)_workbook).getNameRecord(nameIndex); if (nameRecord.isFunctionName()) { return new NameEval(nameRecord.getNameText()); } if (nameRecord.hasFormula()) { - return evaluateNameFormula(nameRecord.getNameDefinition(), sheet); + return evaluateNameFormula(nameRecord.getNameDefinition(), sheetIndex); } - throw new RuntimeException("Don't know how to evalate name '" + nameRecord.getNameText() + "'"); - } else { - throw new RuntimeException("Don't know how to evaluate name records for XSSF"); + + throw new RuntimeException("Don't now how to evalate name '" + nameRecord.getNameText() + "'"); } + throw new RuntimeException("Don't now how to evalate name for XSSFWorkbook"); } if (ptg instanceof NameXPtg) { NameXPtg nameXPtg = (NameXPtg) ptg; return new NameXEval(nameXPtg.getSheetRefIndex(), nameXPtg.getNameIndex()); } - if (ptg instanceof RefPtg) { - return new LazyRefEval(((RefPtg) ptg), sheet, this); - } - if (ptg instanceof Ref3DPtg) { - Ref3DPtg refPtg = (Ref3DPtg) ptg; - Sheet xsheet = getOtherSheet(refPtg.getExternSheetIndex()); - return new LazyRefEval(refPtg, xsheet, createEvaluatorForAnotherSheet(xsheet)); - } - if (ptg instanceof AreaPtg) { - return new LazyAreaEval(((AreaPtg) ptg), sheet, this); - } - if (ptg instanceof Area3DPtg) { - Area3DPtg a3dp = (Area3DPtg) ptg; - Sheet xsheet = getOtherSheet(a3dp.getExternSheetIndex()); - return new LazyAreaEval(a3dp, xsheet, createEvaluatorForAnotherSheet(xsheet)); - } if (ptg instanceof IntPtg) { return new NumberEval(((IntPtg)ptg).getValue()); @@ -550,17 +512,38 @@ public class FormulaEvaluator { if (ptg instanceof ErrPtg) { return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode()); } + Sheet sheet = _workbook.getSheetAt(sheetIndex); + if (ptg instanceof RefPtg) { + return new LazyRefEval(((RefPtg) ptg), sheet, this); + } + if (ptg instanceof AreaPtg) { + return new LazyAreaEval(((AreaPtg) ptg), sheet, this); + } + if (ptg instanceof Ref3DPtg) { + Ref3DPtg refPtg = (Ref3DPtg) ptg; + Sheet xsheet = getOtherSheet(refPtg.getExternSheetIndex()); + return new LazyRefEval(refPtg, xsheet, this); + } + if (ptg instanceof Area3DPtg) { + Area3DPtg a3dp = (Area3DPtg) ptg; + Sheet xsheet = getOtherSheet(a3dp.getExternSheetIndex()); + return new LazyAreaEval(a3dp, xsheet, this); + } + if (ptg instanceof UnknownPtg) { - // TODO - remove UnknownPtg + // POI uses UnknownPtg when the encoded Ptg array seems to be corrupted. + // This seems to occur in very rare cases (e.g. unused name formulas in bug 44774, attachment 21790) + // In any case, formulas are re-parsed before execution, so UnknownPtg should not get here throw new RuntimeException("UnknownPtg not allowed"); } + throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")"); } - private Eval evaluateNameFormula(Ptg[] ptgs, Sheet sheet) { + private Eval evaluateNameFormula(Ptg[] ptgs, int sheetIndex) { if (ptgs.length > 1) { throw new RuntimeException("Complex name formulas not supported yet"); } - return getEvalForPtg(ptgs[0], sheet); + return getEvalForPtg(ptgs[0], sheetIndex); } /** @@ -568,11 +551,8 @@ public class FormulaEvaluator { * impl instance and return that. Since the cell could be an external * reference, we need the sheet that this belongs to. * Non existent cells are treated as empty. - * @param cell - * @param sheet - * @param workbook */ - public ValueEval getEvalForCell(Cell cell, Sheet sheet) { + public ValueEval getEvalForCell(Cell cell) { if (cell == null) { return BlankEval.INSTANCE; @@ -583,7 +563,7 @@ public class FormulaEvaluator { case Cell.CELL_TYPE_STRING: return new StringEval(cell.getRichStringCellValue().getString()); case Cell.CELL_TYPE_FORMULA: - return internalEvaluate(cell, sheet); + return internalEvaluate(cell); case Cell.CELL_TYPE_BOOLEAN: return BoolEval.valueOf(cell.getBooleanCellValue()); case Cell.CELL_TYPE_BLANK: @@ -600,93 +580,99 @@ public class FormulaEvaluator { * or Number or boolean type. * @author Amol S. Deshmukh < amolweb at ya hoo dot com > */ - public static class CellValue { - private CreationHelper creationHelper; - private int cellType; - private RichTextString richTextStringValue; - private double numberValue; - private boolean booleanValue; - private byte errorValue; - - /** - * CellType should be one of the types defined in Cell - * @param cellType - */ - public CellValue(int cellType, CreationHelper creationHelper) { - super(); - this.creationHelper = creationHelper; - this.cellType = cellType; - } + public static final class CellValue { + public static final CellValue TRUE = new CellValue(Cell.CELL_TYPE_BOOLEAN, 0.0, true, null, 0, null); + public static final CellValue FALSE= new CellValue(Cell.CELL_TYPE_BOOLEAN, 0.0, false, null, 0, null); + + private final int _cellType; + private final double _numberValue; + private final boolean _booleanValue; + private final String _textValue; + private final int _errorCode; + private CreationHelper _creationHelper; + + private CellValue(int cellType, double numberValue, boolean booleanValue, + String textValue, int errorCode, CreationHelper creationHelper) { + _cellType = cellType; + _numberValue = numberValue; + _booleanValue = booleanValue; + _textValue = textValue; + _errorCode = errorCode; + _creationHelper = creationHelper; + } + + + /* package*/ CellValue(double numberValue, CreationHelper creationHelper) { + this(Cell.CELL_TYPE_NUMERIC, numberValue, false, null, 0, creationHelper); + } + /* package*/ static CellValue valueOf(boolean booleanValue) { + return booleanValue ? TRUE : FALSE; + } + /* package*/ CellValue(String stringValue, CreationHelper creationHelper) { + this(Cell.CELL_TYPE_STRING, 0.0, false, stringValue, 0, creationHelper); + } + /* package*/ static CellValue getError(int errorCode) { + return new CellValue(Cell.CELL_TYPE_ERROR, 0.0, false, null, errorCode, null); + } + + /** * @return Returns the booleanValue. */ public boolean getBooleanValue() { - return booleanValue; - } - /** - * @param booleanValue The booleanValue to set. - */ - public void setBooleanValue(boolean booleanValue) { - this.booleanValue = booleanValue; + return _booleanValue; } /** * @return Returns the numberValue. */ public double getNumberValue() { - return numberValue; - } - /** - * @param numberValue The numberValue to set. - */ - public void setNumberValue(double numberValue) { - this.numberValue = numberValue; + return _numberValue; } /** - * @return Returns the stringValue. This method is deprecated, use - * getRichTextStringValue instead - * @deprecated + * @return Returns the stringValue. */ public String getStringValue() { - return richTextStringValue.getString(); - } - /** - * @param stringValue The stringValue to set. This method is deprecated, use - * getRichTextStringValue instead. - * @deprecated - */ - public void setStringValue(String stringValue) { - this.richTextStringValue = - creationHelper.createRichTextString(stringValue); + return _textValue; } /** * @return Returns the cellType. */ public int getCellType() { - return cellType; + return _cellType; } /** * @return Returns the errorValue. */ public byte getErrorValue() { - return errorValue; - } - /** - * @param errorValue The errorValue to set. - */ - public void setErrorValue(byte errorValue) { - this.errorValue = errorValue; + return (byte) _errorCode; } /** * @return Returns the richTextStringValue. + * @deprecated (Sep 2008) Text formatting is lost during formula evaluation. Use {@link #getStringValue()} */ public RichTextString getRichTextStringValue() { - return richTextStringValue; + return _creationHelper.createRichTextString(_textValue); } - /** - * @param richTextStringValue The richTextStringValue to set. - */ - public void setRichTextStringValue(RichTextString richTextStringValue) { - this.richTextStringValue = richTextStringValue; + public String toString() { + StringBuffer sb = new StringBuffer(64); + sb.append(getClass().getName()).append(" ["); + sb.append(formatAsString()); + sb.append("]"); + return sb.toString(); + } + + public String formatAsString() { + switch (_cellType) { + case Cell.CELL_TYPE_NUMERIC: + return String.valueOf(_numberValue); + case Cell.CELL_TYPE_STRING: + return '"' + _textValue + '"'; + case Cell.CELL_TYPE_BOOLEAN: + return _booleanValue ? "TRUE" : "FALSE"; + case Cell.CELL_TYPE_ERROR: + return ErrorEval.getText(_errorCode); + } + return "<error unexpected cell type " + _cellType + ">"; } } } diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java b/src/java/org/apache/poi/ss/usermodel/LazyAreaEval.java index 9f38305545..8d9b192291 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java +++ b/src/java/org/apache/poi/ss/usermodel/LazyAreaEval.java @@ -15,22 +15,21 @@ limitations under the License. ==================================================================== */ -package org.apache.poi.hssf.record.formula.eval; +package org.apache.poi.ss.usermodel; import org.apache.poi.hssf.record.formula.AreaI; import org.apache.poi.hssf.record.formula.AreaI.OffsetArea; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.FormulaEvaluator; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.util.CellReference; +import org.apache.poi.hssf.record.formula.eval.AreaEval; +import org.apache.poi.hssf.record.formula.eval.AreaEvalBase; +import org.apache.poi.hssf.record.formula.eval.BlankEval; +import org.apache.poi.hssf.record.formula.eval.ValueEval; +import org.apache.poi.hssf.util.CellReference; /** * * @author Josh Micich */ -public final class LazyAreaEval extends AreaEvalBase { +public class LazyAreaEval extends AreaEvalBase { private final Sheet _sheet; private FormulaEvaluator _evaluator; @@ -54,7 +53,7 @@ public final class LazyAreaEval extends AreaEvalBase { if (cell == null) { return BlankEval.INSTANCE; } - return _evaluator.getEvalForCell(cell, _sheet); + return _evaluator.getEvalForCell(cell); } public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) { diff --git a/src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java b/src/java/org/apache/poi/ss/usermodel/LazyRefEval.java index b458011972..7fb7681aeb 100644 --- a/src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java +++ b/src/java/org/apache/poi/ss/usermodel/LazyRefEval.java @@ -1,85 +1,85 @@ -/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.hssf.record.formula.eval;
-
-import org.apache.poi.hssf.record.formula.AreaI;
-import org.apache.poi.hssf.record.formula.Ref3DPtg;
-import org.apache.poi.hssf.record.formula.RefPtg;
-import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
-import org.apache.poi.ss.usermodel.Cell;
-import org.apache.poi.ss.usermodel.FormulaEvaluator;
-import org.apache.poi.ss.usermodel.Row;
-import org.apache.poi.ss.usermodel.Sheet;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.ss.util.CellReference;
-
-/**
-*
-* @author Josh Micich
-*/
-public final class LazyRefEval extends RefEvalBase {
- private final Sheet _sheet;
- private final FormulaEvaluator _evaluator;
-
-
- public LazyRefEval(RefPtg ptg, Sheet sheet, FormulaEvaluator evaluator) {
- super(ptg.getRow(), ptg.getColumn());
- _sheet = sheet;
- _evaluator = evaluator;
- }
- public LazyRefEval(Ref3DPtg ptg, Sheet sheet, FormulaEvaluator evaluator) {
- super(ptg.getRow(), ptg.getColumn());
- _sheet = sheet;
- _evaluator = evaluator;
- }
-
- public ValueEval getInnerValueEval() {
- int rowIx = getRow();
- int colIx = getColumn();
-
- Row row = _sheet.getRow(rowIx);
- if (row == null) {
- return BlankEval.INSTANCE;
- }
- Cell cell = row.getCell(colIx);
- if (cell == null) {
- return BlankEval.INSTANCE;
- }
- return _evaluator.getEvalForCell(cell, _sheet);
- }
-
- public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
-
- AreaI area = new OffsetArea(getRow(), getColumn(),
- relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
-
- return new LazyAreaEval(area, _sheet, _evaluator);
- }
-
- public String toString() {
- CellReference cr = new CellReference(getRow(), getColumn());
- StringBuffer sb = new StringBuffer();
- sb.append(getClass().getName()).append("[");
- String sheetName = _evaluator.getSheetName(_sheet);
- sb.append(sheetName);
- sb.append('!');
- sb.append(cr.formatAsString());
- sb.append("]");
- return sb.toString();
- }
-}
+/* ==================================================================== + 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.ss.usermodel; + +import org.apache.poi.hssf.record.formula.AreaI; +import org.apache.poi.hssf.record.formula.Ref3DPtg; +import org.apache.poi.hssf.record.formula.RefPtg; +import org.apache.poi.hssf.record.formula.AreaI.OffsetArea; +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.RefEvalBase; +import org.apache.poi.hssf.record.formula.eval.ValueEval; +import org.apache.poi.hssf.util.CellReference; + +/** +* +* @author Josh Micich +*/ +public class LazyRefEval extends RefEvalBase { + + private final Sheet _sheet; + private final FormulaEvaluator _evaluator; + + + public LazyRefEval(RefPtg ptg, Sheet sheet, FormulaEvaluator evaluator) { + super(ptg.getRow(), ptg.getColumn()); + _sheet = sheet; + _evaluator = evaluator; + } + public LazyRefEval(Ref3DPtg ptg, Sheet sheet, FormulaEvaluator evaluator) { + super(ptg.getRow(), ptg.getColumn()); + _sheet = sheet; + _evaluator = evaluator; + } + + public ValueEval getInnerValueEval() { + int rowIx = getRow(); + int colIx = getColumn(); + + Row row = _sheet.getRow(rowIx); + if (row == null) { + return BlankEval.INSTANCE; + } + Cell cell = row.getCell(colIx); + if (cell == null) { + return BlankEval.INSTANCE; + } + return _evaluator.getEvalForCell(cell); + } + + public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) { + + AreaI area = new OffsetArea(getRow(), getColumn(), + relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); + + return new LazyAreaEval(area, _sheet, _evaluator); + } + + public String toString() { + CellReference cr = new CellReference(getRow(), getColumn()); + StringBuffer sb = new StringBuffer(); + sb.append(getClass().getName()).append("["); + String sheetName = _evaluator.getSheetName(_sheet); + sb.append(sheetName); + sb.append('!'); + sb.append(cr.formatAsString()); + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/java/org/apache/poi/util/HexDump.java b/src/java/org/apache/poi/util/HexDump.java index ed6f128822..c5ebab30a7 100644 --- a/src/java/org/apache/poi/util/HexDump.java +++ b/src/java/org/apache/poi/util/HexDump.java @@ -255,8 +255,10 @@ public class HexDump { retVal.append('['); for(int x = 0; x < value.length; x++) { + if (x>0) { + retVal.append(", "); + } retVal.append(toHex(value[x])); - retVal.append(", "); } retVal.append(']'); return retVal.toString(); diff --git a/src/java/org/apache/poi/util/HexRead.java b/src/java/org/apache/poi/util/HexRead.java index cd11ccc640..91632e8fc5 100644 --- a/src/java/org/apache/poi/util/HexRead.java +++ b/src/java/org/apache/poi/util/HexRead.java @@ -172,9 +172,12 @@ public class HexRead return rval; } - static public byte[] readFromString(String data) throws IOException - { - return readData(new ByteArrayInputStream( data.getBytes() ), -1); + static public byte[] readFromString(String data) { + try { + return readData(new ByteArrayInputStream( data.getBytes() ), -1); + } catch (IOException e) { + throw new RuntimeException(e); + } } static private void readToEOL( InputStream stream ) throws IOException diff --git a/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Cell.java b/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Cell.java index 51d9874edc..823b6191c8 100644 --- a/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Cell.java +++ b/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Cell.java @@ -19,6 +19,7 @@ package org.apache.poi.ss.usermodel; import org.apache.poi.hssf.usermodel.HSSFCellStyle; import org.apache.poi.hssf.usermodel.HSSFRichTextString; +import org.apache.poi.hssf.usermodel.HSSFSheet; /** * This is a JDK 1.4 compatible interface for HSSFCell. @@ -85,6 +86,7 @@ public interface Cell { void setCellErrorValue(byte value); HSSFCellStyle getCellStyle(); + HSSFSheet getSheet(); boolean getBooleanCellValue(); double getNumericCellValue(); diff --git a/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Comment.java b/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Comment.java index 0b1d55c568..1959f9ad56 100644 --- a/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Comment.java +++ b/src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Comment.java @@ -17,4 +17,7 @@ package org.apache.poi.ss.usermodel; -public interface Comment {} +public interface Comment { + public void setRow(short row); + public void setColumn(short row); +} diff --git a/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Cell.java b/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Cell.java index 0211a9aaa5..29e6bdd8f8 100644 --- a/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Cell.java +++ b/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Cell.java @@ -106,6 +106,8 @@ public interface Cell { int getRowIndex(); + Sheet getSheet(); + /** * set the cells type (numeric, formula or string) * @see #CELL_TYPE_NUMERIC diff --git a/src/testcases/org/apache/poi/ddf/TestEscherBSERecord.java b/src/testcases/org/apache/poi/ddf/TestEscherBSERecord.java index 7d4dc7bee6..7db08a34a6 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherBSERecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherBSERecord.java @@ -37,7 +37,7 @@ public class TestEscherBSERecord extends TestCase assertEquals( (short) 0x0001, r.getOptions() ); assertEquals( EscherBSERecord.BT_JPEG, r.getBlipTypeWin32() ); assertEquals( EscherBSERecord.BT_JPEG, r.getBlipTypeMacOS() ); - assertEquals( "[01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00, ]", HexDump.toHex( r.getUid() ) ); + assertEquals( "[01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00]", HexDump.toHex( r.getUid() ) ); assertEquals( (short) 1, r.getTag() ); assertEquals( 2, r.getRef() ); assertEquals( 3, r.getOffset() ); @@ -57,7 +57,7 @@ public class TestEscherBSERecord extends TestCase assertEquals( 44, bytesWritten ); assertEquals( "[01, 00, 00, 00, 24, 00, 00, 00, 05, 05, 01, 02, 03, 04, " + "05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00, 01, 00, 00, 00, " + - "00, 00, 02, 00, 00, 00, 03, 00, 00, 00, 04, 05, 06, 07, ]", + "00, 00, 02, 00, 00, 00, 03, 00, 00, 00, 04, 05, 06, 07]", HexDump.toHex( data ) ); } @@ -90,7 +90,7 @@ public class TestEscherBSERecord extends TestCase " Options: 0x0001" + nl + " BlipTypeWin32: 5" + nl + " BlipTypeMacOS: 5" + nl + - " SUID: [01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00, ]" + nl + + " SUID: [01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 00]" + nl + " Tag: 1" + nl + " Size: 0" + nl + " Ref: 2" + nl + diff --git a/src/testcases/org/apache/poi/ddf/TestEscherBlipWMFRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherBlipWMFRecord.java index 3f73ccb1c2..8db7c4be38 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherBlipWMFRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherBlipWMFRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.ddf; import junit.framework.TestCase; @@ -36,8 +35,7 @@ public class TestEscherBlipWMFRecord extends TestCase data = HexRead.readFromString(dataStr); } - public void testSerialize() throws Exception - { + public void testSerialize() { EscherBlipWMFRecord r = new EscherBlipWMFRecord(); r.setBoundaryLeft(1); r.setBoundaryHeight(2); @@ -72,14 +70,13 @@ public class TestEscherBlipWMFRecord extends TestCase "05, 00, 00, 00, " + // field_9_cacheOfSavedSize "08, " + // field_10_compressionFlag "07, " + // field_11_filter - "01, 02, ]", // field_12_data + "01, 02]", // field_12_data HexDump.toHex(buf)); assertEquals(60, r.getRecordSize() ); } - public void testFillFields() throws Exception - { + public void testFillFields() { EscherBlipWMFRecord r = new EscherBlipWMFRecord(); r.fillFields( data, 0, new DefaultEscherRecordFactory()); @@ -92,15 +89,14 @@ public class TestEscherBlipWMFRecord extends TestCase assertEquals( 6, r.getCacheOfSize() ); assertEquals( 7, r.getFilter() ); assertEquals( 8, r.getCompressionFlag() ); - assertEquals( "[01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, ]", HexDump.toHex(r.getSecondaryUID() ) ); + assertEquals( "[01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01]", HexDump.toHex(r.getSecondaryUID() ) ); assertEquals( 10, r.getWidth() ); assertEquals( 11, r.getHeight() ); assertEquals( (short)5420, r.getOptions() ); - assertEquals( "[01, 02, ]", HexDump.toHex( r.getData() ) ); + assertEquals( "[01, 02]", HexDump.toHex( r.getData() ) ); } - public void testToString() throws Exception - { + public void testToString() { EscherBlipWMFRecord r = new EscherBlipWMFRecord(); r.fillFields( data, 0, new DefaultEscherRecordFactory() ); @@ -109,7 +105,7 @@ public class TestEscherBlipWMFRecord extends TestCase assertEquals( "org.apache.poi.ddf.EscherBlipWMFRecord:" + nl + " RecordId: 0xF018" + nl + " Options: 0x152C" + nl + - " Secondary UID: [01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, ]" + nl + + " Secondary UID: [01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01]" + nl + " CacheOfSize: 6" + nl + " BoundaryTop: 3" + nl + " BoundaryLeft: 1" + nl + @@ -124,5 +120,5 @@ public class TestEscherBlipWMFRecord extends TestCase "00000000 01 02 .." + nl , r.toString() ); } - } + diff --git a/src/testcases/org/apache/poi/ddf/TestEscherChildAnchorRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherChildAnchorRecord.java index 545351a3fa..9cf14e47b4 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherChildAnchorRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherChildAnchorRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.ddf; import junit.framework.TestCase; @@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead; public class TestEscherChildAnchorRecord extends TestCase { - public void testSerialize() throws Exception - { + public void testSerialize() { EscherChildAnchorRecord r = createRecord(); byte[] data = new byte[8 + 16]; @@ -37,7 +35,7 @@ public class TestEscherChildAnchorRecord extends TestCase "01, 00, 00, 00, " + "02, 00, 00, 00, " + "03, 00, 00, 00, " + - "04, 00, 00, 00, ]", HexDump.toHex( data ) ); + "04, 00, 00, 00]", HexDump.toHex( data ) ); } public void testFillFields() throws Exception @@ -62,8 +60,7 @@ public class TestEscherChildAnchorRecord extends TestCase assertEquals( (short) 0x0001, r.getOptions() ); } - public void testToString() throws Exception - { + public void testToString(){ String nl = System.getProperty( "line.separator" ); String expected = "org.apache.poi.ddf.EscherChildAnchorRecord:" + nl + @@ -76,7 +73,7 @@ public class TestEscherChildAnchorRecord extends TestCase assertEquals( expected, createRecord().toString() ); } - private EscherChildAnchorRecord createRecord() + private static EscherChildAnchorRecord createRecord() { EscherChildAnchorRecord r = new EscherChildAnchorRecord(); r.setRecordId( EscherChildAnchorRecord.RECORD_ID ); @@ -87,5 +84,5 @@ public class TestEscherChildAnchorRecord extends TestCase r.setDy2( 4 ); return r; } - } + diff --git a/src/testcases/org/apache/poi/ddf/TestEscherClientAnchorRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherClientAnchorRecord.java index c058622dbc..3a1e5c2b60 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherClientAnchorRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherClientAnchorRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.ddf; import junit.framework.TestCase; @@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead; public class TestEscherClientAnchorRecord extends TestCase { - public void testSerialize() throws Exception - { + public void testSerialize() { EscherClientAnchorRecord r = createRecord(); byte[] data = new byte[8 + 18 + 2]; @@ -37,11 +35,10 @@ public class TestEscherClientAnchorRecord extends TestCase "4D, 00, 37, 00, 21, 00, 58, 00, " + "0B, 00, 2C, 00, 16, 00, 63, 00, " + "42, 00, " + - "FF, DD, ]", HexDump.toHex( data ) ); + "FF, DD]", HexDump.toHex( data ) ); } - public void testFillFields() throws Exception - { + public void testFillFields() { String hexData = "01 00 " + "10 F0 " + "14 00 00 00 " + @@ -68,8 +65,7 @@ public class TestEscherClientAnchorRecord extends TestCase assertEquals( (byte) 0xDD, r.getRemainingData()[1] ); } - public void testToString() throws Exception - { + public void testToString() { String nl = System.getProperty("line.separator"); String expected = "org.apache.poi.ddf.EscherClientAnchorRecord:" + nl + diff --git a/src/testcases/org/apache/poi/ddf/TestEscherClientDataRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherClientDataRecord.java index 22a11181c6..2f26a37c5a 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherClientDataRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherClientDataRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.ddf; import junit.framework.TestCase; @@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead; public class TestEscherClientDataRecord extends TestCase { - public void testSerialize() throws Exception - { + public void testSerialize() { EscherClientDataRecord r = createRecord(); byte[] data = new byte[8]; @@ -33,12 +31,11 @@ public class TestEscherClientDataRecord extends TestCase assertEquals( 8, bytesWritten ); assertEquals( "[02, 00, " + "11, F0, " + - "00, 00, 00, 00, ]", + "00, 00, 00, 00]", HexDump.toHex( data ) ); } - public void testFillFields() throws Exception - { + public void testFillFields() { String hexData = "02 00 " + "11 F0 " + "00 00 00 00 "; @@ -51,8 +48,7 @@ public class TestEscherClientDataRecord extends TestCase assertEquals( "[]", HexDump.toHex(r.getRemainingData()) ); } - public void testToString() throws Exception - { + public void testToString() { String nl = System.getProperty("line.separator"); String expected = "org.apache.poi.ddf.EscherClientDataRecord:" + nl + @@ -63,7 +59,7 @@ public class TestEscherClientDataRecord extends TestCase assertEquals( expected, createRecord().toString() ); } - private EscherClientDataRecord createRecord() + private static EscherClientDataRecord createRecord() { EscherClientDataRecord r = new EscherClientDataRecord(); r.setOptions( (short) 0x0002 ); @@ -71,5 +67,4 @@ public class TestEscherClientDataRecord extends TestCase r.setRemainingData( new byte[] {} ); return r; } - } diff --git a/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java index 0f1fc9c733..490a3d50ca 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java @@ -30,13 +30,11 @@ public class TestEscherContainerRecord extends TestCase { private String ESCHER_DATA_PATH; - protected void setUp() throws Exception { - super.setUp(); + protected void setUp() { ESCHER_DATA_PATH = System.getProperty("DDF.testdata.path"); } - public void testFillFields() throws Exception - { + public void testFillFields() { EscherRecordFactory f = new DefaultEscherRecordFactory(); byte[] data = HexRead.readFromString( "0F 02 11 F1 00 00 00 00" ); EscherRecord r = f.createRecord( data, 0 ); @@ -55,15 +53,14 @@ public class TestEscherContainerRecord extends TestCase assertEquals( (short) 0xF222, c.getRecordId() ); } - public void testSerialize() throws Exception - { + public void testSerialize() { UnknownEscherRecord r = new UnknownEscherRecord(); r.setOptions( (short) 0x123F ); r.setRecordId( (short) 0xF112 ); byte[] data = new byte[8]; r.serialize( 0, data, new NullEscherSerializationListener() ); - assertEquals( "[3F, 12, 12, F1, 00, 00, 00, 00, ]", HexDump.toHex( data ) ); + assertEquals( "[3F, 12, 12, F1, 00, 00, 00, 00]", HexDump.toHex( data ) ); EscherRecord childRecord = new UnknownEscherRecord(); childRecord.setOptions( (short) 0x9999 ); @@ -72,12 +69,11 @@ public class TestEscherContainerRecord extends TestCase data = new byte[16]; r.serialize( 0, data, new NullEscherSerializationListener() ); - assertEquals( "[3F, 12, 12, F1, 08, 00, 00, 00, 99, 99, 01, FF, 00, 00, 00, 00, ]", HexDump.toHex( data ) ); + assertEquals( "[3F, 12, 12, F1, 08, 00, 00, 00, 99, 99, 01, FF, 00, 00, 00, 00]", HexDump.toHex( data ) ); } - public void testToString() throws Exception - { + public void testToString() { EscherContainerRecord r = new EscherContainerRecord(); r.setRecordId( EscherContainerRecord.SP_CONTAINER ); r.setOptions( (short) 0x000F ); @@ -134,8 +130,7 @@ public class TestEscherContainerRecord extends TestCase assertEquals( expected, r.toString() ); } - public void testGetRecordSize() throws Exception - { + public void testGetRecordSize() { EscherContainerRecord r = new EscherContainerRecord(); r.addChildRecord(new EscherRecord() { diff --git a/src/testcases/org/apache/poi/ddf/TestEscherDgRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherDgRecord.java index c1be281720..70ddd47330 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherDgRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherDgRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.ddf; import junit.framework.TestCase; @@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead; public class TestEscherDgRecord extends TestCase { - public void testSerialize() throws Exception - { + public void testSerialize() { EscherDgRecord r = createRecord(); byte[] data = new byte[16]; @@ -35,12 +33,11 @@ public class TestEscherDgRecord extends TestCase "08, F0, " + "08, 00, 00, 00, " + "02, 00, 00, 00, " + // num shapes in drawing - "01, 04, 00, 00, ]", // The last MSOSPID given to an SP in this DG + "01, 04, 00, 00]", // The last MSOSPID given to an SP in this DG HexDump.toHex( data ) ); } - public void testFillFields() throws Exception - { + public void testFillFields() { String hexData = "10 00 " + "08 F0 " + "08 00 00 00 " + @@ -55,8 +52,7 @@ public class TestEscherDgRecord extends TestCase assertEquals( 1025, r.getLastMSOSPID() ); } - public void testToString() throws Exception - { + public void testToString() { String nl = System.getProperty("line.separator"); String expected = "org.apache.poi.ddf.EscherDgRecord:" + nl + @@ -67,7 +63,7 @@ public class TestEscherDgRecord extends TestCase assertEquals( expected, createRecord().toString() ); } - private EscherDgRecord createRecord() + private static EscherDgRecord createRecord() { EscherDgRecord r = new EscherDgRecord(); r.setOptions( (short) 0x0010 ); @@ -76,5 +72,4 @@ public class TestEscherDgRecord extends TestCase r.setLastMSOSPID(1025); return r; } - } diff --git a/src/testcases/org/apache/poi/ddf/TestEscherDggRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherDggRecord.java index afecbf3c99..8968066959 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherDggRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherDggRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.ddf; import junit.framework.TestCase; @@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead; public class TestEscherDggRecord extends TestCase { - public void testSerialize() throws Exception - { + public void testSerialize() { EscherDggRecord r = createRecord(); byte[] data = new byte[32]; @@ -38,12 +36,11 @@ public class TestEscherDggRecord extends TestCase "02, 00, 00, 00, " + "02, 00, 00, 00, " + "01, 00, 00, 00, " + - "01, 00, 00, 00, 02, 00, 00, 00, ]", + "01, 00, 00, 00, 02, 00, 00, 00]", HexDump.toHex( data ) ); } - public void testFillFields() throws Exception - { + public void testFillFields() { String hexData = "00 00 " + "06 F0 " + "18 00 00 00 " + @@ -66,8 +63,7 @@ public class TestEscherDggRecord extends TestCase assertEquals( 0x02, r.getFileIdClusters()[0].getNumShapeIdsUsed()); } - public void testToString() throws Exception - { + public void testToString() { String nl = System.getProperty("line.separator"); String expected = "org.apache.poi.ddf.EscherDggRecord:" + nl + @@ -82,7 +78,7 @@ public class TestEscherDggRecord extends TestCase assertEquals( expected, createRecord().toString() ); } - private EscherDggRecord createRecord() + private static EscherDggRecord createRecord() { EscherDggRecord r = new EscherDggRecord(); r.setOptions( (short) 0x0000 ); @@ -96,12 +92,9 @@ public class TestEscherDggRecord extends TestCase return r; } - public void testGetRecordSize() throws Exception - { + public void testGetRecordSize() { EscherDggRecord r = new EscherDggRecord(); r.setFileIdClusters(new EscherDggRecord.FileIdCluster[] { new EscherDggRecord.FileIdCluster(0,0) } ); assertEquals(32,r.getRecordSize()); - } - } diff --git a/src/testcases/org/apache/poi/ddf/TestEscherOptRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherOptRecord.java index fe996f636b..a96ba84968 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherOptRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherOptRecord.java @@ -24,20 +24,16 @@ import org.apache.poi.util.HexDump; import java.io.IOException; import java.util.Arrays; -import java.util.List; -import java.util.Iterator; public class TestEscherOptRecord extends TestCase { - public void testFillFields() throws Exception - { + public void testFillFields() { checkFillFieldsSimple(); checkFillFieldsComplex(); } - private void checkFillFieldsComplex() throws IOException - { + private void checkFillFieldsComplex() { String dataStr = "33 00 " + "0B F0 " + "14 00 00 00 " + @@ -60,9 +56,7 @@ public class TestEscherOptRecord extends TestCase } - private void checkFillFieldsSimple() - throws IOException - { + private void checkFillFieldsSimple() { String dataStr = "33 00 " + // options "0B F0 " + // recordid "12 00 00 00 " + // remaining bytes @@ -83,8 +77,7 @@ public class TestEscherOptRecord extends TestCase assertEquals( prop3, r.getEscherProperty( 2 ) ); } - public void testSerialize() throws Exception - { + public void testSerialize() { checkSerializeSimple(); checkSerializeComplex(); } @@ -111,7 +104,7 @@ public class TestEscherOptRecord extends TestCase "BF, 00, 01, 00, 00, 00, " + "01, 80, 02, 00, 00, 00, " + "BF, 00, 01, 00, 00, 00, " + - "01, 02, ]"; + "01, 02]"; assertEquals( dataStr, HexDump.toHex(data) ); } @@ -135,13 +128,12 @@ public class TestEscherOptRecord extends TestCase "12, 00, 00, 00, " + "BF, 00, 01, 00, 00, 00, " + "81, 01, 09, 00, 00, 08, " + - "C0, 01, 40, 00, 00, 08, ]"; + "C0, 01, 40, 00, 00, 08]"; assertEquals( dataStr, HexDump.toHex(data) ); assertEquals( 26, bytesWritten ); } - public void testToString() throws Exception - { + public void testToString() { String nl = System.getProperty("line.separator"); EscherOptRecord r = new EscherOptRecord(); r.setOptions((short)0x000F); @@ -162,8 +154,8 @@ public class TestEscherOptRecord extends TestCase * Test serialisation of a particually complex example * This test is currently broken! */ - public void testComplexSerialise() throws Exception { - byte[] data = new byte[] { + public void testComplexSerialise() { + byte[] data = { 0x53, 0x01, 0x0B, 0xF0-256, 0x9C-256, 0x01, 0x00, 0x00, // Simple data follows 0x42, 0x01, 0x49, 0x00, 0x00, 0x00, // SP @ 8 @@ -263,7 +255,7 @@ public class TestEscherOptRecord extends TestCase * * See Bug 41946 for details. */ - public void test41946() throws IOException { + public void test41946() { String dataStr1 = "03 08 0B F0 00 03 00 00 81 00 30 65 01 00 82 00 98 B2 00 00 83 00 30 65 01 " + "00 84 00 98 B2 00 00 85 00 00 00 00 00 87 00 01 00 00 00 88 00 00 00 00 00 " + @@ -315,7 +307,7 @@ public class TestEscherOptRecord extends TestCase * Test that EscherOptRecord can properly read/write array properties * with empty complex part. */ - public void testEmptyArrayProperty() throws IOException { + public void testEmptyArrayProperty() { EscherOptRecord r = new EscherOptRecord(); EscherArrayProperty p = new EscherArrayProperty((short)(EscherProperties.FILL__SHADECOLORS + 0x8000), new byte[0] ); assertEquals(0, p.getNumberOfElementsInArray()); diff --git a/src/testcases/org/apache/poi/ddf/TestEscherPropertyFactory.java b/src/testcases/org/apache/poi/ddf/TestEscherPropertyFactory.java index f79b27c637..aef2c72add 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherPropertyFactory.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherPropertyFactory.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.ddf; import junit.framework.TestCase; @@ -30,8 +29,7 @@ import org.apache.poi.util.HexDump; */ public class TestEscherPropertyFactory extends TestCase { - public void testCreateProperties() throws Exception - { + public void testCreateProperties() { String dataStr = "41 C1 " + // propid, complex ind "03 00 00 00 " + // size of complex property "01 00 " + // propid, complex ind @@ -46,15 +44,11 @@ public class TestEscherPropertyFactory extends TestCase List props = f.createProperties( data, 0, (short)3 ); EscherComplexProperty p1 = (EscherComplexProperty) props.get( 0 ); assertEquals( (short)0xC141, p1.getId() ); - assertEquals( "[01, 02, 03, ]", HexDump.toHex( p1.getComplexData() ) ); + assertEquals( "[01, 02, 03]", HexDump.toHex( p1.getComplexData() ) ); EscherComplexProperty p3 = (EscherComplexProperty) props.get( 2 ); assertEquals( (short)0xC141, p3.getId() ); - assertEquals( "[01, 02, 03, ]", HexDump.toHex( p3.getComplexData() ) ); - - + assertEquals( "[01, 02, 03]", HexDump.toHex( p3.getComplexData() ) ); } - - - } + diff --git a/src/testcases/org/apache/poi/ddf/TestEscherSpRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherSpRecord.java index 4521727e0a..b4ab900ea9 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherSpRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherSpRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.ddf; import junit.framework.TestCase; @@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead; public class TestEscherSpRecord extends TestCase { - public void testSerialize() throws Exception - { + public void testSerialize() { EscherSpRecord r = createRecord(); byte[] data = new byte[16]; @@ -35,12 +33,11 @@ public class TestEscherSpRecord extends TestCase "0A, F0, " + "08, 00, 00, 00, " + "00, 04, 00, 00, " + - "05, 00, 00, 00, ]", + "05, 00, 00, 00]", HexDump.toHex( data ) ); } - public void testFillFields() throws Exception - { + public void testFillFields() { String hexData = "02 00 " + "0A F0 " + "08 00 00 00 " + @@ -55,8 +52,7 @@ public class TestEscherSpRecord extends TestCase assertEquals( 0x05, r.getFlags() ); } - public void testToString() throws Exception - { + public void testToString() { String nl = System.getProperty("line.separator"); String expected = "org.apache.poi.ddf.EscherSpRecord:" + nl + @@ -67,7 +63,7 @@ public class TestEscherSpRecord extends TestCase assertEquals( expected, createRecord().toString() ); } - private EscherSpRecord createRecord() + private static EscherSpRecord createRecord() { EscherSpRecord r = new EscherSpRecord(); r.setOptions( (short) 0x0002 ); diff --git a/src/testcases/org/apache/poi/ddf/TestEscherSpgrRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherSpgrRecord.java index f640183097..9feca0abbc 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherSpgrRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherSpgrRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.ddf; import junit.framework.TestCase; @@ -24,8 +23,7 @@ import org.apache.poi.util.HexRead; public class TestEscherSpgrRecord extends TestCase { - public void testSerialize() throws Exception - { + public void testSerialize() { EscherSpgrRecord r = createRecord(); byte[] data = new byte[24]; @@ -37,12 +35,11 @@ public class TestEscherSpgrRecord extends TestCase "01, 00, 00, 00, " + // x "02, 00, 00, 00, " + // y "03, 00, 00, 00, " + // width - "04, 00, 00, 00, ]", // height + "04, 00, 00, 00]", // height HexDump.toHex( data ) ); } - public void testFillFields() throws Exception - { + public void testFillFields() { String hexData = "10 00 " + "09 F0 " + "10 00 00 00 " + @@ -61,8 +58,7 @@ public class TestEscherSpgrRecord extends TestCase assertEquals( 4, r.getRectY2() ); } - public void testToString() throws Exception - { + public void testToString() { String nl = System.getProperty("line.separator"); String expected = "org.apache.poi.ddf.EscherSpgrRecord:" + nl + @@ -72,11 +68,10 @@ public class TestEscherSpgrRecord extends TestCase " RectY: 2" + nl + " RectWidth: 3" + nl + " RectHeight: 4" + nl; - ; assertEquals( expected, createRecord().toString() ); } - private EscherSpgrRecord createRecord() + private static EscherSpgrRecord createRecord() { EscherSpgrRecord r = new EscherSpgrRecord(); r.setOptions( (short) 0x0010 ); @@ -87,5 +82,4 @@ public class TestEscherSpgrRecord extends TestCase r.setRectY2(4); return r; } - } diff --git a/src/testcases/org/apache/poi/ddf/TestEscherSplitMenuColorsRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherSplitMenuColorsRecord.java index 1163575eb8..d9a9080713 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherSplitMenuColorsRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherSplitMenuColorsRecord.java @@ -24,8 +24,7 @@ import org.apache.poi.util.HexRead; public class TestEscherSplitMenuColorsRecord extends TestCase { - public void testSerialize() throws Exception - { + public void testSerialize() { EscherSplitMenuColorsRecord r = createRecord(); byte[] data = new byte[24]; @@ -37,12 +36,11 @@ public class TestEscherSplitMenuColorsRecord extends TestCase "02, 04, 00, 00, " + "02, 00, 00, 00, " + "02, 00, 00, 00, " + - "01, 00, 00, 00, ]", + "01, 00, 00, 00]", HexDump.toHex( data ) ); } - public void testFillFields() throws Exception - { + public void testFillFields() { String hexData = "40 00 " + "1E F1 " + "10 00 00 00 " + @@ -61,8 +59,7 @@ public class TestEscherSplitMenuColorsRecord extends TestCase assertEquals( 0x01, r.getColor4() ); } - public void testToString() throws Exception - { + public void testToString() { String nl = System.getProperty("line.separator"); String expected = "org.apache.poi.ddf.EscherSplitMenuColorsRecord:" + nl + @@ -76,7 +73,7 @@ public class TestEscherSplitMenuColorsRecord extends TestCase assertEquals( expected, createRecord().toString() ); } - private EscherSplitMenuColorsRecord createRecord() + private static EscherSplitMenuColorsRecord createRecord() { EscherSplitMenuColorsRecord r = new EscherSplitMenuColorsRecord(); r.setOptions( (short) 0x0040 ); @@ -87,5 +84,4 @@ public class TestEscherSplitMenuColorsRecord extends TestCase r.setColor4( 0x1 ); return r; } - } diff --git a/src/testcases/org/apache/poi/ddf/TestUnknownEscherRecord.java b/src/testcases/org/apache/poi/ddf/TestUnknownEscherRecord.java index 9845a58891..d8c36e1a5c 100644 --- a/src/testcases/org/apache/poi/ddf/TestUnknownEscherRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestUnknownEscherRecord.java @@ -24,8 +24,7 @@ import org.apache.poi.util.HexDump; public class TestUnknownEscherRecord extends TestCase { - public void testFillFields() throws Exception - { + public void testFillFields() { String testData = "0F 02 " + // options "11 F1 " + // record id @@ -82,15 +81,14 @@ public class TestUnknownEscherRecord extends TestCase } - public void testSerialize() throws Exception - { + public void testSerialize() { UnknownEscherRecord r = new UnknownEscherRecord(); r.setOptions( (short) 0x1234 ); r.setRecordId( (short) 0xF112 ); byte[] data = new byte[8]; r.serialize( 0, data, new NullEscherSerializationListener() ); - assertEquals( "[34, 12, 12, F1, 00, 00, 00, 00, ]", HexDump.toHex( data ) ); + assertEquals( "[34, 12, 12, F1, 00, 00, 00, 00]", HexDump.toHex( data ) ); EscherRecord childRecord = new UnknownEscherRecord(); childRecord.setOptions( (short) 0x9999 ); @@ -100,11 +98,10 @@ public class TestUnknownEscherRecord extends TestCase data = new byte[16]; r.serialize( 0, data, new NullEscherSerializationListener() ); - assertEquals( "[3F, 12, 12, F1, 08, 00, 00, 00, 99, 99, 01, FF, 00, 00, 00, 00, ]", HexDump.toHex( data ) ); + assertEquals( "[3F, 12, 12, F1, 08, 00, 00, 00, 99, 99, 01, FF, 00, 00, 00, 00]", HexDump.toHex( data ) ); } - public void testToString() throws Exception - { + public void testToString() { UnknownEscherRecord r = new UnknownEscherRecord(); r.setOptions( (short) 0x1234 ); r.setRecordId( (short) 0xF112 ); @@ -119,6 +116,4 @@ public class TestUnknownEscherRecord extends TestCase " numchildren: 0" + nl , r.toString() ); } - - } diff --git a/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls b/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls Binary files differindex e4c8e42dcb..f806c792f6 100644 --- a/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls +++ b/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls diff --git a/src/testcases/org/apache/poi/hssf/data/IndexFunctionTestCaseData.xls b/src/testcases/org/apache/poi/hssf/data/IndexFunctionTestCaseData.xls Binary files differnew file mode 100644 index 0000000000..9dcde61772 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/data/IndexFunctionTestCaseData.xls diff --git a/src/testcases/org/apache/poi/hssf/data/testRVA.xls b/src/testcases/org/apache/poi/hssf/data/testRVA.xls Binary files differindex 17aa9fd710..f24b6926e8 100644 --- a/src/testcases/org/apache/poi/hssf/data/testRVA.xls +++ b/src/testcases/org/apache/poi/hssf/data/testRVA.xls diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java index c9fea7f1e4..e1ffc2538d 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java @@ -22,10 +22,12 @@ import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.model.FormulaParser.FormulaParseException; +import org.apache.poi.hssf.record.constant.ErrorConstant; import org.apache.poi.hssf.record.formula.AbstractFunctionPtg; import org.apache.poi.hssf.record.formula.AddPtg; import org.apache.poi.hssf.record.formula.AreaI; import org.apache.poi.hssf.record.formula.AreaPtg; +import org.apache.poi.hssf.record.formula.ArrayPtg; import org.apache.poi.hssf.record.formula.AttrPtg; import org.apache.poi.hssf.record.formula.BoolPtg; import org.apache.poi.hssf.record.formula.ConcatPtg; @@ -48,6 +50,7 @@ import org.apache.poi.hssf.record.formula.SubtractPtg; import org.apache.poi.hssf.record.formula.UnaryMinusPtg; import org.apache.poi.hssf.record.formula.UnaryPlusPtg; import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFErrorConstants; import org.apache.poi.hssf.usermodel.HSSFName; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; @@ -862,4 +865,18 @@ public final class TestFormulaParser extends TestCase { assertEquals(65535, aptg.getLastRow()); } + public void testParseArray() { + Ptg[] ptgs; + ptgs = parseFormula("mode({1,2,2,#REF!;FALSE,3,3,2})"); + assertEquals(2, ptgs.length); + Ptg ptg0 = ptgs[0]; + assertEquals(ArrayPtg.class, ptg0.getClass()); + assertEquals("{1.0,2.0,2.0,#REF!;FALSE,3.0,3.0,2.0}", ptg0.toFormulaString(null)); + + ArrayPtg aptg = (ArrayPtg) ptg0; + Object[][] values = aptg.getTokenArrayValues(); + assertEquals(ErrorConstant.valueOf(HSSFErrorConstants.ERROR_REF), values[0][3]); + assertEquals(Boolean.FALSE, values[1][0]); + + } }
\ No newline at end of file diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParserEval.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParserEval.java index d89ffee3eb..bd40e3d05a 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParserEval.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParserEval.java @@ -85,7 +85,7 @@ public final class TestFormulaParserEval extends TestCase { sheet.createRow(32768).createCell(0).setCellValue(31); sheet.createRow(32769).createCell(0).setCellValue(11); - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); CellValue result; try { result = fe.evaluate(cell); diff --git a/src/testcases/org/apache/poi/hssf/model/TestRVA.java b/src/testcases/org/apache/poi/hssf/model/TestRVA.java index ca74c6e2db..1ec7c92ae9 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestRVA.java +++ b/src/testcases/org/apache/poi/hssf/model/TestRVA.java @@ -61,8 +61,10 @@ public final class TestRVA extends TestCase { try { confirmCell(cell, formula, wb); } catch (AssertionFailedError e) { + System.out.flush(); System.err.println("Problem with row[" + rowIx + "] formula '" + formula + "'"); System.err.println(e.getMessage()); + System.err.flush(); countFailures++; } catch (RuntimeException e) { System.err.println("Problem with row[" + rowIx + "] formula '" + formula + "'"); @@ -104,8 +106,8 @@ public final class TestRVA extends TestCase { if (excelPtg.getClass() != poiPtg.getClass()) { hasMismatch = true; sb.append(" mismatch token type[" + i + "] " + getShortClassName(excelPtg) + " " - + getOperandClassName(excelPtg) + " - " + getShortClassName(poiPtg) + " " - + getOperandClassName(poiPtg)); + + excelPtg.getRVAType() + " - " + getShortClassName(poiPtg) + " " + + poiPtg.getRVAType()); sb.append(NEW_LINE); continue; } @@ -113,16 +115,16 @@ public final class TestRVA extends TestCase { continue; } sb.append(" token[" + i + "] " + excelPtg.toString() + " " - + getOperandClassName(excelPtg)); + + excelPtg.getRVAType()); if (excelPtg.getPtgClass() != poiPtg.getPtgClass()) { hasMismatch = true; - sb.append(" - was " + getOperandClassName(poiPtg)); + sb.append(" - was " + poiPtg.getRVAType()); } sb.append(NEW_LINE); } if (false) { // set 'true' to see trace of RVA values - System.out.println(formula); + System.out.println(formulaCell.getRowIndex() + " " + formula); System.out.println(sb.toString()); } if (hasMismatch) { @@ -135,14 +137,4 @@ public final class TestRVA extends TestCase { int pos = cn.lastIndexOf('.'); return cn.substring(pos + 1); } - - private static String getOperandClassName(Ptg ptg) { - byte ptgClass = ptg.getPtgClass(); - switch (ptgClass) { - case Ptg.CLASS_REF: return "R"; - case Ptg.CLASS_VALUE: return "V"; - case Ptg.CLASS_ARRAY: return "A"; - } - throw new RuntimeException("Unknown operand class (" + ptgClass + ")"); - } } diff --git a/src/testcases/org/apache/poi/hssf/model/TestSheet.java b/src/testcases/org/apache/poi/hssf/model/TestSheet.java index ca6a10f45e..3bc79299b5 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestSheet.java +++ b/src/testcases/org/apache/poi/hssf/model/TestSheet.java @@ -214,7 +214,9 @@ public final class TestSheet extends TestCase { records.add(new DimensionsRecord()); records.add(new RowRecord(0)); records.add(new RowRecord(1)); - records.add(new FormulaRecord()); + FormulaRecord formulaRecord = new FormulaRecord(); + formulaRecord.setCachedResultTypeString(); + records.add(formulaRecord); records.add(new StringRecord()); records.add(new RowRecord(2)); records.add(createWindow2Record()); @@ -357,7 +359,7 @@ public final class TestSheet extends TestCase { xfindex = sheet.getXFIndexForColAt((short) 1); assertEquals(DEFAULT_IDX, xfindex); - ColumnInfoRecord nci = ColumnInfoRecordsAggregate.createColInfo(); + ColumnInfoRecord nci = new ColumnInfoRecord(); sheet._columnInfos.insertColumn(nci); // single column ColumnInfoRecord @@ -567,7 +569,7 @@ public final class TestSheet extends TestCase { sheet.setMargin(HSSFSheet.LeftMargin, 0.3); try { - row.createCell((short) 0); + row.createCell(0); } catch (IllegalStateException e) { if (e.getMessage().equals("Cannot create value records before row records exist")) { throw new AssertionFailedError("Identified bug 45717"); @@ -576,4 +578,3 @@ public final class TestSheet extends TestCase { } } } - diff --git a/src/testcases/org/apache/poi/hssf/model/TestSheetAdditional.java b/src/testcases/org/apache/poi/hssf/model/TestSheetAdditional.java index 0962b9148a..477908dc72 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestSheetAdditional.java +++ b/src/testcases/org/apache/poi/hssf/model/TestSheetAdditional.java @@ -20,7 +20,6 @@ package org.apache.poi.hssf.model; import junit.framework.TestCase; import org.apache.poi.hssf.record.ColumnInfoRecord; -import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate; /** * @author Tony Poppleton @@ -29,7 +28,7 @@ public final class TestSheetAdditional extends TestCase { public void testGetCellWidth() { Sheet sheet = Sheet.createSheet(); - ColumnInfoRecord nci = ColumnInfoRecordsAggregate.createColInfo(); + ColumnInfoRecord nci = new ColumnInfoRecord(); // Prepare test model nci.setFirstColumn((short)5); @@ -55,5 +54,4 @@ public final class TestSheetAdditional extends TestCase { assertEquals((short)100,sheet.getColumnWidth((short)9)); assertEquals((short)100,sheet.getColumnWidth((short)10)); } - } diff --git a/src/testcases/org/apache/poi/hssf/record/TestDrawingGroupRecord.java b/src/testcases/org/apache/poi/hssf/record/TestDrawingGroupRecord.java index d248baf304..17d6475e8b 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestDrawingGroupRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestDrawingGroupRecord.java @@ -22,14 +22,11 @@ import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherSpRecord; import org.apache.poi.util.HexDump; -public class TestDrawingGroupRecord extends TestCase -{ - static final int MAX_RECORD_SIZE = 8228; +public final class TestDrawingGroupRecord extends TestCase { + private static final int MAX_RECORD_SIZE = 8228; private static final int MAX_DATA_SIZE = MAX_RECORD_SIZE - 4; - public void testGetRecordSize() - throws Exception - { + public void testGetRecordSize() { DrawingGroupRecord r = new DrawingGroupRecord(); assertEquals(4, r.getRecordSize()); @@ -48,7 +45,7 @@ public class TestDrawingGroupRecord extends TestCase byte[] data = new byte[28]; int size = r.serialize(0, data); - assertEquals("[EB, 00, 18, 00, 0F, 00, 00, F0, 10, 00, 00, 00, 11, 11, 0A, F0, 08, 00, 00, 00, FF, FF, FF, FF, FF, FF, FF, FF, ]", HexDump.toHex(data)); + assertEquals("[EB, 00, 18, 00, 0F, 00, 00, F0, 10, 00, 00, 00, 11, 11, 0A, F0, 08, 00, 00, 00, FF, FF, FF, FF, FF, FF, FF, FF]", HexDump.toHex(data)); assertEquals(28, size); assertEquals(24, dggContainer.getRecordSize()); @@ -65,8 +62,7 @@ public class TestDrawingGroupRecord extends TestCase assertEquals( MAX_RECORD_SIZE * 2 + 5, r.getRecordSize() ); } - public void testSerialize() throws Exception - { + public void testSerialize() { // Check under max record size DrawingGroupRecord r = new DrawingGroupRecord(); byte[] rawData = new byte[100]; @@ -76,7 +72,7 @@ public class TestDrawingGroupRecord extends TestCase byte[] buffer = new byte[r.getRecordSize()]; int size = r.serialize( 0, buffer ); assertEquals( 104, size ); - assertEquals("[EB, 00, 64, 00, 64, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, C8, ]", HexDump.toHex(buffer)); + assertEquals("[EB, 00, 64, 00, 64, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, C8]", HexDump.toHex(buffer)); // check at max record size rawData = new byte[MAX_DATA_SIZE]; @@ -92,8 +88,8 @@ public class TestDrawingGroupRecord extends TestCase buffer = new byte[r.getRecordSize()]; size = r.serialize( 0, buffer ); assertEquals( MAX_RECORD_SIZE + 5, size ); - assertEquals( "[EB, 00, 20, 20, ]", HexDump.toHex(cut(buffer, 0, 4) )); - assertEquals( "[00, EB, 00, 01, 00, FF, ]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE - 1, MAX_RECORD_SIZE + 5) )); + assertEquals( "[EB, 00, 20, 20]", HexDump.toHex(cut(buffer, 0, 4) )); + assertEquals( "[00, EB, 00, 01, 00, FF]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE - 1, MAX_RECORD_SIZE + 5) )); // check continue record rawData = new byte[MAX_DATA_SIZE * 2 + 1]; @@ -103,9 +99,9 @@ public class TestDrawingGroupRecord extends TestCase size = r.serialize( 0, buffer ); assertEquals( MAX_RECORD_SIZE * 2 + 5, size ); assertEquals( MAX_RECORD_SIZE * 2 + 5, r.getRecordSize() ); - assertEquals( "[EB, 00, 20, 20, ]", HexDump.toHex(cut(buffer, 0, 4) )); - assertEquals( "[EB, 00, 20, 20, ]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE, MAX_RECORD_SIZE + 4) )); - assertEquals( "[3C, 00, 01, 00, FF, ]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE * 2, MAX_RECORD_SIZE * 2 + 5) )); + assertEquals( "[EB, 00, 20, 20]", HexDump.toHex(cut(buffer, 0, 4) )); + assertEquals( "[EB, 00, 20, 20]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE, MAX_RECORD_SIZE + 4) )); + assertEquals( "[3C, 00, 01, 00, FF]", HexDump.toHex(cut(buffer, MAX_RECORD_SIZE * 2, MAX_RECORD_SIZE * 2 + 5) )); // check continue record rawData = new byte[664532]; @@ -116,7 +112,7 @@ public class TestDrawingGroupRecord extends TestCase assertEquals( 664856, r.getRecordSize() ); } - private byte[] cut( byte[] data, int fromInclusive, int toExclusive ) + private static byte[] cut( byte[] data, int fromInclusive, int toExclusive ) { int length = toExclusive - fromInclusive; byte[] result = new byte[length]; @@ -124,8 +120,7 @@ public class TestDrawingGroupRecord extends TestCase return result; } - public void testGrossSizeFromDataSize() throws Exception - { + public void testGrossSizeFromDataSize() { for (int i = 0; i < MAX_RECORD_SIZE * 4; i += 11) { //System.out.print( "data size = " + i + ", gross size = " + DrawingGroupRecord.grossSizeFromDataSize( i ) ); @@ -139,6 +134,4 @@ public class TestDrawingGroupRecord extends TestCase assertEquals( MAX_RECORD_SIZE * 2, DrawingGroupRecord.grossSizeFromDataSize( MAX_DATA_SIZE * 2 ) ); assertEquals( MAX_RECORD_SIZE * 2 + 5, DrawingGroupRecord.grossSizeFromDataSize( MAX_DATA_SIZE * 2 + 1 ) ); } - - } diff --git a/src/testcases/org/apache/poi/hssf/record/TestEscherAggregate.java b/src/testcases/org/apache/poi/hssf/record/TestEscherAggregate.java index 21be7ad1d0..593290b337 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestEscherAggregate.java +++ b/src/testcases/org/apache/poi/hssf/record/TestEscherAggregate.java @@ -133,7 +133,7 @@ public class TestEscherAggregate extends TestCase byte[] data = new byte[112]; int bytesWritten = aggregate.serialize( 0, data ); assertEquals( 112, bytesWritten ); - assertEquals( "[EC, 00, 40, 00, 0F, 00, 00, 00, 58, 00, 00, 00, 0F, 00, 04, F0, 10, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00, EC, 00, 20, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00, ]", + assertEquals( "[EC, 00, 40, 00, 0F, 00, 00, 00, 58, 00, 00, 00, 0F, 00, 04, F0, 10, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00, EC, 00, 20, 00, 0F, 00, 04, F0, 18, 00, 00, 00, 00, 00, 0A, F0, 08, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 11, F0, 00, 00, 00, 00, 5D, 00, 00, 00]", HexDump.toHex( data ) ); } diff --git a/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java b/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java index 0d96d737cf..4696539f23 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestFormulaRecord.java @@ -26,12 +26,14 @@ import org.apache.poi.hssf.record.formula.FuncVarPtg; import org.apache.poi.hssf.record.formula.IntPtg; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.hssf.record.formula.RefPtg; +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFErrorConstants; /** * Tests the serialization and deserialization of the FormulaRecord - * class works correctly. + * class works correctly. * - * @author Andrew C. Oliver + * @author Andrew C. Oliver */ public final class TestFormulaRecord extends TestCase { @@ -40,52 +42,66 @@ public final class TestFormulaRecord extends TestCase { record.setColumn((short)0); record.setRow(1); record.setXFIndex((short)4); - + assertEquals(record.getColumn(),0); assertEquals(record.getRow(), 1); assertEquals(record.getXFIndex(),4); } - + /** * Make sure a NAN value is preserved - * This formula record is a representation of =1/0 at row 0, column 0 + * This formula record is a representation of =1/0 at row 0, column 0 */ public void testCheckNanPreserve() { - byte[] formulaByte = new byte[29]; - - formulaByte[4] = (byte)0x0F; - formulaByte[6] = (byte)0x02; - formulaByte[8] = (byte)0x07; - formulaByte[12] = (byte)0xFF; - formulaByte[13] = (byte)0xFF; - formulaByte[18] = (byte)0xE0; - formulaByte[19] = (byte)0xFC; - formulaByte[20] = (byte)0x07; - formulaByte[22] = (byte)0x1E; - formulaByte[23] = (byte)0x01; - formulaByte[25] = (byte)0x1E; - formulaByte[28] = (byte)0x06; - + byte[] formulaByte = { + 0, 0, 0, 0, + 0x0F, 0x00, + + // 8 bytes cached number is a 'special value' in this case + 0x02, // special cached value type 'error' + 0x00, + HSSFErrorConstants.ERROR_DIV_0, + 0x00, + 0x00, + 0x00, + (byte)0xFF, + (byte)0xFF, + + 0x00, + 0x00, + 0x00, + 0x00, + + (byte)0xE0, //18 + (byte)0xFC, + // Ptgs + 0x07, 0x00, // encoded length + 0x1E, 0x01, 0x00, // IntPtg(1) + 0x1E, 0x00, 0x00, // IntPtg(0) + 0x06, // DividePtg + + }; + FormulaRecord record = new FormulaRecord(new TestcaseRecordInputStream(FormulaRecord.sid, (short)29, formulaByte)); assertEquals("Row", 0, record.getRow()); - assertEquals("Column", 0, record.getColumn()); - assertTrue("Value is not NaN", Double.isNaN(record.getValue())); - + assertEquals("Column", 0, record.getColumn()); + assertEquals(HSSFCell.CELL_TYPE_ERROR, record.getCachedResultType()); + byte[] output = record.serialize(); assertEquals("Output size", 33, output.length); //includes sid+recordlength - + for (int i = 5; i < 13;i++) { assertEquals("FormulaByte NaN doesn't match", formulaByte[i], output[i+4]); } } - + /** * Tests to see if the shared formula cells properly reserialize the expPtg * */ public void testExpFormula() { byte[] formulaByte = new byte[27]; - + formulaByte[4] =(byte)0x0F; formulaByte[14]=(byte)0x08; formulaByte[18]=(byte)0xE0; @@ -99,13 +115,13 @@ public final class TestFormulaRecord extends TestCase { assertEquals("Output size", 31, output.length); //includes sid+recordlength assertEquals("Offset 22", 1, output[26]); } - + public void testWithConcat() { // =CHOOSE(2,A2,A3,A4) byte[] data = { 6, 0, 68, 0, 1, 0, 1, 0, 15, 0, 0, 0, 0, 0, 0, 0, 57, - 64, 0, 0, 12, 0, 12, -4, 46, 0, + 64, 0, 0, 12, 0, 12, -4, 46, 0, 30, 2, 0, // Int - 2 25, 4, 3, 0, // Attr 8, 0, 17, 0, 26, 0, // jumpTable @@ -115,14 +131,14 @@ public final class TestFormulaRecord extends TestCase { 36, 2, 0, 0, -64, // Ref - A3 25, 8, 12, 0, // Attr 36, 3, 0, 0, -64, // Ref - A4 - 25, 8, 3, 0, // Attr + 25, 8, 3, 0, // Attr 66, 4, 100, 0 // CHOOSE }; RecordInputStream inp = new RecordInputStream( new ByteArrayInputStream(data)); inp.nextRecord(); - + FormulaRecord fr = new FormulaRecord(inp); - + Ptg[] ptgs = fr.getParsedExpression(); assertEquals(9, ptgs.length); assertEquals(IntPtg.class, ptgs[0].getClass()); @@ -134,7 +150,7 @@ public final class TestFormulaRecord extends TestCase { assertEquals(RefPtg.class, ptgs[6].getClass()); assertEquals(AttrPtg.class, ptgs[7].getClass()); assertEquals(FuncVarPtg.class, ptgs[8].getClass()); - + FuncVarPtg choose = (FuncVarPtg)ptgs[8]; assertEquals("CHOOSE", choose.getName()); } diff --git a/src/testcases/org/apache/poi/hssf/record/aggregates/TestColumnInfoRecordsAggregate.java b/src/testcases/org/apache/poi/hssf/record/aggregates/TestColumnInfoRecordsAggregate.java index 2988290616..6c981cab5e 100644 --- a/src/testcases/org/apache/poi/hssf/record/aggregates/TestColumnInfoRecordsAggregate.java +++ b/src/testcases/org/apache/poi/hssf/record/aggregates/TestColumnInfoRecordsAggregate.java @@ -17,9 +17,16 @@ package org.apache.poi.hssf.record.aggregates; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.AssertionFailedError; import junit.framework.TestCase; + import org.apache.poi.hssf.record.ColumnInfoRecord; +import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RecordBase; +import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor; /** * @author Glen Stampoultzis @@ -28,11 +35,11 @@ public final class TestColumnInfoRecordsAggregate extends TestCase { public void testGetRecordSize() { ColumnInfoRecordsAggregate agg = new ColumnInfoRecordsAggregate(); - agg.insertColumn(createColumn(1, 3)); - agg.insertColumn(createColumn(4, 7)); - agg.insertColumn(createColumn(8, 8)); + agg.insertColumn(createColInfo(1, 3)); + agg.insertColumn(createColInfo(4, 7)); + agg.insertColumn(createColInfo(8, 8)); agg.groupColumnRange((short) 2, (short) 5, true); - assertEquals(6, agg.getNumColumns()); + assertEquals(4, agg.getNumColumns()); confirmSerializedSize(agg); @@ -48,10 +55,91 @@ public final class TestColumnInfoRecordsAggregate extends TestCase { assertEquals(estimatedSize, serializedSize); } - private static ColumnInfoRecord createColumn(int firstCol, int lastCol) { + private static ColumnInfoRecord createColInfo(int firstCol, int lastCol) { ColumnInfoRecord columnInfoRecord = new ColumnInfoRecord(); columnInfoRecord.setFirstColumn((short) firstCol); columnInfoRecord.setLastColumn((short) lastCol); return columnInfoRecord; } -}
\ No newline at end of file + + private static final class CIRCollector implements RecordVisitor { + + private List _list; + public CIRCollector() { + _list = new ArrayList(); + } + public void visitRecord(Record r) { + _list.add(r); + } + public static ColumnInfoRecord[] getRecords(ColumnInfoRecordsAggregate agg) { + CIRCollector circ = new CIRCollector(); + agg.visitContainedRecords(circ); + List list = circ._list; + ColumnInfoRecord[] result = new ColumnInfoRecord[list.size()]; + list.toArray(result); + return result; + } + } + + public void testGroupColumns_bug45639() { + ColumnInfoRecordsAggregate agg = new ColumnInfoRecordsAggregate(); + agg.groupColumnRange( 7, 9, true); + agg.groupColumnRange( 4, 12, true); + try { + agg.groupColumnRange( 1, 15, true); + } catch (ArrayIndexOutOfBoundsException e) { + throw new AssertionFailedError("Identified bug 45639"); + } + ColumnInfoRecord[] cirs = CIRCollector.getRecords(agg); + assertEquals(5, cirs.length); + confirmCIR(cirs, 0, 1, 3, 1, false, false); + confirmCIR(cirs, 1, 4, 6, 2, false, false); + confirmCIR(cirs, 2, 7, 9, 3, false, false); + confirmCIR(cirs, 3, 10, 12, 2, false, false); + confirmCIR(cirs, 4, 13, 15, 1, false, false); + } + + /** + * Check that an inner group remains hidden + */ + public void testHiddenAfterExpanding() { + ColumnInfoRecordsAggregate agg = new ColumnInfoRecordsAggregate(); + agg.groupColumnRange(1, 15, true); + agg.groupColumnRange(4, 12, true); + + ColumnInfoRecord[] cirs; + + // collapse both inner and outer groups + agg.collapseColumn(6); + agg.collapseColumn(3); + + cirs = CIRCollector.getRecords(agg); + assertEquals(5, cirs.length); + confirmCIR(cirs, 0, 1, 3, 1, true, false); + confirmCIR(cirs, 1, 4, 12, 2, true, false); + confirmCIR(cirs, 2, 13, 13, 1, true, true); + confirmCIR(cirs, 3, 14, 15, 1, true, false); + confirmCIR(cirs, 4, 16, 16, 0, false, true); + + // just expand the inner group + agg.expandColumn(6); + + cirs = CIRCollector.getRecords(agg); + assertEquals(4, cirs.length); + if (!cirs[1].getHidden()) { + throw new AssertionFailedError("Inner group should still be hidden"); + } + confirmCIR(cirs, 0, 1, 3, 1, true, false); + confirmCIR(cirs, 1, 4, 12, 2, true, false); + confirmCIR(cirs, 2, 13, 15, 1, true, false); + confirmCIR(cirs, 3, 16, 16, 0, false, true); + } + private static void confirmCIR(ColumnInfoRecord[] cirs, int ix, int startColIx, int endColIx, int level, boolean isHidden, boolean isCollapsed) { + ColumnInfoRecord cir = cirs[ix]; + assertEquals("startColIx", startColIx, cir.getFirstColumn()); + assertEquals("endColIx", endColIx, cir.getLastColumn()); + assertEquals("level", level, cir.getOutlineLevel()); + assertEquals("hidden", isHidden, cir.getHidden()); + assertEquals("collapsed", isCollapsed, cir.getCollapsed()); + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java b/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java index 9b95356027..978f400fcd 100644 --- a/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java +++ b/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java @@ -30,6 +30,7 @@ public final class TestFormulaRecordAggregate extends TestCase { public void testBasic() throws Exception { FormulaRecord f = new FormulaRecord(); + f.setCachedResultTypeString(); StringRecord s = new StringRecord(); s.setString("abc"); FormulaRecordAggregate fagg = new FormulaRecordAggregate(f, s, SharedValueManager.EMPTY); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java index 9e5a340045..2455c87ab9 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestArrayPtg.java @@ -35,9 +35,8 @@ import junit.framework.TestCase; public final class TestArrayPtg extends TestCase { private static final byte[] ENCODED_PTG_DATA = { - 0x40, 0x00, - 0x08, 0x00, - 0, 0, 0, 0, 0, 0, 0, 0, + 0x40, + 0, 0, 0, 0, 0, 0, 0, }; private static final byte[] ENCODED_CONSTANT_DATA = { 2, // 3 columns @@ -60,15 +59,15 @@ public final class TestArrayPtg extends TestCase { ptg.readTokenValues(new TestcaseRecordInputStream(0, ENCODED_CONSTANT_DATA)); assertEquals(3, ptg.getColumnCount()); assertEquals(2, ptg.getRowCount()); - Object[] values = ptg.getTokenArrayValues(); - assertEquals(6, values.length); + Object[][] values = ptg.getTokenArrayValues(); + assertEquals(2, values.length); - assertEquals(Boolean.TRUE, values[0]); - assertEquals(new UnicodeString("ABCD"), values[1]); - assertEquals(new Double(0), values[3]); - assertEquals(Boolean.FALSE, values[4]); - assertEquals(new UnicodeString("FG"), values[5]); + assertEquals(Boolean.TRUE, values[0][0]); + assertEquals(new UnicodeString("ABCD"), values[0][1]); + assertEquals(new Double(0), values[1][0]); + assertEquals(Boolean.FALSE, values[1][1]); + assertEquals(new UnicodeString("FG"), values[1][2]); byte[] outBuf = new byte[ENCODED_CONSTANT_DATA.length]; ptg.writeTokenValueBytes(outBuf, 0); @@ -89,10 +88,10 @@ public final class TestArrayPtg extends TestCase { assertEquals(2, ptg.getRowCount()); assertEquals(0, ptg.getValueIndex(0, 0)); - assertEquals(2, ptg.getValueIndex(1, 0)); - assertEquals(4, ptg.getValueIndex(2, 0)); - assertEquals(1, ptg.getValueIndex(0, 1)); - assertEquals(3, ptg.getValueIndex(1, 1)); + assertEquals(1, ptg.getValueIndex(1, 0)); + assertEquals(2, ptg.getValueIndex(2, 0)); + assertEquals(3, ptg.getValueIndex(0, 1)); + assertEquals(4, ptg.getValueIndex(1, 1)); assertEquals(5, ptg.getValueIndex(2, 1)); } @@ -110,7 +109,7 @@ public final class TestArrayPtg extends TestCase { if (formula.equals("SUM({1.0,6.0,11.0;2.0,7.0,12.0;3.0,8.0,13.0;4.0,9.0,14.0;5.0,10.0,15.0})")) { throw new AssertionFailedError("Identified bug 42564 b"); } - assertEquals("SUM({1.0,2.0,3.0;4.0,5.0,6.0;7.0,8.0,9.0;10.0,11.0,12.0;13.0,14.0,15.0})", formula); + assertEquals("SUM({1.0,2.0,3.0,4.0,5.0;6.0,7.0,8.0,9.0,10.0;11.0,12.0,13.0,14.0,15.0})", formula); } public void testToFormulaString() { @@ -127,7 +126,7 @@ public final class TestArrayPtg extends TestCase { } throw e; } - assertEquals("{TRUE,\"ABCD\";\"E\",0.0;FALSE,\"FG\"}", actualFormula); + assertEquals("{TRUE,\"ABCD\",\"E\";0.0,FALSE,\"FG\"}", actualFormula); } /** @@ -150,6 +149,7 @@ public final class TestArrayPtg extends TestCase { RecordInputStream in = new TestcaseRecordInputStream(ArrayPtg.sid, fullData); Ptg[] ptgs = Ptg.readTokens(ENCODED_PTG_DATA.length, in); + assertEquals(1, ptgs.length); ArrayPtg aPtg = (ArrayPtg) ptgs[0]; assertEquals(operandClass, aPtg.getPtgClass()); } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestExternalFunctionFormulas.java b/src/testcases/org/apache/poi/hssf/record/formula/TestExternalFunctionFormulas.java index c1087c1fc4..7e71889d8b 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestExternalFunctionFormulas.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestExternalFunctionFormulas.java @@ -74,7 +74,7 @@ public final class TestExternalFunctionFormulas extends TestCase { public void testEvaluate() { HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls"); HSSFSheet sheet = wb.getSheetAt(0); - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); confirmCellEval(sheet, 0, 0, fe, "YEARFRAC(B1,C1)", 29.0/90.0); confirmCellEval(sheet, 1, 0, fe, "YEARFRAC(B2,C2)", 0.0); confirmCellEval(sheet, 2, 0, fe, "YEARFRAC(B3,C3,D3)", 0.0); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculatorFromSpreadsheet.java b/src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculatorFromSpreadsheet.java index 2cad8e3620..37d1bb314b 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculatorFromSpreadsheet.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/atp/TestYearFracCalculatorFromSpreadsheet.java @@ -56,7 +56,7 @@ public final class TestYearFracCalculatorFromSpreadsheet extends TestCase { HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("yearfracExamples.xls"); HSSFSheet sheet = wb.getSheetAt(0); - HSSFFormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator(wb); int nSuccess = 0; int nFailures = 0; int nUnexpectedErrors = 0; diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestCircularReferences.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestCircularReferences.java index 07f13a0445..74691b27dd 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestCircularReferences.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestCircularReferences.java @@ -35,9 +35,9 @@ public final class TestCircularReferences extends TestCase { /** * Translates StackOverflowError into AssertionFailedError */ - private static CellValue evaluateWithCycles(HSSFWorkbook wb, HSSFSheet sheet, HSSFCell testCell) + private static CellValue evaluateWithCycles(HSSFWorkbook wb, HSSFCell testCell) throws AssertionFailedError { - HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb); try { return evaluator.evaluate(testCell); } catch (StackOverflowError e) { @@ -75,7 +75,7 @@ public final class TestCircularReferences extends TestCase { // arguments before invoking operators, POI must handle such potential cycles gracefully. - CellValue cellValue = evaluateWithCycles(wb, sheet, testCell); + CellValue cellValue = evaluateWithCycles(wb, testCell); assertTrue(cellValue.getCellType() == HSSFCell.CELL_TYPE_NUMERIC); assertEquals(2, cellValue.getNumberValue(), 0); @@ -93,7 +93,7 @@ public final class TestCircularReferences extends TestCase { HSSFCell testCell = row.createCell(0); testCell.setCellFormula("A1"); - CellValue cellValue = evaluateWithCycles(wb, sheet, testCell); + CellValue cellValue = evaluateWithCycles(wb, testCell); confirmCycleErrorCode(cellValue); } @@ -113,7 +113,7 @@ public final class TestCircularReferences extends TestCase { HSSFCell testCell = row.createCell(3); testCell.setCellFormula("A1"); - CellValue cellValue = evaluateWithCycles(wb, sheet, testCell); + CellValue cellValue = evaluateWithCycles(wb, testCell); confirmCycleErrorCode(cellValue); } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java index e1bed55c51..95408a4aaf 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestExternalFunction.java @@ -65,7 +65,7 @@ public final class TestExternalFunction extends TestCase { String actualFormula=cell.getCellFormula(); assertEquals("myFunc()", actualFormula); - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); CellValue evalResult = fe.evaluate(cell); // Check the return value from ExternalFunction.evaluate() diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java index da110a020c..257bb8f711 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java @@ -66,7 +66,7 @@ public final class TestFormulaBugs extends TestCase { .getCellFormula()); // We might as well evaluate the formula - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); CellValue cv = fe.evaluate(cell); assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType()); @@ -111,7 +111,7 @@ public final class TestFormulaBugs extends TestCase { } // use POI's evaluator as an extra sanity check - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); CellValue cv; cv = fe.evaluate(cell); assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType()); @@ -162,7 +162,7 @@ public final class TestFormulaBugs extends TestCase { double expectedResult = (4.0 * 8.0 + 5.0 * 9.0) / 10.0; - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet1, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); CellValue cv = fe.evaluate(cell); assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType()); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulasFromSpreadsheet.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulasFromSpreadsheet.java index ffe4bffc0a..74b436cdb7 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulasFromSpreadsheet.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulasFromSpreadsheet.java @@ -113,15 +113,6 @@ public final class TestFormulasFromSpreadsheet extends TestCase { throw new AssertionFailedError(msg + " - actual value was null"); } - if (expected.getCellType() == Cell.CELL_TYPE_STRING) { - String value = expected.getRichStringCellValue().getString(); - if (value.startsWith("#")) { - // TODO - this code never called - expected.setCellType(Cell.CELL_TYPE_ERROR); - // expected.setCellErrorValue(...?); - } - } - switch (expected.getCellType()) { case Cell.CELL_TYPE_BLANK: assertEquals(msg, Cell.CELL_TYPE_BLANK, actual.getCellType()); @@ -132,32 +123,27 @@ public final class TestFormulasFromSpreadsheet extends TestCase { break; case Cell.CELL_TYPE_ERROR: assertEquals(msg, Cell.CELL_TYPE_ERROR, actual.getCellType()); - if(false) { // TODO: fix ~45 functions which are currently returning incorrect error values - assertEquals(msg, expected.getErrorCellValue(), actual.getErrorValue()); - } + assertEquals(msg, ErrorEval.getText(expected.getErrorCellValue()), ErrorEval.getText(actual.getErrorValue())); break; case Cell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation throw new AssertionFailedError("Cannot expect formula as result of formula evaluation: " + msg); case Cell.CELL_TYPE_NUMERIC: assertEquals(msg, Cell.CELL_TYPE_NUMERIC, actual.getCellType()); TestMathX.assertEquals(msg, expected.getNumericCellValue(), actual.getNumberValue(), TestMathX.POS_ZERO, TestMathX.DIFF_TOLERANCE_FACTOR); -// double delta = Math.abs(expected.getNumericCellValue()-actual.getNumberValue()); -// double pctExpected = Math.abs(0.00001*expected.getNumericCellValue()); -// assertTrue(msg, delta <= pctExpected); break; case Cell.CELL_TYPE_STRING: assertEquals(msg, Cell.CELL_TYPE_STRING, actual.getCellType()); - assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getRichTextStringValue().getString()); + assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getStringValue()); break; } } - protected void setUp() throws Exception { + protected void setUp() { if (workbook == null) { workbook = HSSFTestDataSamples.openSampleWorkbook(SS.FILENAME); sheet = workbook.getSheetAt( 0 ); - } + } _functionFailureCount = 0; _functionSuccessCount = 0; _evaluationFailureCount = 0; @@ -192,8 +178,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase { * Typically pass <code>null</code> to test all functions */ private void processFunctionGroup(int startRowIndex, String testFocusFunctionName) { - - FormulaEvaluator evaluator = new FormulaEvaluator(sheet, workbook); + FormulaEvaluator evaluator = new FormulaEvaluator(workbook); int rowIndex = startRowIndex; while (true) { @@ -263,7 +248,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase { result = Result.SOME_EVALUATIONS_FAILED; } } - return result; + return result; } /** diff --git a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java index ef2340d4a4..bac2e68862 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java @@ -67,7 +67,7 @@ public final class TestPercentEval extends TestCase { cell.setCellFormula("B1%"); row.createCell(1).setCellValue(50.0); - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); CellValue cv; try { cv = fe.evaluate(cell); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java index 2ec7ad005a..4ae90d067b 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/AllIndividualFunctionEvaluationTests.java @@ -34,6 +34,7 @@ public final class AllIndividualFunctionEvaluationTests { result.addTestSuite(TestDate.class); result.addTestSuite(TestFinanceLib.class); result.addTestSuite(TestIndex.class); + result.addTestSuite(TestIndexFunctionFromSpreadsheet.class); result.addTestSuite(TestIsBlank.class); result.addTestSuite(TestLen.class); result.addTestSuite(TestLookupFunctionsFromSpreadsheet.class); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestAverage.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestAverage.java index 4f0e5fff2c..727f6b7645 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestAverage.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestAverage.java @@ -27,14 +27,13 @@ import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; /** * Tests for Excel function AVERAGE() - * + * * @author Josh Micich */ public final class TestAverage extends TestCase { - private static Eval invokeAverage(Eval[] args) { - return new Average().evaluate(args, -1, (short)-1); + return AggregateFunction.AVERAGE.evaluate(args, -1, (short)-1); } private void confirmAverage(Eval[] args, double expected) { @@ -48,56 +47,56 @@ public final class TestAverage extends TestCase { assertEquals(ErrorEval.class, result.getClass()); assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode()); } - + public void testBasic() { - + ValueEval[] values = { - new NumberEval(1), - new NumberEval(2), - new NumberEval(3), - new NumberEval(4), + new NumberEval(1), + new NumberEval(2), + new NumberEval(3), + new NumberEval(4), }; - + confirmAverage(values, 2.5); - + values = new ValueEval[] { - new NumberEval(1), + new NumberEval(1), new NumberEval(2), BlankEval.INSTANCE, - new NumberEval(3), + new NumberEval(3), BlankEval.INSTANCE, - new NumberEval(4), + new NumberEval(4), BlankEval.INSTANCE, }; - + confirmAverage(values, 2.5); } - + /** * Valid cases where values are not pure numbers */ public void testUnusualArgs() { ValueEval[] values = { - new NumberEval(1), - new NumberEval(2), - BoolEval.TRUE, - BoolEval.FALSE, + new NumberEval(1), + new NumberEval(2), + BoolEval.TRUE, + BoolEval.FALSE, }; - + confirmAverage(values, 1.0); - + } // currently disabled because MultiOperandNumericFunction.getNumberArray(Eval[], int, short) // does not handle error values properly yet public void XtestErrors() { ValueEval[] values = { - new NumberEval(1), - ErrorEval.NAME_INVALID, - new NumberEval(3), - ErrorEval.DIV_ZERO, + new NumberEval(1), + ErrorEval.NAME_INVALID, + new NumberEval(3), + ErrorEval.DIV_ZERO, }; confirmAverage(values, ErrorEval.NAME_INVALID); - + } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java index 41d703e2cf..8e5a8f32a4 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestCountFuncs.java @@ -21,13 +21,11 @@ import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; -import org.apache.poi.hssf.record.formula.RefPtg; 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.Eval; import org.apache.poi.hssf.record.formula.eval.NumberEval; -import org.apache.poi.hssf.record.formula.eval.Ref2DEval; import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.functions.CountUtils.I_MatchPredicate; @@ -270,7 +268,7 @@ public final class TestCountFuncs extends TestCase { int failureCount = 0; HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(FILE_NAME); HSSFSheet sheet = wb.getSheetAt(0); - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); int maxRow = sheet.getLastRowNum(); for (int rowIx=START_ROW_IX; rowIx<maxRow; rowIx++) { HSSFRow row = sheet.getRow(rowIx); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestDate.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestDate.java index 68bc43154a..0c08ef5a54 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestDate.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestDate.java @@ -37,7 +37,7 @@ public final class TestDate extends TestCase { HSSFSheet sheet = wb.createSheet("new sheet"); cell11 = sheet.createRow(0).createCell(0); cell11.setCellType(HSSFCell.CELL_TYPE_FORMULA); - evaluator = new HSSFFormulaEvaluator(sheet, wb); + evaluator = new HSSFFormulaEvaluator(wb); } /** diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndex.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndex.java index 80c154596e..95355733d2 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndex.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndex.java @@ -48,11 +48,11 @@ public final class TestIndex extends TestCase { double[] values = TEST_VALUES0; confirmAreaEval("C1:D6", values, 4, 1, 7); confirmAreaEval("C1:D6", values, 6, 2, 12); - confirmAreaEval("C1:D6", values, 3, -1, 5); + confirmAreaEval("C1:D6", values, 3, 1, 5); // now treat same data as 3 columns, 4 rows confirmAreaEval("C10:E13", values, 2, 2, 5); - confirmAreaEval("C10:E13", values, 4, -1, 10); + confirmAreaEval("C10:E13", values, 4, 1, 10); } /** diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndexFunctionFromSpreadsheet.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndexFunctionFromSpreadsheet.java new file mode 100644 index 0000000000..f28a6462f8 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIndexFunctionFromSpreadsheet.java @@ -0,0 +1,250 @@ +/* ==================================================================== + 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.io.PrintStream; + +import junit.framework.Assert; +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.record.formula.eval.ErrorEval; +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.hssf.util.CellReference; +import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue; + +/** + * Tests INDEX() as loaded from a test data spreadsheet.<p/> + * + * @author Josh Micich + */ +public final class TestIndexFunctionFromSpreadsheet extends TestCase { + + private static final class Result { + public static final int SOME_EVALUATIONS_FAILED = -1; + public static final int ALL_EVALUATIONS_SUCCEEDED = +1; + public static final int NO_EVALUATIONS_FOUND = 0; + } + + /** + * This class defines constants for navigating around the test data spreadsheet used for these tests. + */ + private static final class SS { + + /** Name of the test spreadsheet (found in the standard test data folder) */ + public final static String FILENAME = "IndexFunctionTestCaseData.xls"; + + public static final int COLUMN_INDEX_EVALUATION = 2; // Column 'C' + public static final int COLUMN_INDEX_EXPECTED_RESULT = 3; // Column 'D' + + } + + // Note - multiple failures are aggregated before ending. + // If one or more functions fail, a single AssertionFailedError is thrown at the end + private int _evaluationFailureCount; + private int _evaluationSuccessCount; + + + + private static void confirmExpectedResult(String msg, HSSFCell expected, HSSFFormulaEvaluator.CellValue actual) { + if (expected == null) { + throw new AssertionFailedError(msg + " - Bad setup data expected value is null"); + } + if(actual == null) { + throw new AssertionFailedError(msg + " - actual value was null"); + } + if(expected.getCellType() == HSSFCell.CELL_TYPE_ERROR) { + confirmErrorResult(msg, expected.getErrorCellValue(), actual); + return; + } + if(actual.getCellType() == HSSFCell.CELL_TYPE_ERROR) { + throw unexpectedError(msg, expected, actual.getErrorValue()); + } + if(actual.getCellType() != expected.getCellType()) { + throw wrongTypeError(msg, expected, actual); + } + + + switch (expected.getCellType()) { + case HSSFCell.CELL_TYPE_BOOLEAN: + assertEquals(msg, expected.getBooleanCellValue(), actual.getBooleanValue()); + break; + case HSSFCell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation + throw new AssertionFailedError("Cannot expect formula as result of formula evaluation: " + msg); + case HSSFCell.CELL_TYPE_NUMERIC: + assertEquals(expected.getNumericCellValue(), actual.getNumberValue(), 0.0); + break; + case HSSFCell.CELL_TYPE_STRING: + assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getStringValue()); + break; + } + } + + + private static AssertionFailedError wrongTypeError(String msgPrefix, HSSFCell expectedCell, CellValue actualValue) { + return new AssertionFailedError(msgPrefix + " Result type mismatch. Evaluated result was " + + actualValue.formatAsString() + + " but the expected result was " + + formatValue(expectedCell) + ); + } + private static AssertionFailedError unexpectedError(String msgPrefix, HSSFCell expected, int actualErrorCode) { + return new AssertionFailedError(msgPrefix + " Error code (" + + ErrorEval.getText(actualErrorCode) + + ") was evaluated, but the expected result was " + + formatValue(expected) + ); + } + + + private static void confirmErrorResult(String msgPrefix, int expectedErrorCode, CellValue actual) { + if(actual.getCellType() != HSSFCell.CELL_TYPE_ERROR) { + throw new AssertionFailedError(msgPrefix + " Expected cell error (" + + ErrorEval.getText(expectedErrorCode) + ") but actual value was " + + actual.formatAsString()); + } + if(expectedErrorCode != actual.getErrorValue()) { + throw new AssertionFailedError(msgPrefix + " Expected cell error code (" + + ErrorEval.getText(expectedErrorCode) + + ") but actual error code was (" + + ErrorEval.getText(actual.getErrorValue()) + + ")"); + } + } + + + private static String formatValue(HSSFCell expecedCell) { + switch (expecedCell.getCellType()) { + case HSSFCell.CELL_TYPE_BLANK: return "<blank>"; + case HSSFCell.CELL_TYPE_BOOLEAN: return String.valueOf(expecedCell.getBooleanCellValue()); + case HSSFCell.CELL_TYPE_NUMERIC: return String.valueOf(expecedCell.getNumericCellValue()); + case HSSFCell.CELL_TYPE_STRING: return expecedCell.getRichStringCellValue().getString(); + } + throw new RuntimeException("Unexpected cell type of expected value (" + expecedCell.getCellType() + ")"); + } + + + protected void setUp() { + _evaluationFailureCount = 0; + _evaluationSuccessCount = 0; + } + + public void testFunctionsFromTestSpreadsheet() { + HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook(SS.FILENAME); + + processTestSheet(workbook, workbook.getSheetName(0)); + + // confirm results + String successMsg = "There were " + + _evaluationSuccessCount + " function(s) without error"; + if(_evaluationFailureCount > 0) { + String msg = _evaluationFailureCount + " evaluation(s) failed. " + successMsg; + throw new AssertionFailedError(msg); + } + if(false) { // normally no output for successful tests + System.out.println(getClass().getName() + ": " + successMsg); + } + } + + private void processTestSheet(HSSFWorkbook workbook, String sheetName) { + HSSFSheet sheet = workbook.getSheetAt(0); + HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(workbook); + int maxRows = sheet.getLastRowNum()+1; + int result = Result.NO_EVALUATIONS_FOUND; // so far + + for(int rowIndex=0; rowIndex<maxRows; rowIndex++) { + HSSFRow r = sheet.getRow(rowIndex); + if(r == null) { + continue; + } + HSSFCell c = r.getCell(SS.COLUMN_INDEX_EVALUATION); + if (c == null || c.getCellType() != HSSFCell.CELL_TYPE_FORMULA) { + continue; + } + CellValue actualValue = evaluator.evaluate(c); + HSSFCell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT); + + String msgPrefix = formatTestCaseDetails(sheetName, r.getRowNum(), c); + try { + confirmExpectedResult(msgPrefix, expectedValueCell, actualValue); + _evaluationSuccessCount ++; + if(result != Result.SOME_EVALUATIONS_FAILED) { + result = Result.ALL_EVALUATIONS_SUCCEEDED; + } + } catch (RuntimeException e) { + _evaluationFailureCount ++; + printShortStackTrace(System.err, e); + result = Result.SOME_EVALUATIONS_FAILED; + } catch (AssertionFailedError e) { + _evaluationFailureCount ++; + printShortStackTrace(System.err, e); + result = Result.SOME_EVALUATIONS_FAILED; + } + + } + } + + + private static String formatTestCaseDetails(String sheetName, int rowNum, HSSFCell c) { + + StringBuffer sb = new StringBuffer(); + CellReference cr = new CellReference(sheetName, rowNum, c.getCellNum(), false, false); + sb.append(cr.formatAsString()); + sb.append(" {=").append(c.getCellFormula()).append("}"); + return sb.toString(); + } + + /** + * Useful to keep output concise when expecting many failures to be reported by this test case + */ + private static void printShortStackTrace(PrintStream ps, Throwable e) { + StackTraceElement[] stes = e.getStackTrace(); + + int startIx = 0; + // skip any top frames inside junit.framework.Assert + while(startIx<stes.length) { + if(!stes[startIx].getClassName().equals(Assert.class.getName())) { + break; + } + startIx++; + } + // skip bottom frames (part of junit framework) + int endIx = startIx+1; + while(endIx < stes.length) { + if(stes[endIx].getClassName().equals(TestCase.class.getName())) { + break; + } + endIx++; + } + if(startIx >= endIx) { + // something went wrong. just print the whole stack trace + e.printStackTrace(ps); + } + endIx -= 4; // skip 4 frames of reflection invocation + ps.println(e.toString()); + for(int i=startIx; i<endIx; i++) { + ps.println("\tat " + stes[i].toString()); + } + } +} + diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIsBlank.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIsBlank.java index 62cc32e957..50f92dfecf 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIsBlank.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestIsBlank.java @@ -44,7 +44,7 @@ public final class TestIsBlank extends TestCase { cell.setCellFormula("isblank(Sheet2!A1:A1)"); - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet1, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); CellValue result = fe.evaluate(cell); assertEquals(HSSFCell.CELL_TYPE_BOOLEAN, result.getCellType()); assertEquals(true, result.getBooleanValue()); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java index a96fb4e2b0..459d2fb555 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLen.java @@ -35,7 +35,7 @@ public final class TestLen extends TestCase { private static Eval invokeLen(Eval text) { Eval[] args = new Eval[] { text, }; - return new Len().evaluate(args, -1, (short)-1); + return TextFunction.LEN.evaluate(args, -1, (short)-1); } private void confirmLen(Eval text, int expected) { diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLookupFunctionsFromSpreadsheet.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLookupFunctionsFromSpreadsheet.java index fe0bab0a0d..49d67a8d4e 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLookupFunctionsFromSpreadsheet.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestLookupFunctionsFromSpreadsheet.java @@ -37,33 +37,33 @@ import org.apache.poi.hssf.util.CellReference; * Tests lookup functions (VLOOKUP, HLOOKUP, LOOKUP, MATCH) as loaded from a test data spreadsheet.<p/> * These tests have been separated from the common function and operator tests because the lookup * functions have more complex test cases and test data setup. - * + * * Tests for bug fixes and specific/tricky behaviour can be found in the corresponding test class * (<tt>TestXxxx</tt>) of the target (<tt>Xxxx</tt>) implementor, where execution can be observed * more easily. - * + * * @author Josh Micich */ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { - + private static final class Result { public static final int SOME_EVALUATIONS_FAILED = -1; public static final int ALL_EVALUATIONS_SUCCEEDED = +1; public static final int NO_EVALUATIONS_FOUND = 0; } - /** + /** * This class defines constants for navigating around the test data spreadsheet used for these tests. */ private static final class SS { - + /** Name of the test spreadsheet (found in the standard test data folder) */ public final static String FILENAME = "LookupFunctionsTestCaseData.xls"; - + /** Name of the first sheet in the spreadsheet (contains comments) */ public final static String README_SHEET_NAME = "Read Me"; - - + + /** Row (zero-based) in each sheet where the evaluation cases start. */ public static final int START_TEST_CASES_ROW_INDEX = 4; // Row '5' /** Index of the column that contains the function names */ @@ -71,15 +71,15 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { public static final int COLUMN_INDEX_EVALUATION = 1; // Column 'B' public static final int COLUMN_INDEX_EXPECTED_RESULT = 2; // Column 'C' public static final int COLUMN_ROW_COMMENT = 3; // Column 'D' - + /** Used to indicate when there are no more test cases on the current sheet */ public static final String TEST_CASES_END_MARKER = "<end>"; /** Used to indicate that the test on the current row should be ignored */ public static final String SKIP_CURRENT_TEST_CASE_MARKER = "<skip>"; - + } - // Note - multiple failures are aggregated before ending. + // Note - multiple failures are aggregated before ending. // If one or more functions fail, a single AssertionFailedError is thrown at the end private int _sheetFailureCount; private int _sheetSuccessCount; @@ -105,19 +105,19 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { if(actual.getCellType() != expected.getCellType()) { throw wrongTypeError(msg, expected, actual); } - - + + switch (expected.getCellType()) { case HSSFCell.CELL_TYPE_BOOLEAN: assertEquals(msg, expected.getBooleanCellValue(), actual.getBooleanValue()); break; case HSSFCell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation - throw new AssertionFailedError("Cannot expect formula as result of formula evaluation: " + msg); + throw new IllegalStateException("Cannot expect formula as result of formula evaluation: " + msg); case HSSFCell.CELL_TYPE_NUMERIC: assertEquals(expected.getNumericCellValue(), actual.getNumberValue(), 0.0); break; case HSSFCell.CELL_TYPE_STRING: - assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getRichTextStringValue().getString()); + assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getStringValue()); break; } } @@ -125,14 +125,14 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { private static AssertionFailedError wrongTypeError(String msgPrefix, HSSFCell expectedCell, CellValue actualValue) { return new AssertionFailedError(msgPrefix + " Result type mismatch. Evaluated result was " - + formatValue(actualValue) + + actualValue.formatAsString() + " but the expected result was " + formatValue(expectedCell) ); } private static AssertionFailedError unexpectedError(String msgPrefix, HSSFCell expected, int actualErrorCode) { return new AssertionFailedError(msgPrefix + " Error code (" - + ErrorEval.getText(actualErrorCode) + + ErrorEval.getText(actualErrorCode) + ") was evaluated, but the expected result was " + formatValue(expected) ); @@ -141,15 +141,15 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { private static void confirmErrorResult(String msgPrefix, int expectedErrorCode, CellValue actual) { if(actual.getCellType() != HSSFCell.CELL_TYPE_ERROR) { - throw new AssertionFailedError(msgPrefix + " Expected cell error (" + throw new AssertionFailedError(msgPrefix + " Expected cell error (" + ErrorEval.getText(expectedErrorCode) + ") but actual value was " - + formatValue(actual)); + + actual.formatAsString()); } if(expectedErrorCode != actual.getErrorValue()) { - throw new AssertionFailedError(msgPrefix + " Expected cell error code (" - + ErrorEval.getText(expectedErrorCode) + throw new AssertionFailedError(msgPrefix + " Expected cell error code (" + + ErrorEval.getText(expectedErrorCode) + ") but actual error code was (" - + ErrorEval.getText(actual.getErrorValue()) + + ErrorEval.getText(actual.getErrorValue()) + ")"); } } @@ -164,57 +164,48 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { } throw new RuntimeException("Unexpected cell type of expected value (" + expecedCell.getCellType() + ")"); } - private static String formatValue(CellValue actual) { - switch (actual.getCellType()) { - case HSSFCell.CELL_TYPE_BLANK: return "<blank>"; - case HSSFCell.CELL_TYPE_BOOLEAN: return String.valueOf(actual.getBooleanValue()); - case HSSFCell.CELL_TYPE_NUMERIC: return String.valueOf(actual.getNumberValue()); - case HSSFCell.CELL_TYPE_STRING: return actual.getRichTextStringValue().getString(); - } - throw new RuntimeException("Unexpected cell type of evaluated value (" + actual.getCellType() + ")"); - } - protected void setUp() throws Exception { + protected void setUp() { _sheetFailureCount = 0; _sheetSuccessCount = 0; _evaluationFailureCount = 0; _evaluationSuccessCount = 0; } - + public void testFunctionsFromTestSpreadsheet() { HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook(SS.FILENAME); - + confirmReadMeSheet(workbook); int nSheets = workbook.getNumberOfSheets(); for(int i=1; i< nSheets; i++) { int sheetResult = processTestSheet(workbook, i, workbook.getSheetName(i)); switch(sheetResult) { - case Result.ALL_EVALUATIONS_SUCCEEDED: _sheetSuccessCount ++; break; - case Result.SOME_EVALUATIONS_FAILED: _sheetFailureCount ++; break; + case Result.ALL_EVALUATIONS_SUCCEEDED: _sheetSuccessCount ++; break; + case Result.SOME_EVALUATIONS_FAILED: _sheetFailureCount ++; break; } } - + // confirm results - String successMsg = "There were " + String successMsg = "There were " + _sheetSuccessCount + " successful sheets(s) and " + _evaluationSuccessCount + " function(s) without error"; - if(_sheetFailureCount > 0) { + if(_sheetFailureCount > 0) { String msg = _sheetFailureCount + " sheets(s) failed with " + _evaluationFailureCount + " evaluation(s). " + successMsg; throw new AssertionFailedError(msg); } - if(false) { // normally no output for successful tests - System.out.println(getClass().getName() + ": " + successMsg); - } + if(false) { // normally no output for successful tests + System.out.println(getClass().getName() + ": " + successMsg); + } } private int processTestSheet(HSSFWorkbook workbook, int sheetIndex, String sheetName) { HSSFSheet sheet = workbook.getSheetAt(sheetIndex); - HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, workbook); + HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(workbook); int maxRows = sheet.getLastRowNum()+1; int result = Result.NO_EVALUATIONS_FOUND; // so far - + String currentGroupComment = null; for(int rowIndex=SS.START_TEST_CASES_ROW_INDEX; rowIndex<maxRows; rowIndex++) { HSSFRow r = sheet.getRow(rowIndex); @@ -240,7 +231,7 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { CellValue actualValue = evaluator.evaluate(c); HSSFCell expectedValueCell = r.getCell(SS.COLUMN_INDEX_EXPECTED_RESULT); String rowComment = getRowCommentColumnValue(r); - + String msgPrefix = formatTestCaseDetails(sheetName, r.getRowNum(), c, currentGroupComment, rowComment); try { confirmExpectedResult(msgPrefix, expectedValueCell, actualValue); @@ -257,22 +248,22 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { printShortStackTrace(System.err, e); result = Result.SOME_EVALUATIONS_FAILED; } - + } - throw new RuntimeException("Missing end marker '" + SS.TEST_CASES_END_MARKER + throw new RuntimeException("Missing end marker '" + SS.TEST_CASES_END_MARKER + "' on sheet '" + sheetName + "'"); - + } private static String formatTestCaseDetails(String sheetName, int rowNum, HSSFCell c, String currentGroupComment, String rowComment) { - + StringBuffer sb = new StringBuffer(); CellReference cr = new CellReference(sheetName, rowNum, c.getCellNum(), false, false); sb.append(cr.formatAsString()); sb.append(" {=").append(c.getCellFormula()).append("}"); - + if(currentGroupComment != null) { sb.append(" '"); sb.append(currentGroupComment); @@ -288,13 +279,13 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { sb.append("' "); } } - + return sb.toString(); } /** - * Asserts that the 'read me' comment page exists, and has this class' name in one of the - * cells. This back-link is to make it easy to find this class if a reader encounters the + * Asserts that the 'read me' comment page exists, and has this class' name in one of the + * cells. This back-link is to make it easy to find this class if a reader encounters the * spreadsheet first. */ private void confirmReadMeSheet(HSSFWorkbook workbook) { @@ -313,7 +304,7 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { */ private static void printShortStackTrace(PrintStream ps, Throwable e) { StackTraceElement[] stes = e.getStackTrace(); - + int startIx = 0; // skip any top frames inside junit.framework.Assert while(startIx<stes.length) { @@ -339,17 +330,17 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { for(int i=startIx; i<endIx; i++) { ps.println("\tat " + stes[i].toString()); } - + } private static String getRowCommentColumnValue(HSSFRow r) { return getCellTextValue(r, SS.COLUMN_ROW_COMMENT, "row comment"); } - + private static String getMarkerColumnValue(HSSFRow r) { return getCellTextValue(r, SS.COLUMN_INDEX_MARKER, "marker"); } - + /** * @return <code>null</code> if cell is missing, empty or blank */ @@ -367,7 +358,7 @@ public final class TestLookupFunctionsFromSpreadsheet extends TestCase { if(cell.getCellType() == HSSFCell.CELL_TYPE_STRING) { return cell.getRichStringCellValue().getString(); } - + throw new RuntimeException("Bad cell type for '" + columnName + "' column: (" + cell.getCellType() + ") row (" + (r.getRowNum() +1) + ")"); } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java index 76cd1056ed..81e78e737c 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestMid.java @@ -38,7 +38,7 @@ public final class TestMid extends TestCase { private static Eval invokeMid(Eval text, Eval startPos, Eval numChars) { Eval[] args = new Eval[] { text, startPos, numChars, }; - return new Mid().evaluate(args, -1, (short)-1); + return TextFunction.MID.evaluate(args, -1, (short)-1); } private void confirmMid(Eval text, Eval startPos, Eval numChars, String expected) { diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java index 2fecef7046..9a4cded9af 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestPmt.java @@ -36,7 +36,7 @@ public final class TestPmt extends TestCase { assertEquals(expected, ne.getNumberValue(), 0.00005); } private static Eval invoke(Eval[] args) { - return new Pmt().evaluate(args, -1, (short)-1); + return FinanceFunction.PMT.evaluate(args, -1, (short)-1); } /** * Invocation when not expecting an error result diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestRoundFuncs.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestRoundFuncs.java index a6ce345aeb..9dbbb438e0 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestRoundFuncs.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestRoundFuncs.java @@ -17,24 +17,25 @@ package org.apache.poi.hssf.record.formula.functions; +import junit.framework.TestCase; + import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.StringEval; -import junit.framework.TestCase; - /** * Test cases for ROUND(), ROUNDUP(), ROUNDDOWN() * * @author Josh Micich */ public final class TestRoundFuncs extends TestCase { + private static final NumericFunction F = null; public void testRounddownWithStringArg() { Eval strArg = new StringEval("abc"); Eval[] args = { strArg, new NumberEval(2), }; - Eval result = new Rounddown().evaluate(args, -1, (short)-1); + Eval result = F.ROUNDDOWN.evaluate(args, -1, (short)-1); assertEquals(ErrorEval.VALUE_INVALID, result); } @@ -42,7 +43,7 @@ public final class TestRoundFuncs extends TestCase { Eval strArg = new StringEval("abc"); Eval[] args = { strArg, new NumberEval(2), }; - Eval result = new Roundup().evaluate(args, -1, (short)-1); + Eval result = F.ROUNDUP.evaluate(args, -1, (short)-1); assertEquals(ErrorEval.VALUE_INVALID, result); } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestStatsLib.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestStatsLib.java index 237366baf0..3562a67789 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestStatsLib.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestStatsLib.java @@ -20,6 +20,11 @@ */ package org.apache.poi.hssf.record.formula.functions; +import junit.framework.AssertionFailedError; + +import org.apache.poi.hssf.record.formula.eval.ErrorEval; +import org.apache.poi.hssf.record.formula.eval.EvaluationException; + /** * @author Amol S. Deshmukh < amolweb at ya hoo dot com > @@ -181,49 +186,53 @@ public class TestStatsLib extends AbstractNumericTestCase { } public void testMode() { - double[] v = null; + double[] v; double d, x = 0; v = new double[] {1,2,3,4,5,6,7,8,9,10}; - d = StatsLib.mode(v); - x = Double.NaN; - assertEquals("mode ", x, d); + confirmMode(v, null); v = new double[] {1,1,1,1,1,1,1,1,1,1}; - d = StatsLib.mode(v); - x = 1; - assertEquals("mode ", x, d); + confirmMode(v, 1.0); v = new double[] {0,0,0,0,0,0,0,0,0,0}; - d = StatsLib.mode(v); - x = 0; - assertEquals("mode ", x, d); + confirmMode(v, 0.0); v = new double[] {1,2,1,2,1,2,1,2,1,2}; - d = StatsLib.mode(v); - x = 1; - assertEquals("mode ", x, d); + confirmMode(v, 1.0); v = new double[] {123.12,33.3333,2d/3d,5.37828,0.999}; - d = StatsLib.mode(v); - x = Double.NaN; - assertEquals("mode ", x, d); + confirmMode(v, null); v = new double[] {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10}; - d = StatsLib.mode(v); - x = Double.NaN; - assertEquals("mode ", x, d); + confirmMode(v, null); v = new double[] {1,2,3,4,1,1,1,1,0,0,0,0,0}; - d = StatsLib.mode(v); - x = 1; - assertEquals("mode ", x, d); + confirmMode(v, 1.0); v = new double[] {0,1,2,3,4,1,1,1,0,0,0,0,1}; - d = StatsLib.mode(v); - x = 0; - assertEquals("mode ", x, d); + confirmMode(v, 0.0); + } + private static void confirmMode(double[] v, double expectedResult) { + confirmMode(v, new Double(expectedResult)); + } + private static void confirmMode(double[] v, Double expectedResult) { + double actual; + try { + actual = Mode.evaluate(v); + if (expectedResult == null) { + throw new AssertionFailedError("Expected N/A exception was not thrown"); + } + } catch (EvaluationException e) { + if (expectedResult == null) { + assertEquals(ErrorEval.NA, e.getErrorEval()); + return; + } + throw new RuntimeException(e); + } + assertEquals("mode", expectedResult.doubleValue(), actual); } + public void testStddev() { double[] v = null; diff --git a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java index 076ac1fc7e..1c65c9ca6d 100755 --- a/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/functions/TestTrim.java @@ -35,7 +35,7 @@ public final class TestTrim extends TestCase { private static Eval invokeTrim(Eval text) { Eval[] args = new Eval[] { text, }; - return new Trim().evaluate(args, -1, (short)-1); + return TextFunction.TRIM.evaluate(args, -1, (short)-1); } private void confirmTrim(Eval text, String expected) { diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBug42464.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBug42464.java index c128c773c5..a43764e852 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBug42464.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBug42464.java @@ -43,10 +43,9 @@ public final class TestBug42464 extends TestCase { } private static void process(HSSFWorkbook wb) { + HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(wb); for(int i=0; i<wb.getNumberOfSheets(); i++) { HSSFSheet s = wb.getSheetAt(i); - HSSFFormulaEvaluator eval = - new HSSFFormulaEvaluator(s, wb); Iterator it = s.rowIterator(); while(it.hasNext()) { diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBug43093.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBug43093.java index 1989d7d4e7..3dbf5907ff 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBug43093.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBug43093.java @@ -39,7 +39,7 @@ public final class TestBug43093 extends TestCase { } public void testBug43093() { - HSSFWorkbook xlw = new HSSFWorkbook(); + HSSFWorkbook xlw = new HSSFWorkbook(); addNewSheetWithCellsA1toD4(xlw, 1); addNewSheetWithCellsA1toD4(xlw, 2); @@ -51,7 +51,7 @@ public final class TestBug43093 extends TestCase { HSSFCell s2E4 = s2r3.createCell(4); s2E4.setCellFormula("SUM(s3!B2:C3)"); - HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(s2, xlw); + HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(xlw); double d = eva.evaluate(s2E4).getNumberValue(); // internalEvaluate(...) Area3DEval.: 311+312+321+322 expected diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index 200e62884a..8954656c6e 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -963,7 +963,7 @@ public final class TestBugs extends TestCase { writeOutAndReadBack(wb); assertTrue("no errors writing sample xls", true); } - + /** * Problems with extracting check boxes from * HSSFObjectData @@ -975,35 +975,35 @@ public final class TestBugs extends TestCase { // Take a look at the embeded objects List objects = wb.getAllEmbeddedObjects(); assertEquals(1, objects.size()); - + HSSFObjectData obj = (HSSFObjectData)objects.get(0); assertNotNull(obj); - + // Peek inside the underlying record EmbeddedObjectRefSubRecord rec = obj.findObjectRecord(); assertNotNull(rec); - + assertEquals(32, rec.field_1_stream_id_offset); assertEquals(0, rec.field_6_stream_id); // WRONG! assertEquals("Forms.CheckBox.1", rec.field_5_ole_classname); assertEquals(12, rec.remainingBytes.length); - + // Doesn't have a directory assertFalse(obj.hasDirectoryEntry()); assertNotNull(obj.getObjectData()); assertEquals(12, obj.getObjectData().length); assertEquals("Forms.CheckBox.1", obj.getOLE2ClassName()); - + try { obj.getDirectory(); fail(); } catch(FileNotFoundException e) { - // expectd during successful test + // expectd during successful test } catch (IOException e) { - throw new RuntimeException(e); - } + throw new RuntimeException(e); + } } - + /** * Test that we can delete sheets without * breaking the build in named ranges @@ -1013,73 +1013,73 @@ public final class TestBugs extends TestCase { HSSFWorkbook wb = openSample("30978-alt.xls"); assertEquals(1, wb.getNumberOfNames()); assertEquals(3, wb.getNumberOfSheets()); - + // Check all names fit within range, and use // DeletedArea3DPtg Workbook w = wb.getWorkbook(); for(int i=0; i<w.getNumNames(); i++) { NameRecord r = w.getNameRecord(i); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); - + Ptg[] nd = r.getNameDefinition(); assertEquals(1, nd.length); assertTrue(nd[0] instanceof DeletedArea3DPtg); } - - + + // Delete the 2nd sheet wb.removeSheetAt(1); - - + + // Re-check assertEquals(1, wb.getNumberOfNames()); assertEquals(2, wb.getNumberOfSheets()); - + for(int i=0; i<w.getNumNames(); i++) { NameRecord r = w.getNameRecord(i); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); - + Ptg[] nd = r.getNameDefinition(); assertEquals(1, nd.length); assertTrue(nd[0] instanceof DeletedArea3DPtg); } - - + + // Save and re-load wb = writeOutAndReadBack(wb); w = wb.getWorkbook(); - + assertEquals(1, wb.getNumberOfNames()); assertEquals(2, wb.getNumberOfSheets()); - + for(int i=0; i<w.getNumNames(); i++) { NameRecord r = w.getNameRecord(i); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); - + Ptg[] nd = r.getNameDefinition(); assertEquals(1, nd.length); assertTrue(nd[0] instanceof DeletedArea3DPtg); } } - + /** * Test that fonts get added properly */ public void test45338() { HSSFWorkbook wb = new HSSFWorkbook(); assertEquals(4, wb.getNumberOfFonts()); - + HSSFSheet s = wb.createSheet(); s.createRow(0); s.createRow(1); HSSFCell c1 = s.getRow(0).createCell(0); HSSFCell c2 = s.getRow(1).createCell(0); - + assertEquals(4, wb.getNumberOfFonts()); - + HSSFFont f1 = wb.getFontAt((short)0); assertEquals(400, f1.getBoldweight()); - + // Check that asking for the same font // multiple times gives you the same thing. // Otherwise, our tests wouldn't work! @@ -1096,22 +1096,22 @@ public final class TestBugs extends TestCase { != wb.getFontAt((short)2) ); - + // Look for a new font we have // yet to add assertNull( wb.findFont( - (short)11, (short)123, (short)22, + (short)11, (short)123, (short)22, "Thingy", false, true, (short)2, (byte)2 ) ); - + HSSFFont nf = wb.createFont(); assertEquals(5, wb.getNumberOfFonts()); - + assertEquals(5, nf.getIndex()); assertEquals(nf, wb.getFontAt((short)5)); - + nf.setBoldweight((short)11); nf.setColor((short)123); nf.setFontHeight((short)22); @@ -1120,32 +1120,32 @@ public final class TestBugs extends TestCase { nf.setStrikeout(true); nf.setTypeOffset((short)2); nf.setUnderline((byte)2); - + assertEquals(5, wb.getNumberOfFonts()); assertEquals(nf, wb.getFontAt((short)5)); - + // Find it now assertNotNull( wb.findFont( - (short)11, (short)123, (short)22, + (short)11, (short)123, (short)22, "Thingy", false, true, (short)2, (byte)2 ) ); assertEquals( 5, wb.findFont( - (short)11, (short)123, (short)22, + (short)11, (short)123, (short)22, "Thingy", false, true, (short)2, (byte)2 ).getIndex() ); assertEquals(nf, wb.findFont( - (short)11, (short)123, (short)22, + (short)11, (short)123, (short)22, "Thingy", false, true, (short)2, (byte)2 ) ); } - + /** * From the mailing list - ensure we can handle a formula * containing a zip code, eg ="70164" @@ -1162,63 +1162,59 @@ public final class TestBugs extends TestCase { c1.setCellFormula("70164"); c2.setCellFormula("\"70164\""); c3.setCellFormula("\"90210\""); - + // Check the formulas assertEquals("70164.0", c1.getCellFormula()); assertEquals("\"70164\"", c2.getCellFormula()); - + // And check the values - blank - assertEquals(0.0, c1.getNumericCellValue(), 0.00001); - assertEquals("", c1.getRichStringCellValue().getString()); - assertEquals(0.0, c2.getNumericCellValue(), 0.00001); - assertEquals("", c2.getRichStringCellValue().getString()); - assertEquals(0.0, c3.getNumericCellValue(), 0.00001); - assertEquals("", c3.getRichStringCellValue().getString()); - + confirmCachedValue(0.0, c1); + confirmCachedValue(0.0, c2); + confirmCachedValue(0.0, c3); + // Try changing the cached value on one of the string // formula cells, so we can see it updates properly c3.setCellValue(new HSSFRichTextString("test")); - assertEquals(0.0, c3.getNumericCellValue(), 0.00001); - assertEquals("test", c3.getRichStringCellValue().getString()); - - + confirmCachedValue("test", c3); + try { + c3.getNumericCellValue(); + throw new AssertionFailedError("exception should have been thrown"); + } catch (IllegalStateException e) { + assertEquals("Cannot get a numeric value from a text formula cell", e.getMessage()); + } + + // Now evaluate, they should all be changed - HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(s, wb); + HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(wb); eval.evaluateFormulaCell(c1); eval.evaluateFormulaCell(c2); eval.evaluateFormulaCell(c3); - + // Check that the cells now contain // the correct values - assertEquals(70164.0, c1.getNumericCellValue(), 0.00001); - assertEquals("", c1.getRichStringCellValue().getString()); - assertEquals(0.0, c2.getNumericCellValue(), 0.00001); - assertEquals("70164", c2.getRichStringCellValue().getString()); - assertEquals(0.0, c3.getNumericCellValue(), 0.00001); - assertEquals("90210", c3.getRichStringCellValue().getString()); - - + confirmCachedValue(70164.0, c1); + confirmCachedValue("70164", c2); + confirmCachedValue("90210", c3); + + // Write and read HSSFWorkbook nwb = writeOutAndReadBack(wb); HSSFSheet ns = nwb.getSheetAt(0); HSSFCell nc1 = ns.getRow(0).getCell(0); HSSFCell nc2 = ns.getRow(0).getCell(1); HSSFCell nc3 = ns.getRow(0).getCell(2); - + // Re-check - assertEquals(70164.0, nc1.getNumericCellValue(), 0.00001); - assertEquals("", nc1.getRichStringCellValue().getString()); - assertEquals(0.0, nc2.getNumericCellValue(), 0.00001); - assertEquals("70164", nc2.getRichStringCellValue().getString()); - assertEquals(0.0, nc3.getNumericCellValue(), 0.00001); - assertEquals("90210", nc3.getRichStringCellValue().getString()); - + confirmCachedValue(70164.0, nc1); + confirmCachedValue("70164", nc2); + confirmCachedValue("90210", nc3); + CellValueRecordInterface[] cvrs = ns.getSheet().getValueRecords(); for (int i = 0; i < cvrs.length; i++) { CellValueRecordInterface cvr = cvrs[i]; if(cvr instanceof FormulaRecordAggregate) { FormulaRecordAggregate fr = (FormulaRecordAggregate)cvr; - + if(i == 0) { assertEquals(70164.0, fr.getFormulaRecord().getValue(), 0.0001); assertNull(fr.getStringRecord()); @@ -1235,7 +1231,18 @@ public final class TestBugs extends TestCase { } assertEquals(3, cvrs.length); } - + + private static void confirmCachedValue(double expectedValue, HSSFCell cell) { + assertEquals(HSSFCell.CELL_TYPE_FORMULA, cell.getCellType()); + assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cell.getCachedFormulaResultType()); + assertEquals(expectedValue, cell.getNumericCellValue(), 0.0); + } + private static void confirmCachedValue(String expectedValue, HSSFCell cell) { + assertEquals(HSSFCell.CELL_TYPE_FORMULA, cell.getCellType()); + assertEquals(HSSFCell.CELL_TYPE_STRING, cell.getCachedFormulaResultType()); + assertEquals(expectedValue, cell.getRichStringCellValue().getString()); + } + /** * Problem with "Vector Rows", eg a whole * column which is set to the result of @@ -1244,37 +1251,37 @@ public final class TestBugs extends TestCase { * {=sin(B1:B9){9,1)[rownum][0] * In this sample file, the vector column * is C, and the data column is B. - * + * * For now, blows up with an exception from ExtPtg * Expected ExpPtg to be converted from Shared to Non-Shared... */ public void DISABLEDtest43623() { HSSFWorkbook wb = openSample("43623.xls"); assertEquals(1, wb.getNumberOfSheets()); - + HSSFSheet s1 = wb.getSheetAt(0); - + HSSFCell c1 = s1.getRow(0).getCell(2); HSSFCell c2 = s1.getRow(1).getCell(2); HSSFCell c3 = s1.getRow(2).getCell(2); - + // These formula contents are a guess... assertEquals("{=sin(B1:B9){9,1)[0][0]", c1.getCellFormula()); assertEquals("{=sin(B1:B9){9,1)[1][0]", c2.getCellFormula()); assertEquals("{=sin(B1:B9){9,1)[2][0]", c3.getCellFormula()); - + // Save and re-open, ensure it still works HSSFWorkbook nwb = writeOutAndReadBack(wb); HSSFSheet ns1 = nwb.getSheetAt(0); HSSFCell nc1 = ns1.getRow(0).getCell(2); HSSFCell nc2 = ns1.getRow(1).getCell(2); HSSFCell nc3 = ns1.getRow(2).getCell(2); - + assertEquals("{=sin(B1:B9){9,1)[0][0]", nc1.getCellFormula()); assertEquals("{=sin(B1:B9){9,1)[1][0]", nc2.getCellFormula()); assertEquals("{=sin(B1:B9){9,1)[2][0]", nc3.getCellFormula()); } - + /** * People are all getting confused about the last * row and cell number @@ -1282,48 +1289,48 @@ public final class TestBugs extends TestCase { public void test30635() { HSSFWorkbook wb = new HSSFWorkbook(); HSSFSheet s = wb.createSheet(); - + // No rows, everything is 0 assertEquals(0, s.getFirstRowNum()); assertEquals(0, s.getLastRowNum()); assertEquals(0, s.getPhysicalNumberOfRows()); - + // One row, most things are 0, physical is 1 s.createRow(0); assertEquals(0, s.getFirstRowNum()); assertEquals(0, s.getLastRowNum()); assertEquals(1, s.getPhysicalNumberOfRows()); - + // And another, things change s.createRow(4); assertEquals(0, s.getFirstRowNum()); assertEquals(4, s.getLastRowNum()); assertEquals(2, s.getPhysicalNumberOfRows()); - - + + // Now start on cells HSSFRow r = s.getRow(0); assertEquals(-1, r.getFirstCellNum()); assertEquals(-1, r.getLastCellNum()); assertEquals(0, r.getPhysicalNumberOfCells()); - + // Add a cell, things move off -1 r.createCell(0); assertEquals(0, r.getFirstCellNum()); assertEquals(1, r.getLastCellNum()); // last cell # + 1 assertEquals(1, r.getPhysicalNumberOfCells()); - + r.createCell(1); assertEquals(0, r.getFirstCellNum()); assertEquals(2, r.getLastCellNum()); // last cell # + 1 assertEquals(2, r.getPhysicalNumberOfCells()); - + r.createCell(4); assertEquals(0, r.getFirstCellNum()); assertEquals(5, r.getLastCellNum()); // last cell # + 1 assertEquals(3, r.getPhysicalNumberOfCells()); } - + /** * Data Tables - ptg 0x2 */ @@ -1332,25 +1339,25 @@ public final class TestBugs extends TestCase { HSSFSheet s; HSSFRow r; HSSFCell c; - + // Check the contents of the formulas - + // E4 to G9 of sheet 4 make up the table s = wb.getSheet("OneVariable Table Completed"); r = s.getRow(3); c = r.getCell(4); assertEquals(HSSFCell.CELL_TYPE_FORMULA, c.getCellType()); - + // TODO - check the formula once tables and // arrays are properly supported - + // E4 to H9 of sheet 5 make up the table s = wb.getSheet("TwoVariable Table Example"); r = s.getRow(3); c = r.getCell(4); assertEquals(HSSFCell.CELL_TYPE_FORMULA, c.getCellType()); - + // TODO - check the formula once tables and // arrays are properly supported } @@ -1363,7 +1370,7 @@ public final class TestBugs extends TestCase { HSSFSheet sh = wb.getSheetAt(0); for(short i=0; i < 30; i++) sh.autoSizeColumn(i); } - + /** * We used to add too many UncalcRecords to sheets * with diagrams on. Don't any more @@ -1373,41 +1380,41 @@ public final class TestBugs extends TestCase { wb.getSheetAt(0).setForceFormulaRecalculation(true); wb.getSheetAt(1).setForceFormulaRecalculation(false); wb.getSheetAt(2).setForceFormulaRecalculation(true); - + // Write out and back in again // This used to break HSSFWorkbook nwb = writeOutAndReadBack(wb); - + // Check now set as it should be assertTrue(nwb.getSheetAt(0).getForceFormulaRecalculation()); assertFalse(nwb.getSheetAt(1).getForceFormulaRecalculation()); assertTrue(nwb.getSheetAt(2).getForceFormulaRecalculation()); } - + /** * Very hidden sheets not displaying as such */ public void test45761() { - HSSFWorkbook wb = openSample("45761.xls"); - assertEquals(3, wb.getNumberOfSheets()); - - assertFalse(wb.isSheetHidden(0)); - assertFalse(wb.isSheetVeryHidden(0)); - assertTrue(wb.isSheetHidden(1)); - assertFalse(wb.isSheetVeryHidden(1)); - assertFalse(wb.isSheetHidden(2)); - assertTrue(wb.isSheetVeryHidden(2)); - - // Change 0 to be very hidden, and re-load - wb.setSheetHidden(0, 2); - + HSSFWorkbook wb = openSample("45761.xls"); + assertEquals(3, wb.getNumberOfSheets()); + + assertFalse(wb.isSheetHidden(0)); + assertFalse(wb.isSheetVeryHidden(0)); + assertTrue(wb.isSheetHidden(1)); + assertFalse(wb.isSheetVeryHidden(1)); + assertFalse(wb.isSheetHidden(2)); + assertTrue(wb.isSheetVeryHidden(2)); + + // Change 0 to be very hidden, and re-load + wb.setSheetHidden(0, 2); + HSSFWorkbook nwb = writeOutAndReadBack(wb); - assertFalse(nwb.isSheetHidden(0)); - assertTrue(nwb.isSheetVeryHidden(0)); - assertTrue(nwb.isSheetHidden(1)); - assertFalse(nwb.isSheetVeryHidden(1)); - assertFalse(nwb.isSheetHidden(2)); - assertTrue(nwb.isSheetVeryHidden(2)); + assertFalse(nwb.isSheetHidden(0)); + assertTrue(nwb.isSheetVeryHidden(0)); + assertTrue(nwb.isSheetHidden(1)); + assertFalse(nwb.isSheetVeryHidden(1)); + assertFalse(nwb.isSheetHidden(2)); + assertTrue(nwb.isSheetVeryHidden(2)); } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java index f1d838efa3..dc4454d515 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java @@ -117,7 +117,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase { HSSFSheet sheet = wb.getSheetAt(0); - HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(wb); row = sheet.getRow(0); cell = row.getCell(0); @@ -177,7 +177,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase { HSSFSheet sheet = wb.getSheetAt(0); - HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator eva = new HSSFFormulaEvaluator(wb); // =index(C:C,2,1) -> 2 HSSFRow rowIDX = sheet.getRow(3); @@ -238,7 +238,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase { cell.setCellFormula("1=1"); - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); try { fe.evaluateInCell(cell); } catch (NumberFormatException e) { @@ -257,7 +257,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase { int numSheets = wb.getNumberOfSheets(); for (int i = 0; i < numSheets; i++) { HSSFSheet s = wb.getSheetAt(i); - HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(s, wb); + HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(wb); for (Iterator rows = s.rowIterator(); rows.hasNext();) { HSSFRow r = (HSSFRow) rows.next(); @@ -276,7 +276,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase { HSSFRow row = sheet.createRow(1); HSSFCell cell = row.createCell(0); cell.setCellFormula("na()"); // this formula evaluates to an Excel error code '#N/A' - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); try { fe.evaluateInCell(cell); } catch (NumberFormatException e) { @@ -312,7 +312,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase { // Choose cell A9, so that the failing test case doesn't take too long to execute. HSSFCell cell = row.getCell(8); - HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb); evaluator.evaluate(cell); int evalCount = evaluator.getEvaluationCount(); // With caching, the evaluationCount is 8 which is a big improvement diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorDocs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorDocs.java index bed8869d54..d2c9d0753c 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorDocs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorDocs.java @@ -68,7 +68,7 @@ public final class TestFormulaEvaluatorDocs extends TestCase { // uses evaluateFormulaCell() for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) { HSSFSheet sheet = wb.getSheetAt(sheetNum); - HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb); for(Iterator rit = sheet.rowIterator(); rit.hasNext();) { HSSFRow r = (HSSFRow)rit.next(); @@ -103,7 +103,7 @@ public final class TestFormulaEvaluatorDocs extends TestCase { // uses evaluateInCell() for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) { HSSFSheet sheet = wb.getSheetAt(sheetNum); - HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb); for(Iterator rit = sheet.rowIterator(); rit.hasNext();) { HSSFRow r = (HSSFRow)rit.next(); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java index f1be47fd1b..e1a6e1b3ca 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDataFormatter.java @@ -243,7 +243,7 @@ public final class TestHSSFDataFormatter extends TestCase { assertEquals("SUM(12.25,12.25)/100", formatter.formatCellValue(cell)); // now with a formula evaluator - HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb.getSheetAt(0), wb); + HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb); log(formatter.formatCellValue(cell, evaluator) + "\t\t\t (with evaluator)"); assertEquals("24.50%", formatter.formatCellValue(cell,evaluator)); } @@ -257,7 +257,7 @@ public final class TestHSSFDataFormatter extends TestCase { Iterator it = row.cellIterator(); Format defaultFormat = new DecimalFormat("Balance $#,#00.00 USD;Balance -$#,#00.00 USD"); formatter.setDefaultNumberFormat(defaultFormat); - double value = 10d; + log("\n==== DEFAULT NUMBER FORMAT ===="); while (it.hasNext()) { HSSFCell cell = (HSSFCell) it.next(); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java index 970b166dd7..d1c511feb2 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFFormulaEvaluator.java @@ -35,7 +35,7 @@ public final class TestHSSFFormulaEvaluator extends TestCase { HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("testNames.xls"); HSSFSheet sheet = wb.getSheetAt(0); HSSFCell cell = sheet.getRow(8).getCell(0); - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); CellValue cv = fe.evaluate(cell); assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType()); assertEquals(3.72, cv.getNumberValue(), 0.0); @@ -67,7 +67,7 @@ public final class TestHSSFFormulaEvaluator extends TestCase { setValue(sheet, 3, 6, 100.0); - HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); + HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb); assertEquals(26.0, fe.evaluate(cell0).getNumberValue(), 0.0); assertEquals(56.0, fe.evaluate(cell1).getNumberValue(), 0.0); } |