Browse Source

Merged revisions 693591,693639,693658,693939,693941,693947,693990,694050,694065,694153,694534,694615,694619-694620,694631,694643,694877,694881 via svnmerge from

https://svn.apache.org/repos/asf/poi/trunk

........
  r693591 | josh | 2008-09-09 21:25:16 +0100 (Tue, 09 Sep 2008) | 1 line
  
  Added support for parsing array constants in formulas. (Helping investigation for bug 45752)
........
  r693639 | josh | 2008-09-09 23:26:28 +0100 (Tue, 09 Sep 2008) | 1 line
  
  removed debug code accidentally submitted with r693591
........
  r693658 | josh | 2008-09-10 00:46:46 +0100 (Wed, 10 Sep 2008) | 2 lines
  
  Fixed special cases of INDEX function (single columns / single rows, and errors)
........
  r693939 | josh | 2008-09-10 20:23:43 +0100 (Wed, 10 Sep 2008) | 1 line
  
  Fixing error value handling for numeric functions. Refactored hierarchy.
........
  r693941 | josh | 2008-09-10 20:27:24 +0100 (Wed, 10 Sep 2008) | 1 line
  
  (Should have been submitted with 693939) Fixing error value handling for numeric functions. Refactored hierarchy.
........
  r693947 | josh | 2008-09-10 20:33:58 +0100 (Wed, 10 Sep 2008) | 1 line
  
  (Should have been submitted with 693939) Fixing error value handling for numeric functions. Refactored hierarchy.
........
  r693990 | josh | 2008-09-10 22:21:28 +0100 (Wed, 10 Sep 2008) | 1 line
  
  Refactored hierarchy of MultiOperandNumericFunction. Fixed error value handling.  Enabled error value check in TestFormulasFromSpreadsheet
........
  r694050 | josh | 2008-09-10 23:43:30 +0100 (Wed, 10 Sep 2008) | 1 line
  
  Refactored finance functions.
........
  r694065 | josh | 2008-09-11 00:37:22 +0100 (Thu, 11 Sep 2008) | 1 line
  
  fixed special cases of MODE function
........
  r694153 | josh | 2008-09-11 08:16:20 +0100 (Thu, 11 Sep 2008) | 1 line
  
  Refactoring MultiOperandNumericFunction - removed Ref2DEval.
........
  r694534 | josh | 2008-09-12 00:18:50 +0100 (Fri, 12 Sep 2008) | 1 line
  
  Fix for bug 45639 - cleaned up index logic inside ColumnInfoRecordsAggregate
........
  r694615 | josh | 2008-09-12 07:14:07 +0100 (Fri, 12 Sep 2008) | 1 line
  
  small tweak to unit test which was silently creating UnknownPtgs 
........
  r694619 | josh | 2008-09-12 07:58:52 +0100 (Fri, 12 Sep 2008) | 1 line
  
  Removed trailing comma from output of HexDump.toHex()
........
  r694620 | josh | 2008-09-12 08:03:00 +0100 (Fri, 12 Sep 2008) | 1 line
  
  clarification of ArrayPtg size increment
........
  r694631 | josh | 2008-09-12 08:43:20 +0100 (Fri, 12 Sep 2008) | 1 line
  
  Extended support for cached results of formula cells
........
  r694643 | josh | 2008-09-12 09:18:54 +0100 (Fri, 12 Sep 2008) | 2 lines
  
  Made HSSFFormulaEvaluator no longer require initialisation with sheet or row.
........
  r694877 | josh | 2008-09-13 06:14:26 +0100 (Sat, 13 Sep 2008) | 1 line
  
  Refactored TextFunctions. Some minor fixes - test cases added.
........
  r694881 | josh | 2008-09-13 06:43:41 +0100 (Sat, 13 Sep 2008) | 1 line
  
  Added toString methods formatAsString to CellValue. Changed deprecation on CellValue.getRichTextStringValue
........


