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-ffa450edef68tags/REL_3_5_BETA3
@@ -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> |
@@ -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> |
@@ -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); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -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 |
@@ -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]; | |||
@@ -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); | |||
} | |||
} | |||
} |
@@ -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 |
@@ -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() { |
@@ -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; | |||
} | |||
} | |||
@@ -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(); |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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() { |
@@ -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; | |||
} | |||
} |
@@ -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 + ")"); | |||
} | |||
} |
@@ -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++) { |
@@ -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; | |||
} | |||
}; |
@@ -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) { |
@@ -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); | |||
} | |||
} |
@@ -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()); | |||
} |
@@ -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, |
@@ -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 |
@@ -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; | |||
} | |||
@@ -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 | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
}; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
}; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); |
@@ -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; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
}; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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() + ")"); | |||
} | |||
} |
@@ -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() + ")"); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
}; | |||
} |
@@ -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); | |||
} | |||
}; | |||
} |
@@ -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) { |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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)); | |||
} | |||
}; | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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 |
@@ -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; |
@@ -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); | |||
} | |||
} | |||
} | |||
@@ -1601,15 +1599,33 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet | |||
return patriarch; | |||
} | |||
/** | |||
* @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) |
@@ -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 |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 {" |
@@ -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 + ">"; | |||
} | |||
} | |||
} |
@@ -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) { |
@@ -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(); | |||
} | |||
} |
@@ -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(); |
@@ -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 |
@@ -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(); |
@@ -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); | |||
} |
@@ -106,6 +106,8 @@ public interface Cell { | |||
int getRowIndex(); | |||
Sheet getSheet(); | |||
/** | |||
* set the cells type (numeric, formula or string) | |||
* @see #CELL_TYPE_NUMERIC |
@@ -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 + |
@@ -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() ); | |||
} | |||
} | |||
@@ -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; | |||
} | |||
} | |||