git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@694947 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_3_5_BETA3
Nick Burch 15 years ago
parent
commit
1255f2e0bc
100 changed files with 3624 additions and 5984 deletions
  1. 4
    0
      src/documentation/content/xdocs/changes.xml
  2. 4
    0
      src/documentation/content/xdocs/status.xml
  3. 100
    99
      src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java
  4. 28
    14
      src/java/org/apache/poi/hssf/extractor/ExcelExtractor.java
  5. 135
    40
      src/java/org/apache/poi/hssf/model/FormulaParser.java
  6. 48
    15
      src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
  7. 11
    16
      src/java/org/apache/poi/hssf/model/Sheet.java
  8. 77
    63
      src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java
  9. 427
    259
      src/java/org/apache/poi/hssf/record/FormulaRecord.java
  10. 6
    23
      src/java/org/apache/poi/hssf/record/RecordInputStream.java
  11. 474
    473
      src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java
  12. 40
    9
      src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
  13. 2
    2
      src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java
  14. 58
    32
      src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java
  15. 23
    10
      src/java/org/apache/poi/hssf/record/formula/ErrPtg.java
  16. 35
    31
      src/java/org/apache/poi/hssf/record/formula/Ptg.java
  17. 2
    3
      src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java
  18. 4
    4
      src/java/org/apache/poi/hssf/record/formula/atp/ParityFunction.java
  19. 6
    6
      src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java
  20. 34
    31
      src/java/org/apache/poi/hssf/record/formula/eval/ConcatEval.java
  21. 3
    3
      src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java
  22. 78
    81
      src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java
  23. 3
    8
      src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java
  24. 0
    53
      src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java
  25. 0
    179
      src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java
  26. 113
    0
      src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java
  27. 0
    84
      src/java/org/apache/poi/hssf/record/formula/functions/Atan2.java
  28. 0
    76
      src/java/org/apache/poi/hssf/record/formula/functions/Avedev.java
  29. 0
    76
      src/java/org/apache/poi/hssf/record/formula/functions/Average.java
  30. 0
    82
      src/java/org/apache/poi/hssf/record/formula/functions/Ceiling.java
  31. 0
    87
      src/java/org/apache/poi/hssf/record/formula/functions/Combin.java
  32. 0
    60
      src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java
  33. 0
    114
      src/java/org/apache/poi/hssf/record/formula/functions/Date.java
  34. 76
    0
      src/java/org/apache/poi/hssf/record/formula/functions/DateFunc.java
  35. 0
    77
      src/java/org/apache/poi/hssf/record/formula/functions/Devsq.java
  36. 1
    1
      src/java/org/apache/poi/hssf/record/formula/functions/Even.java
  37. 0
    84
      src/java/org/apache/poi/hssf/record/formula/functions/Exact.java
  38. 68
    62
      src/java/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java
  39. 0
    82
      src/java/org/apache/poi/hssf/record/formula/functions/Floor.java
  40. 2
    2
      src/java/org/apache/poi/hssf/record/formula/functions/FreeRefFunction.java
  41. 0
    75
      src/java/org/apache/poi/hssf/record/formula/functions/Fv.java
  42. 55
    3
      src/java/org/apache/poi/hssf/record/formula/functions/Index.java
  43. 1
    1
      src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java
  44. 0
    81
      src/java/org/apache/poi/hssf/record/formula/functions/Large.java
  45. 0
    107
      src/java/org/apache/poi/hssf/record/formula/functions/Left.java
  46. 0
    49
      src/java/org/apache/poi/hssf/record/formula/functions/Len.java
  47. 0
    87
      src/java/org/apache/poi/hssf/record/formula/functions/Log.java
  48. 0
    65
      src/java/org/apache/poi/hssf/record/formula/functions/Lower.java
  49. 0
    68
      src/java/org/apache/poi/hssf/record/formula/functions/Max.java
  50. 0
    66
      src/java/org/apache/poi/hssf/record/formula/functions/Maxa.java
  51. 0
    77
      src/java/org/apache/poi/hssf/record/formula/functions/Median.java
  52. 0
    87
      src/java/org/apache/poi/hssf/record/formula/functions/Mid.java
  53. 0
    68
      src/java/org/apache/poi/hssf/record/formula/functions/Min.java
  54. 0
    66
      src/java/org/apache/poi/hssf/record/formula/functions/Mina.java
  55. 40
    0
      src/java/org/apache/poi/hssf/record/formula/functions/MinaMaxa.java
  56. 0
    87
      src/java/org/apache/poi/hssf/record/formula/functions/Mod.java
  57. 121
    65
      src/java/org/apache/poi/hssf/record/formula/functions/Mode.java
  58. 184
    190
      src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java
  59. 0
    75
      src/java/org/apache/poi/hssf/record/formula/functions/Nper.java
  60. 304
    86
      src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java
  61. 0
    172
      src/java/org/apache/poi/hssf/record/formula/functions/NumericFunctionOneArg.java
  62. 1
    1
      src/java/org/apache/poi/hssf/record/formula/functions/Odd.java
  63. 0
    91
      src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java
  64. 0
    80
      src/java/org/apache/poi/hssf/record/formula/functions/Power.java
  65. 0
    68
      src/java/org/apache/poi/hssf/record/formula/functions/Product.java
  66. 0
    74
      src/java/org/apache/poi/hssf/record/formula/functions/Pv.java
  67. 57
    99
      src/java/org/apache/poi/hssf/record/formula/functions/Replace.java
  68. 0
    108
      src/java/org/apache/poi/hssf/record/formula/functions/Right.java
  69. 0
    83
      src/java/org/apache/poi/hssf/record/formula/functions/Round.java
  70. 0
    86
      src/java/org/apache/poi/hssf/record/formula/functions/Rounddown.java
  71. 0
    88
      src/java/org/apache/poi/hssf/record/formula/functions/Roundup.java
  72. 0
    81
      src/java/org/apache/poi/hssf/record/formula/functions/Small.java
  73. 0
    30
      src/java/org/apache/poi/hssf/record/formula/functions/StatsLib.java
  74. 0
    76
      src/java/org/apache/poi/hssf/record/formula/functions/Stdev.java
  75. 86
    101
      src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java
  76. 0
    68
      src/java/org/apache/poi/hssf/record/formula/functions/Sum.java
  77. 0
    70
      src/java/org/apache/poi/hssf/record/formula/functions/Sumsq.java
  78. 182
    95
      src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java
  79. 0
    53
      src/java/org/apache/poi/hssf/record/formula/functions/Trim.java
  80. 0
    65
      src/java/org/apache/poi/hssf/record/formula/functions/Upper.java
  81. 262
    257
      src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
  82. 3
    0
      src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
  83. 6
    7
      src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
  84. 29
    15
      src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
  85. 10
    0
      src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
  86. 29
    0
      src/java/org/apache/poi/hssf/usermodel/LazyAreaEval.java
  87. 33
    0
      src/java/org/apache/poi/hssf/usermodel/LazyRefEval.java
  88. 12
    15
      src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetector.java
  89. 219
    233
      src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java
  90. 8
    9
      src/java/org/apache/poi/ss/usermodel/LazyAreaEval.java
  91. 85
    85
      src/java/org/apache/poi/ss/usermodel/LazyRefEval.java
  92. 3
    1
      src/java/org/apache/poi/util/HexDump.java
  93. 6
    3
      src/java/org/apache/poi/util/HexRead.java
  94. 2
    0
      src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Cell.java
  95. 4
    1
      src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Comment.java
  96. 2
    0
      src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Cell.java
  97. 3
    3
      src/testcases/org/apache/poi/ddf/TestEscherBSERecord.java
  98. 9
    13
      src/testcases/org/apache/poi/ddf/TestEscherBlipWMFRecord.java
  99. 6
    9
      src/testcases/org/apache/poi/ddf/TestEscherChildAnchorRecord.java
  100. 0
    0
      src/testcases/org/apache/poi/ddf/TestEscherClientAnchorRecord.java

+ 4
- 0
src/documentation/content/xdocs/changes.xml View File

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

+ 4
- 0
src/documentation/content/xdocs/status.xml View File

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

+ 100
- 99
src/java/org/apache/poi/hssf/extractor/EventBasedExcelExtractor.java View File

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

+ 28
- 14
src/java/org/apache/poi/hssf/extractor/ExcelExtractor.java View File

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

+ 135
- 40
src/java/org/apache/poi/hssf/model/FormulaParser.java View File

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


+ 48
- 15
src/java/org/apache/poi/hssf/model/OperandClassTransformer.java View File

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

+ 11
- 16
src/java/org/apache/poi/hssf/model/Sheet.java View File

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

+ 77
- 63
src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java View File

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

+ 427
- 259
src/java/org/apache/poi/hssf/record/FormulaRecord.java View File

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


+ 6
- 23
src/java/org/apache/poi/hssf/record/RecordInputStream.java View File

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

+ 474
- 473
src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java View File

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


}

+ 40
- 9
src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java View File

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

+ 2
- 2
src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java View File

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

+ 58
- 32
src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java View File

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

+ 23
- 10
src/java/org/apache/poi/hssf/record/formula/ErrPtg.java View File

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

+ 35
- 31
src/java/org/apache/poi/hssf/record/formula/Ptg.java View File

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

+ 2
- 3
src/java/org/apache/poi/hssf/record/formula/atp/AnalysisToolPak.java View File

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

+ 4
- 4
src/java/org/apache/poi/hssf/record/formula/atp/ParityFunction.java View File

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

+ 6
- 6
src/java/org/apache/poi/hssf/record/formula/atp/YearFrac.java View File

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

+ 34
- 31
src/java/org/apache/poi/hssf/record/formula/eval/ConcatEval.java View File

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

+ 3
- 3
src/java/org/apache/poi/hssf/record/formula/eval/ExternalFunction.java View File

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

+ 78
- 81
src/java/org/apache/poi/hssf/record/formula/eval/FunctionEval.java View File

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

+ 3
- 8
src/java/org/apache/poi/hssf/record/formula/eval/OperandResolver.java View File

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


+ 0
- 53
src/java/org/apache/poi/hssf/record/formula/eval/Ref2DEval.java View File

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

+ 0
- 179
src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java View File

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

+ 113
- 0
src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java View File

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

+ 0
- 84
src/java/org/apache/poi/hssf/record/formula/functions/Atan2.java View File

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

}

+ 0
- 76
src/java/org/apache/poi/hssf/record/formula/functions/Avedev.java View File

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

+ 0
- 76
src/java/org/apache/poi/hssf/record/formula/functions/Average.java View File

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

+ 0
- 82
src/java/org/apache/poi/hssf/record/formula/functions/Ceiling.java View File

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

}

+ 0
- 87
src/java/org/apache/poi/hssf/record/formula/functions/Combin.java View File

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

}

+ 0
- 60
src/java/org/apache/poi/hssf/record/formula/functions/Concatenate.java View File

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

+ 0
- 114
src/java/org/apache/poi/hssf/record/formula/functions/Date.java View File

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

+ 76
- 0
src/java/org/apache/poi/hssf/record/formula/functions/DateFunc.java View File

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

+ 0
- 77
src/java/org/apache/poi/hssf/record/formula/functions/Devsq.java View File

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

+ 1
- 1
src/java/org/apache/poi/hssf/record/formula/functions/Even.java View File

@@ -22,7 +22,7 @@ package org.apache.poi.hssf.record.formula.functions;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class Even extends NumericFunctionOneArg {
public final class Even extends NumericFunction.OneArg {

private static final long PARITY_MASK = 0xFFFFFFFFFFFFFFFEL;


+ 0
- 84
src/java/org/apache/poi/hssf/record/formula/functions/Exact.java View File

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

+ 68
- 62
src/java/org/apache/poi/hssf/record/formula/functions/FinanceFunction.java View File

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

+ 0
- 82
src/java/org/apache/poi/hssf/record/formula/functions/Floor.java View File

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

}

+ 2
- 2
src/java/org/apache/poi/hssf/record/formula/functions/FreeRefFunction.java View File

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

+ 0
- 75
src/java/org/apache/poi/hssf/record/formula/functions/Fv.java View File

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

}

+ 55
- 3
src/java/org/apache/poi/hssf/record/formula/functions/Index.java View File

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

+ 1
- 1
src/java/org/apache/poi/hssf/record/formula/functions/Indirect.java View File

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

+ 0
- 81
src/java/org/apache/poi/hssf/record/formula/functions/Large.java View File

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

+ 0
- 107
src/java/org/apache/poi/hssf/record/formula/functions/Left.java View File

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

+ 0
- 49
src/java/org/apache/poi/hssf/record/formula/functions/Len.java View File

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

package org.apache.poi.hssf.record.formula.functions;

import org.apache.poi.hssf.record.formula.eval.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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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();
}
}
}

+ 0
- 87
src/java/org/apache/poi/hssf/record/formula/functions/Log.java View File

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

}

+ 0
- 65
src/java/org/apache/poi/hssf/record/formula/functions/Lower.java View File

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

+ 0
- 68
src/java/org/apache/poi/hssf/record/formula/functions/Max.java View File

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

+ 0
- 66
src/java/org/apache/poi/hssf/record/formula/functions/Maxa.java View File

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

+ 0
- 77
src/java/org/apache/poi/hssf/record/formula/functions/Median.java View File

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

+ 0
- 87
src/java/org/apache/poi/hssf/record/formula/functions/Mid.java View File

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

package org.apache.poi.hssf.record.formula.functions;

import 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 &lt; wilson at c bio dot msk cc dot org &gt;
*/
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);
}
}

+ 0
- 68
src/java/org/apache/poi/hssf/record/formula/functions/Min.java View File

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

+ 0
- 66
src/java/org/apache/poi/hssf/record/formula/functions/Mina.java View File

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

+ 40
- 0
src/java/org/apache/poi/hssf/record/formula/functions/MinaMaxa.java View File

@@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
public abstract class MinaMaxa extends MultiOperandNumericFunction {

protected MinaMaxa() {
super(true, true);
}

public static final Function MAXA = new MinaMaxa() {
protected double evaluate(double[] values) {
return values.length > 0 ? MathX.max(values) : 0;
}
};
public static final Function MINA = new MinaMaxa() {
protected double evaluate(double[] values) {
return values.length > 0 ? MathX.min(values) : 0;
}
};
}

+ 0
- 87
src/java/org/apache/poi/hssf/record/formula/functions/Mod.java View File

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

}

+ 121
- 65
src/java/org/apache/poi/hssf/record/formula/functions/Mode.java View File

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

+ 184
- 190
src/java/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java View File

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

+ 0
- 75
src/java/org/apache/poi/hssf/record/formula/functions/Nper.java View File

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

}

+ 304
- 86
src/java/org/apache/poi/hssf/record/formula/functions/NumericFunction.java View File

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

+ 0
- 172
src/java/org/apache/poi/hssf/record/formula/functions/NumericFunctionOneArg.java View File

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

+ 1
- 1
src/java/org/apache/poi/hssf/record/formula/functions/Odd.java View File

@@ -22,7 +22,7 @@ package org.apache.poi.hssf.record.formula.functions;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
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) {

+ 0
- 91
src/java/org/apache/poi/hssf/record/formula/functions/Pmt.java View File

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

+ 0
- 80
src/java/org/apache/poi/hssf/record/formula/functions/Power.java View File

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

}

+ 0
- 68
src/java/org/apache/poi/hssf/record/formula/functions/Product.java View File

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

+ 0
- 74
src/java/org/apache/poi/hssf/record/formula/functions/Pv.java View File

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

+ 57
- 99
src/java/org/apache/poi/hssf/record/formula/functions/Replace.java View File

@@ -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 &lt; wilson at c bio dot msk cc dot org &gt;
*/
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());
}
}

+ 0
- 108
src/java/org/apache/poi/hssf/record/formula/functions/Right.java View File

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

+ 0
- 83
src/java/org/apache/poi/hssf/record/formula/functions/Round.java View File

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

+ 0
- 86
src/java/org/apache/poi/hssf/record/formula/functions/Rounddown.java View File

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

+ 0
- 88
src/java/org/apache/poi/hssf/record/formula/functions/Roundup.java View File

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

+ 0
- 81
src/java/org/apache/poi/hssf/record/formula/functions/Small.java View File

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

+ 0
- 30
src/java/org/apache/poi/hssf/record/formula/functions/StatsLib.java View File

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

+ 0
- 76
src/java/org/apache/poi/hssf/record/formula/functions/Stdev.java View File

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

+ 86
- 101
src/java/org/apache/poi/hssf/record/formula/functions/Substitute.java View File

@@ -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 &lt; wilson at c bio dot msk cc dot org &gt;
*/
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();
}
}
}

+ 0
- 68
src/java/org/apache/poi/hssf/record/formula/functions/Sum.java View File

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

+ 0
- 70
src/java/org/apache/poi/hssf/record/formula/functions/Sumsq.java View File

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

+ 182
- 95
src/java/org/apache/poi/hssf/record/formula/functions/TextFunction.java View File

@@ -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 &lt; wilson at c bio dot msk cc dot org &gt;
*/
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 &lt; wilson at c bio dot msk cc dot org &gt;
*/
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));
}
};
}

+ 0
- 53
src/java/org/apache/poi/hssf/record/formula/functions/Trim.java View File

@@ -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 &lt; wilson at c bio dot msk cc dot org &gt;
*/
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();
}
}
}

+ 0
- 65
src/java/org/apache/poi/hssf/record/formula/functions/Upper.java View File

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

+ 262
- 257
src/java/org/apache/poi/hssf/usermodel/HSSFCell.java View File

@@ -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&lt;errIdx&gt;
*/
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();
}
}

+ 3
- 0
src/java/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java View File

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

+ 6
- 7
src/java/org/apache/poi/hssf/usermodel/HSSFRow.java View File

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

+ 29
- 15
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java View File

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

+ 10
- 0
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java View File

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

+ 29
- 0
src/java/org/apache/poi/hssf/usermodel/LazyAreaEval.java View File

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

+ 33
- 0
src/java/org/apache/poi/hssf/usermodel/LazyRefEval.java View File

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

+ 12
- 15
src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetector.java View File

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

+ 219
- 233
src/java/org/apache/poi/ss/usermodel/FormulaEvaluator.java View File

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

src/java/org/apache/poi/hssf/record/formula/eval/LazyAreaEval.java → src/java/org/apache/poi/ss/usermodel/LazyAreaEval.java View File

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

src/java/org/apache/poi/hssf/record/formula/eval/LazyRefEval.java → src/java/org/apache/poi/ss/usermodel/LazyRefEval.java View File

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

+ 3
- 1
src/java/org/apache/poi/util/HexDump.java View File

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

+ 6
- 3
src/java/org/apache/poi/util/HexRead.java View File

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

+ 2
- 0
src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Cell.java View File

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

+ 4
- 1
src/ooxml/interfaces-jdk14/org/apache/poi/ss/usermodel/Comment.java View File

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

+ 2
- 0
src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Cell.java View File

@@ -106,6 +106,8 @@ public interface Cell {

int getRowIndex();

Sheet getSheet();

/**
* set the cells type (numeric, formula or string)
* @see #CELL_TYPE_NUMERIC

+ 3
- 3
src/testcases/org/apache/poi/ddf/TestEscherBSERecord.java View File

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

+ 9
- 13
src/testcases/org/apache/poi/ddf/TestEscherBlipWMFRecord.java View File

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

}


+ 6
- 9
src/testcases/org/apache/poi/ddf/TestEscherChildAnchorRecord.java View File

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

}


+ 0
- 0
src/testcases/org/apache/poi/ddf/TestEscherClientAnchorRecord.java View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save