aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Burch <nick@apache.org>2008-07-10 20:37:09 +0000
committerNick Burch <nick@apache.org>2008-07-10 20:37:09 +0000
commit99b7fbfaea7af348bd13c61cd16cb5a3c8dc509a (patch)
tree6d08f74c6e5c988f4a743ef6b26892d33cf66902
parent1f301d019ba0c2bc66cf05a91f68c7b85176dae1 (diff)
downloadpoi-99b7fbfaea7af348bd13c61cd16cb5a3c8dc509a.tar.gz
poi-99b7fbfaea7af348bd13c61cd16cb5a3c8dc509a.zip
Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574,659576-660255,660257-660262,660264-660279,660281-660343,660345-660473,660475-660827,660829-660833,660835-660888,660890-663321,663323-663435,663437-663764,663766-663854,663856-664219,664221-664489,664494-664514,664516-668013,668015-668142,668144-668152,668154,668156-668256,668258,668260-669139,669141-669455,669457-669657,669659-669808,669810-670189,670191-671321,671323-672229,672231-672549,672551-672552,672554-672561,672563-672566,672568,672571-673049,673051-673852,673854-673862,673864-673986,673988-673996,673998-674347,674349-675673 via svnmerge from
https://svn.apache.org:443/repos/asf/poi/trunk ........ r674891 | nick | 2008-07-08 18:31:36 +0100 (Tue, 08 Jul 2008) | 1 line Test to check that formula stuff is mostly working with zip codes ........ r674911 | josh | 2008-07-08 19:56:21 +0100 (Tue, 08 Jul 2008) | 1 line Fix bug #45338 - JDK 1.4/1.5 issue from r673997 ........ r674937 | josh | 2008-07-08 21:19:40 +0100 (Tue, 08 Jul 2008) | 1 line Hooked up new junit in suite (from bug 45338) ........ r674953 | josh | 2008-07-08 22:00:13 +0100 (Tue, 08 Jul 2008) | 1 line Fix for bug 45354 - Proper distinguishing of cell references and named ranges within formulas ........ r675079 | josh | 2008-07-09 02:45:33 +0100 (Wed, 09 Jul 2008) | 1 line Fix for bug 45348 - required tweaks to RVA formula logic ........ r675086 | josh | 2008-07-09 03:15:52 +0100 (Wed, 09 Jul 2008) | 2 lines Minor junit fix after r674953 (bug 45354). Hooked up loose tests in suite. ........ r675218 | josh | 2008-07-09 15:58:06 +0100 (Wed, 09 Jul 2008) | 1 line Fix for bug 45367 - fixed boundary case when row zero is the last row removed from the sheet ........ r675661 | nick | 2008-07-10 18:52:33 +0100 (Thu, 10 Jul 2008) | 1 line Apply some changes inspired by bug # 45373, which improve the performance of HSSFSheet.shiftRows ........ r675671 | nick | 2008-07-10 19:41:25 +0100 (Thu, 10 Jul 2008) | 1 line Fix bug #45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@675718 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--src/documentation/content/xdocs/changes.xml5
-rw-r--r--src/documentation/content/xdocs/status.xml5
-rw-r--r--src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java48
-rw-r--r--src/java/org/apache/poi/hssf/eventusermodel/FormatTrackingHSSFListener.java62
-rw-r--r--src/java/org/apache/poi/hssf/model/FormulaParser.java25
-rw-r--r--src/java/org/apache/poi/hssf/model/OperandClassTransformer.java57
-rwxr-xr-xsrc/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java120
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFRow.java13
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java120
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java72
-rw-r--r--src/java/org/apache/poi/hssf/util/CellReference.java18
-rw-r--r--src/java/org/apache/poi/ss/util/CellReference.java262
-rw-r--r--src/testcases/org/apache/poi/hssf/HSSFTests.java2
-rw-r--r--src/testcases/org/apache/poi/hssf/data/45365.xlsbin0 -> 51712 bytes
-rw-r--r--src/testcases/org/apache/poi/hssf/data/testRVA.xlsbin32256 -> 32256 bytes
-rw-r--r--src/testcases/org/apache/poi/hssf/eventusermodel/AllEventUserModelTests.java38
-rw-r--r--src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java4
-rw-r--r--src/testcases/org/apache/poi/hssf/eventusermodel/TestFormatTrackingHSSFListener.java43
-rw-r--r--src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java34
-rw-r--r--src/testcases/org/apache/poi/hssf/model/TestOperandClassTransformer.java30
-rw-r--r--src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java10
-rw-r--r--src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java80
-rw-r--r--src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java14
-rwxr-xr-xsrc/testcases/org/apache/poi/hssf/util/AllHSSFUtilTests.java15
-rw-r--r--src/testcases/org/apache/poi/hssf/util/TestCellReference.java23
-rw-r--r--src/testcases/org/apache/poi/hssf/util/TestHSSFColor.java6
26 files changed, 782 insertions, 324 deletions
diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml
index 46b55c3ceb..f0c5d67ac2 100644
--- a/src/documentation/content/xdocs/changes.xml
+++ b/src/documentation/content/xdocs/changes.xml
@@ -50,6 +50,11 @@
<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="fix">45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra</action>
+ <action dev="POI-DEVELOPERS" type="fix">45373 - Improve the performance of HSSFSheet.shiftRows</action>
+ <action dev="POI-DEVELOPERS" type="fix">45367 - Fixed bug when last row removed from sheet is row zero</action>
+ <action dev="POI-DEVELOPERS" type="fix">45348 - Tweaks to RVA formula logic</action>
+ <action dev="POI-DEVELOPERS" type="fix">45354 - Fixed recognition of named ranges within formulas</action>
<action dev="POI-DEVELOPERS" type="fix">45338 - Fix HSSFWorkbook to give you the same HSSFFont every time, and then fix it to find newly added fonts</action>
<action dev="POI-DEVELOPERS" type="fix">45336 - Fix HSSFColor.getTripletHash()</action>
<action dev="POI-DEVELOPERS" type="fix">45334 - Fixed formula parser to handle dots in identifiers</action>
diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml
index 31427e939e..693fc42e2e 100644
--- a/src/documentation/content/xdocs/status.xml
+++ b/src/documentation/content/xdocs/status.xml
@@ -47,6 +47,11 @@
<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="fix">45365 - Handle more excel number formatting rules in FormatTrackingHSSFListener / XLS2CSVmra</action>
+ <action dev="POI-DEVELOPERS" type="fix">45373 - Improve the performance of HSSFSheet.shiftRows</action>
+ <action dev="POI-DEVELOPERS" type="fix">45367 - Fixed bug when last row removed from sheet is row zero</action>
+ <action dev="POI-DEVELOPERS" type="fix">45348 - Tweaks to RVA formula logic</action>
+ <action dev="POI-DEVELOPERS" type="fix">45354 - Fixed recognition of named ranges within formulas</action>
<action dev="POI-DEVELOPERS" type="fix">45338 - Fix HSSFWorkbook to give you the same HSSFFont every time, and then fix it to find newly added fonts</action>
<action dev="POI-DEVELOPERS" type="fix">45336 - Fix HSSFColor.getTripletHash()</action>
<action dev="POI-DEVELOPERS" type="fix">45334 - Fixed formula parser to handle dots in identifiers</action>
diff --git a/src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java b/src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java
index 1c9b220356..632eacf5cc 100644
--- a/src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java
+++ b/src/examples/src/org/apache/poi/hssf/eventusermodel/examples/XLS2CSVmra.java
@@ -20,10 +20,6 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
-import java.text.DateFormat;
-import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
@@ -37,7 +33,6 @@ import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.BlankRecord;
import org.apache.poi.hssf.record.BoolErrRecord;
-import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.LabelRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
@@ -47,7 +42,6 @@ import org.apache.poi.hssf.record.RKRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SSTRecord;
import org.apache.poi.hssf.record.StringRecord;
-import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
@@ -180,7 +174,7 @@ public class XLS2CSVmra implements HSSFListener {
nextRow = frec.getRow();
nextColumn = frec.getColumn();
} else {
- thisStr = formatNumberDateCell(frec, frec.getValue());
+ thisStr = formatListener.formatNumberDateCell(frec);
}
} else {
thisStr = '"' +
@@ -231,7 +225,7 @@ public class XLS2CSVmra implements HSSFListener {
thisColumn = numrec.getColumn();
// Format
- thisStr = formatNumberDateCell(numrec, numrec.getValue());
+ thisStr = formatListener.formatNumberDateCell(numrec);
break;
case RKRecord.sid:
RKRecord rkrec = (RKRecord) record;
@@ -290,44 +284,6 @@ public class XLS2CSVmra implements HSSFListener {
}
}
- /**
- * 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
- int formatIndex = formatListener.getFormatIndex(cell);
- String formatString = formatListener.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);
- }
- }
- }
-
-
public static void main(String[] args) throws Exception {
if(args.length < 1) {
System.err.println("Use:");
diff --git a/src/java/org/apache/poi/hssf/eventusermodel/FormatTrackingHSSFListener.java b/src/java/org/apache/poi/hssf/eventusermodel/FormatTrackingHSSFListener.java
index b88143713d..5a84f45646 100644
--- a/src/java/org/apache/poi/hssf/eventusermodel/FormatTrackingHSSFListener.java
+++ b/src/java/org/apache/poi/hssf/eventusermodel/FormatTrackingHSSFListener.java
@@ -16,7 +16,11 @@
==================================================================== */
package org.apache.poi.hssf.eventusermodel;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
@@ -24,8 +28,11 @@ import java.util.Map;
import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.ExtendedFormatRecord;
import org.apache.poi.hssf.record.FormatRecord;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
/**
* A proxy HSSFListener that keeps track of the document
@@ -70,6 +77,61 @@ public class FormatTrackingHSSFListener implements HSSFListener {
}
/**
+ * Formats the given numeric of date Cell's contents
+ * as a String, in as close as we can to the way
+ * that Excel would do so.
+ * Uses the various format records to manage this.
+ *
+ * TODO - move this to a central class in such a
+ * way that hssf.usermodel can make use of it too
+ */
+ public String formatNumberDateCell(CellValueRecordInterface cell) {
+ double value;
+ if(cell instanceof NumberRecord) {
+ value = ((NumberRecord)cell).getValue();
+ } else if(cell instanceof FormulaRecord) {
+ value = ((FormulaRecord)cell).getValue();
+ } else {
+ throw new IllegalArgumentException("Unsupported CellValue Record passed in " + cell);
+ }
+
+ // Get the built in format, if there is one
+ int formatIndex = getFormatIndex(cell);
+ String formatString = 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);
+ }
+ if(formatString == "0.00E+00") {
+ // This seems to mean output as a normal double
+ return Double.toString(value);
+ }
+
+ // Format as a number
+ DecimalFormat df = new DecimalFormat(formatString);
+ return df.format(value);
+ }
+ }
+ }
+
+ /**
* Returns the format string, eg $##.##, for the
* given number format index.
*/
diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java
index 2cc2a80531..afe7de239e 100644
--- a/src/java/org/apache/poi/hssf/model/FormulaParser.java
+++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java
@@ -29,6 +29,7 @@ import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.hssf.util.AreaReference;
import org.apache.poi.hssf.util.CellReference;
+import org.apache.poi.hssf.util.CellReference.NameType;
/**
* This class parses a formula string into a List of tokens in RPN order.
@@ -293,9 +294,14 @@ public final class FormulaParser {
// This can be either a cell ref or a named range
// Try to spot which it is
- if (isValidCellReference(name)) {
+ int nameType = CellReference.classifyCellReference(name);
+ if (nameType == NameType.CELL) {
return new RefPtg(name);
}
+ if (nameType != NameType.NAMED_RANGE) {
+ new FormulaParseException("Name '" + name
+ + "' does not look like a cell reference or named range");
+ }
for(int i = 0; i < book.getNumberOfNames(); i++) {
// named range name matching is case insensitive
@@ -303,11 +309,12 @@ public final class FormulaParser {
return new NamePtg(name, book);
}
}
- throw new FormulaParseException("Found reference to named range \""
- + name + "\", but that named range wasn't defined!");
+ throw new FormulaParseException("Specified named range '"
+ + name + "' does not exist in the current workbook.");
}
/**
+ * @param name an 'identifier' like string (i.e. contains alphanums, and dots)
* @return <code>null</code> if name cannot be split at a dot
*/
private AreaReference parseArea(String name) {
@@ -323,6 +330,8 @@ public final class FormulaParser {
return null;
}
}
+ // This expression is only valid as an area ref, if the LHS and RHS of the dot(s) are both
+ // cell refs. Otherwise, this expression must be a named range name
String partA = name.substring(0, dotPos);
if (!isValidCellReference(partA)) {
return null;
@@ -336,12 +345,14 @@ public final class FormulaParser {
return new AreaReference(topLeft, bottomRight);
}
+ /**
+ * @return <code>true</code> if the specified name is a valid cell reference
+ */
private static boolean isValidCellReference(String str) {
- // TODO - exact rules for recognising cell references may be too complicated for regex
- return CELL_REFERENCE_PATTERN.matcher(str).matches();
+ return CellReference.classifyCellReference(str) == NameType.CELL;
}
-
-
+
+
/**
* Note - Excel function names are 'case aware but not case sensitive'. This method may end
* up creating a defined name record in the workbook if the specified name is not an internal
diff --git a/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java b/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
index 5358324a38..07d2bd2fd6 100644
--- a/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
+++ b/src/java/org/apache/poi/hssf/model/OperandClassTransformer.java
@@ -71,11 +71,16 @@ final class OperandClassTransformer {
+ _formulaType + ") not supported yet");
}
- transformNode(rootNode, rootNodeOperandClass, false);
+ transformNode(rootNode, rootNodeOperandClass, false, false);
}
+ /**
+ * @param callerForceArrayFlag <code>true</code> if one of the current node's parents is a
+ * function Ptg which has been changed from default 'V' to 'A' type (due to requirements on
+ * the function return value).
+ */
private void transformNode(ParseNode node, byte desiredOperandClass,
- boolean callerForceArrayFlag) {
+ boolean callerForceArrayFlag, boolean isDirectChildOfValueOperator) {
Ptg token = node.getToken();
ParseNode[] children = node.getChildren();
if (token instanceof ValueOperatorPtg || token instanceof ControlPtg) {
@@ -84,7 +89,7 @@ final class OperandClassTransformer {
// but any child nodes are processed according to desiredOperandClass and callerForceArrayFlag
for (int i = 0; i < children.length; i++) {
ParseNode child = children[i];
- transformNode(child, desiredOperandClass, callerForceArrayFlag);
+ transformNode(child, desiredOperandClass, callerForceArrayFlag, true);
}
return;
}
@@ -101,22 +106,34 @@ final class OperandClassTransformer {
// nothing to do
return;
}
- if (callerForceArrayFlag) {
- switch (desiredOperandClass) {
- case Ptg.CLASS_VALUE:
- case Ptg.CLASS_ARRAY:
- token.setClass(Ptg.CLASS_ARRAY);
- break;
- case Ptg.CLASS_REF:
- token.setClass(Ptg.CLASS_REF);
- break;
- default:
- throw new IllegalStateException("Unexpected operand class ("
- + desiredOperandClass + ")");
- }
- } else {
- token.setClass(desiredOperandClass);
- }
+ 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 byte transformClass(byte currentOperandClass, byte desiredOperandClass,
+ boolean callerForceArrayFlag) {
+ switch (desiredOperandClass) {
+ case Ptg.CLASS_VALUE:
+ if (!callerForceArrayFlag) {
+ return Ptg.CLASS_VALUE;
+ }
+ // else fall through
+ case Ptg.CLASS_ARRAY:
+ return Ptg.CLASS_ARRAY;
+ case Ptg.CLASS_REF:
+ if (!callerForceArrayFlag) {
+ return currentOperandClass;
+ }
+ return Ptg.CLASS_REF;
+ }
+ throw new IllegalStateException("Unexpected operand class (" + desiredOperandClass + ")");
}
private void transformFunctionNode(AbstractFunctionPtg afp, ParseNode[] children,
@@ -200,7 +217,7 @@ final class OperandClassTransformer {
for (int i = 0; i < children.length; i++) {
ParseNode child = children[i];
byte paramOperandClass = afp.getParameterClass(i);
- transformNode(child, paramOperandClass, localForceArrayFlag);
+ transformNode(child, paramOperandClass, localForceArrayFlag, false);
}
}
}
diff --git a/src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java b/src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java
index 8e47cbe7a0..ace857da1e 100755
--- a/src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java
+++ b/src/java/org/apache/poi/hssf/record/formula/SheetNameFormatter.java
@@ -14,13 +14,14 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.hssf.record.formula;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import org.apache.poi.hssf.util.CellReference;
+
/**
* Formats sheet names for use in formula expressions.
*
@@ -28,14 +29,12 @@ import java.util.regex.Pattern;
*/
public final class SheetNameFormatter {
- private static final String BIFF8_LAST_COLUMN = "IV";
- private static final int BIFF8_LAST_COLUMN_TEXT_LEN = BIFF8_LAST_COLUMN.length();
- private static final String BIFF8_LAST_ROW = String.valueOf(0x10000);
- private static final int BIFF8_LAST_ROW_TEXT_LEN = BIFF8_LAST_ROW.length();
-
private static final char DELIMITER = '\'';
- private static final Pattern CELL_REF_PATTERN = Pattern.compile("([A-Za-z])+[0-9]+");
+ /**
+ * Matches a single cell ref with no absolute ('$') markers
+ */
+ private static final Pattern CELL_REF_PATTERN = Pattern.compile("([A-Za-z]+)([0-9]+)");
private SheetNameFormatter() {
// no instances of this class
@@ -105,27 +104,27 @@ public final class SheetNameFormatter {
return false;
}
- /**
- * @return <code>true</code> if the presence of the specified character in a sheet name would
- * require the sheet name to be delimited in formulas. This includes every non-alphanumeric
- * character besides underscore '_'.
- */
- /* package */ static boolean isSpecialChar(char ch) {
- // note - Character.isJavaIdentifierPart() would allow dollars '$'
- if(Character.isLetterOrDigit(ch)) {
- return false;
- }
- switch(ch) {
- case '_': // underscore is ok
- return false;
- case '\n':
- case '\r':
- case '\t':
- throw new RuntimeException("Illegal character (0x"
- + Integer.toHexString(ch) + ") found in sheet name");
- }
- return true;
- }
+ /**
+ * @return <code>true</code> if the presence of the specified character in a sheet name would
+ * require the sheet name to be delimited in formulas. This includes every non-alphanumeric
+ * character besides underscore '_'.
+ */
+ /* package */ static boolean isSpecialChar(char ch) {
+ // note - Character.isJavaIdentifierPart() would allow dollars '$'
+ if(Character.isLetterOrDigit(ch)) {
+ return false;
+ }
+ switch(ch) {
+ case '_': // underscore is ok
+ return false;
+ case '\n':
+ case '\r':
+ case '\t':
+ throw new RuntimeException("Illegal character (0x"
+ + Integer.toHexString(ch) + ") found in sheet name");
+ }
+ return true;
+ }
/**
@@ -149,64 +148,11 @@ public final class SheetNameFormatter {
* <p/>
* For better or worse this implementation attempts to replicate Excel's formula renderer.
* Excel uses range checking on the apparent 'row' and 'column' components. Note however that
- * the maximum sheet size varies across versions:
- * <p/>
- * <blockquote><table border="0" cellpadding="1" cellspacing="0"
- * summary="Notable cases.">
- * <tr><th>Version&nbsp;&nbsp;</th><th>File Format&nbsp;&nbsp;</th>
- * <th>Last Column&nbsp;&nbsp;</th><th>Last Row</th></tr>
- * <tr><td>97-2003</td><td>BIFF8</td><td>"IV" (2^8)</td><td>65536 (2^14)</td></tr>
- * <tr><td>2007</td><td>BIFF12</td><td>"XFD" (2^14)</td><td>1048576 (2^20)</td></tr>
- * </table></blockquote>
- * POI currently targets BIFF8 (Excel 97-2003), so the following behaviour can be observed for
- * this method:
- * <blockquote><table border="0" cellpadding="1" cellspacing="0"
- * summary="Notable cases.">
- * <tr><th>Input&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>
- * <th>Result&nbsp;</th></tr>
- * <tr><td>"A1", 1</td><td>true</td></tr>
- * <tr><td>"a111", 1</td><td>true</td></tr>
- * <tr><td>"A65536", 1</td><td>true</td></tr>
- * <tr><td>"A65537", 1</td><td>false</td></tr>
- * <tr><td>"iv1", 2</td><td>true</td></tr>
- * <tr><td>"IW1", 2</td><td>false</td></tr>
- * <tr><td>"AAA1", 3</td><td>false</td></tr>
- * <tr><td>"a111", 1</td><td>true</td></tr>
- * <tr><td>"Sheet1", 6</td><td>false</td></tr>
- * </table></blockquote>
+ * the maximum sheet size varies across versions.
+ * @see org.apache.poi.hssf.util.CellReference
*/
- /* package */ static boolean cellReferenceIsWithinRange(String rawSheetName, int numberOfLetters) {
-
- if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) {
- // "Sheet1" case etc
- return false; // that was easy
- }
- int nDigits = rawSheetName.length() - numberOfLetters;
- if(nDigits > BIFF8_LAST_ROW_TEXT_LEN) {
- return false;
- }
- if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) {
- String colStr = rawSheetName.substring(0, BIFF8_LAST_COLUMN_TEXT_LEN).toUpperCase();
- if(colStr.compareTo(BIFF8_LAST_COLUMN) > 0) {
- return false;
- }
- } else {
- // apparent column name has less chars than max
- // no need to check range
- }
-
- if(nDigits == BIFF8_LAST_ROW_TEXT_LEN) {
- String colStr = rawSheetName.substring(numberOfLetters);
- // ASCII comparison is valid if digit count is same
- if(colStr.compareTo(BIFF8_LAST_ROW) > 0) {
- return false;
- }
- } else {
- // apparent row has less chars than max
- // no need to check range
- }
-
- return true;
+ /* package */ static boolean cellReferenceIsWithinRange(String lettersPrefix, String numbersSuffix) {
+ return CellReference.cellReferenceIsWithinRange(lettersPrefix, numbersSuffix);
}
/**
@@ -239,7 +185,7 @@ public final class SheetNameFormatter {
// rawSheetName == "Sheet1" gets this far.
String lettersPrefix = matcher.group(1);
- return cellReferenceIsWithinRange(rawSheetName, lettersPrefix.length());
+ String numbersSuffix = matcher.group(2);
+ return cellReferenceIsWithinRange(lettersPrefix, numbersSuffix);
}
-
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
index 3e95aaee4c..0ddac1f6db 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
@@ -187,6 +187,19 @@ public final class HSSFRow implements Comparable, Row {
row.setFirstCol(findFirstCell(row.getFirstCol()));
}
}
+
+ /**
+ * Removes all the cells from the row, and their
+ * records too.
+ */
+ protected void removeAllCells() {
+ for(int i=0; i<cells.length; i++) {
+ if(cells[i] != null) {
+ removeCell(cells[i], true);
+ }
+ }
+ cells=new HSSFCell[INITIAL_CAPACITY];
+ }
/**
* create a high level HSSFCell object from an existing low level record. Should
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index 23ce6120a9..dc18e5599e 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -280,18 +280,19 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
/**
* used internally to refresh the "last row" when the last row is removed.
*/
-
- private int findLastRow(int lastrow)
- {
+ private int findLastRow(int lastrow) {
+ if (lastrow < 1) {
+ return -1;
+ }
int rownum = lastrow - 1;
HSSFRow r = getRow(rownum);
- while (r == null && rownum > 0)
- {
+ while (r == null && rownum > 0) {
r = getRow(--rownum);
}
- if (r == null)
- return -1;
+ if (r == null) {
+ return -1;
+ }
return rownum;
}
@@ -1225,6 +1226,28 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
*/
public void shiftRows( int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight)
{
+ shiftRows(startRow, endRow, n, copyRowHeight, resetOriginalRowHeight, true);
+ }
+
+ /**
+ * Shifts rows between startRow and endRow n number of rows.
+ * If you use a negative number, it will shift rows up.
+ * Code ensures that rows don't wrap around
+ *
+ * <p>
+ * Additionally shifts merged regions that are completely defined in these
+ * rows (ie. merged 2 cells on a row to be shifted).
+ * <p>
+ * TODO Might want to add bounds checking here
+ * @param startRow the row to start shifting
+ * @param endRow the row to end shifting
+ * @param n the number of rows to shift
+ * @param copyRowHeight whether to copy the row height during the shift
+ * @param resetOriginalRowHeight whether to set the original row's height to the default
+ * @param moveComments whether to move comments at the same time as the cells they are attached to
+ */
+ public void shiftRows( int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight, boolean moveComments)
+ {
int s, e, inc;
if ( n < 0 )
{
@@ -1250,44 +1273,55 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
row2Replace = createRow( rowNum + n );
HSSFCell cell;
-
-
-
-
- // Removes the cells before over writting them.
- for ( short col = row2Replace.getFirstCellNum(); col <= row2Replace.getLastCellNum(); col++ )
- {
- cell = row2Replace.getCell( col );
- if ( cell != null )
- row2Replace.removeCell( cell );
+
+ // Remove all the old cells from the row we'll
+ // be writing too, before we start overwriting
+ // any cells. This avoids issues with cells
+ // changing type, and records not being correctly
+ // overwritten
+ row2Replace.removeAllCells();
+
+ // If this row doesn't exist, nothing needs to
+ // be done for the now empty destination row
+ if (row == null) continue; // Nothing to do for this row
+
+ // Fetch the first and last columns of the
+ // row now, so we still have them to hand
+ // once we start removing cells
+ short firstCol = row.getFirstCellNum();
+ short lastCol = row.getLastCellNum();
+
+ // Fix up row heights if required
+ if (copyRowHeight) {
+ row2Replace.setHeight(row.getHeight());
+ }
+ if (resetOriginalRowHeight) {
+ row.setHeight((short)0xff);
}
- if (row == null) continue; // Nothing to do for this row
- else {
- if (copyRowHeight) {
- row2Replace.setHeight(row.getHeight());
- }
-
- if (resetOriginalRowHeight) {
- row.setHeight((short)0xff);
- }
- }
- for ( short col = row.getFirstCellNum(); col <= row.getLastCellNum(); col++ )
- {
- cell = row.getCell( col );
- if ( cell != null )
- {
- row.removeCell( cell );
- CellValueRecordInterface cellRecord = cell.getCellValueRecord();
- cellRecord.setRow( rowNum + n );
- row2Replace.createCellFromRecord( cellRecord );
- sheet.addValueRecord( rowNum + n, cellRecord );
- }
- // move comments if exist (can exist even if cell is null)
- HSSFComment comment = getCellComment(rowNum, col);
- if (comment != null) {
- comment.setRow(rowNum + n);
- }
+ // Copy each cell from the source row to
+ // the destination row
+ for(Iterator cells = row.cellIterator(); cells.hasNext(); ) {
+ cell = (HSSFCell)cells.next();
+ row.removeCell( cell );
+ CellValueRecordInterface cellRecord = cell.getCellValueRecord();
+ cellRecord.setRow( rowNum + n );
+ row2Replace.createCellFromRecord( cellRecord );
+ sheet.addValueRecord( rowNum + n, cellRecord );
+ }
+ // Now zap all the cells in the source row
+ row.removeAllCells();
+
+ // Move comments from the source row to the
+ // destination row. Note that comments can
+ // exist for cells which are null
+ if(moveComments) {
+ for( short col = firstCol; col <= lastCol; col++ ) {
+ HSSFComment comment = getCellComment(rowNum, col);
+ if (comment != null) {
+ comment.setRow(rowNum + n);
+ }
+ }
}
}
if ( endRow == lastrow || endRow + n > lastrow ) lastrow = Math.min( endRow + n, 65535 );
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
index b80ccb7905..09d66e1f60 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
@@ -376,28 +376,28 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
log.log(POILogger.DEBUG, "convertLabelRecords exit");
}
- /**
- * Retrieves the current policy on what to do when
- * getting missing or blank cells from a row.
- * The default is to return blank and null cells.
- * {@link MissingCellPolicy}
- */
- public MissingCellPolicy getMissingCellPolicy() {
- return missingCellPolicy;
- }
+ /**
+ * Retrieves the current policy on what to do when
+ * getting missing or blank cells from a row.
+ * The default is to return blank and null cells.
+ * {@link MissingCellPolicy}
+ */
+ public MissingCellPolicy getMissingCellPolicy() {
+ return missingCellPolicy;
+ }
- /**
- * Sets the policy on what to do when
- * getting missing or blank cells from a row.
- * This will then apply to all calls to
- * {@link Row.getCell()}. See
- * {@link MissingCellPolicy}
- */
- public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy) {
- this.missingCellPolicy = missingCellPolicy;
- }
+ /**
+ * Sets the policy on what to do when
+ * getting missing or blank cells from a row.
+ * This will then apply to all calls to
+ * {@link HSSFRow.getCell()}. See
+ * {@link MissingCellPolicy}
+ */
+ public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy) {
+ this.missingCellPolicy = missingCellPolicy;
+ }
- /**
+ /**
* sets the order of appearance for a given sheet.
*
* @param sheetname the name of the sheet to reorder
@@ -1041,11 +1041,11 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
String name, boolean italic, boolean strikeout,
short typeOffset, byte underline)
{
- for (short i=0; i<=getNumberOfFonts(); i++) {
- // Remember - there is no 4!
- if(i == 4) continue;
-
- HSSFFont hssfFont = getFontAt(i);
+ for (short i=0; i<=getNumberOfFonts(); i++) {
+ // Remember - there is no 4!
+ if(i == 4) continue;
+
+ HSSFFont hssfFont = getFontAt(i);
if (hssfFont.getBoldweight() == boldWeight
&& hssfFont.getColor() == color
&& hssfFont.getFontHeight() == fontHeight
@@ -1077,19 +1077,17 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
* @param idx index number
* @return HSSFFont at the index
*/
+ public HSSFFont getFontAt(short idx) {
+ if(fonts == null) fonts = new Hashtable();
+
+ // So we don't confuse users, give them back
+ // the same object every time, but create
+ // them lazily
+ Short sIdx = new Short(idx);
+ if(fonts.containsKey(sIdx)) {
+ return (HSSFFont)fonts.get(sIdx);
+ }
- public HSSFFont getFontAt(short idx)
- {
- if(fonts == null) fonts = new Hashtable();
-
- // So we don't confuse users, give them back
- // the same object every time, but create
- // them lazily
- Short sIdx = Short.valueOf(idx);
- if(fonts.containsKey(sIdx)) {
- return (HSSFFont)fonts.get(sIdx);
- }
-
FontRecord font = workbook.getFontRecordAt(idx);
HSSFFont retval = new HSSFFont(idx, font);
fonts.put(sIdx, retval);
diff --git a/src/java/org/apache/poi/hssf/util/CellReference.java b/src/java/org/apache/poi/hssf/util/CellReference.java
index c63e7bc999..0ddb0764ee 100644
--- a/src/java/org/apache/poi/hssf/util/CellReference.java
+++ b/src/java/org/apache/poi/hssf/util/CellReference.java
@@ -25,6 +25,15 @@ package org.apache.poi.hssf.util;
* @author Dennis Doubleday (patch to seperateRowColumns())
*/
public final class CellReference extends org.apache.poi.ss.util.CellReference {
+ /**
+ * Used to classify identifiers found in formulas as cell references or not.
+ */
+ public static final class NameType {
+ public static final int CELL = 1;
+ public static final int NAMED_RANGE = 2;
+ public static final int BAD_CELL_OR_NAMED_RANGE = -1;
+ }
+
/**
* Create an cell ref from a string representation. Sheet names containing special characters should be
* delimited and escaped as per normal syntax rules for formulas.
@@ -33,8 +42,15 @@ public final class CellReference extends org.apache.poi.ss.util.CellReference {
super(cellRef);
}
+ public CellReference(int pRow, int pCol) {
+ super(pRow, pCol, true, true);
+ }
+ public CellReference(int pRow, short pCol) {
+ super(pRow, (int)pCol, true, true);
+ }
+
public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
- this(null, pRow, pCol, pAbsRow, pAbsCol);
+ super(null, pRow, pCol, pAbsRow, pAbsCol);
}
public CellReference(String pSheetName, int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
super(pSheetName, pRow, pCol, pAbsRow, pAbsCol);
diff --git a/src/java/org/apache/poi/ss/util/CellReference.java b/src/java/org/apache/poi/ss/util/CellReference.java
index 4f90311f3b..b46cd88ccb 100644
--- a/src/java/org/apache/poi/ss/util/CellReference.java
+++ b/src/java/org/apache/poi/ss/util/CellReference.java
@@ -17,17 +17,26 @@
package org.apache.poi.ss.util;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
import org.apache.poi.hssf.record.formula.SheetNameFormatter;
/**
- * Common convertion functions between Excel style A1, C27 style
- * cell references, and POI usermodel style row=0, column=0
- * style references.
- * Applys for both HSSF and XSSF.
+ *
* @author Avik Sengupta
* @author Dennis Doubleday (patch to seperateRowColumns())
*/
public class CellReference {
+ /**
+ * Used to classify identifiers found in formulas as cell references or not.
+ */
+ public static final class NameType {
+ public static final int CELL = 1;
+ public static final int NAMED_RANGE = 2;
+ public static final int BAD_CELL_OR_NAMED_RANGE = -1;
+ }
+
/** The character ($) that signifies a row or column value is absolute instead of relative */
private static final char ABSOLUTE_REFERENCE_MARKER = '$';
/** The character (!) that separates sheet names from cell references */
@@ -35,6 +44,20 @@ public class CellReference {
/** The character (') used to quote sheet names when they contain special characters */
private static final char SPECIAL_NAME_DELIMITER = '\'';
+ /**
+ * Matches a run of letters followed by a run of digits. The run of letters is group 1 and the
+ * run of digits is group 2. Each group may optionally be prefixed with a single '$'.
+ */
+ private static final Pattern CELL_REF_PATTERN = Pattern.compile("\\$?([A-Za-z]+)\\$?([0-9]+)");
+ /**
+ * Named range names must start with a letter or underscore. Subsequent characters may include
+ * digits or dot. (They can even end in dot).
+ */
+ private static final Pattern NAMED_RANGE_NAME_PATTERN = Pattern.compile("[_A-Za-z][_.A-Za-z0-9]*");
+ private static final String BIFF8_LAST_COLUMN = "IV";
+ private static final int BIFF8_LAST_COLUMN_TEXT_LEN = BIFF8_LAST_COLUMN.length();
+ private static final String BIFF8_LAST_ROW = String.valueOf(0x10000);
+ private static final int BIFF8_LAST_ROW_TEXT_LEN = BIFF8_LAST_ROW.length();
private final int _rowIndex;
private final int _colIndex;
@@ -70,13 +93,13 @@ public class CellReference {
_rowIndex = Integer.parseInt(rowRef)-1; // -1 to convert 1-based to zero-based
}
- /**
- * Creates a cell reference for the given row and cell.
- * Assumes these references are relative
- */
- public CellReference(int row, int col) {
- this(row, col, false, false);
+ public CellReference(int pRow, int pCol) {
+ this(pRow, pCol, false, false);
}
+ public CellReference(int pRow, short pCol) {
+ this(pRow, (int)pCol, false, false);
+ }
+
public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
this(null, pRow, pCol, pAbsRow, pAbsCol);
}
@@ -97,7 +120,7 @@ public class CellReference {
}
public int getRow(){return _rowIndex;}
- public int getCol(){return _colIndex;}
+ public short getCol(){return (short) _colIndex;}
public boolean isRowAbsolute(){return _isRowAbs;}
public boolean isColAbsolute(){return _isColAbs;}
/**
@@ -111,27 +134,148 @@ public class CellReference {
/**
* takes in a column reference portion of a CellRef and converts it from
* ALPHA-26 number format to 0-based base 10.
- * ALPHA-26 goes A to Z, then AA to AZ, BA to BZ, ..., ZA to ZZ,
- * AAA to AAZ, ABA to ABZ, ..., AZA to AZZ, BAA to BAZ etc
*/
private int convertColStringToNum(String ref) {
- int lastIx = ref.length()-1;
- int retval=0;
- int pos = 0;
+ int lastIx = ref.length()-1;
+ int retval=0;
+ int pos = 0;
+
+ for (int k = lastIx; k > -1; k--) {
+ char thechar = ref.charAt(k);
+ // Character.getNumericValue() returns the values
+ // 10-35 for the letter A-Z
+ int shift = (int)Math.pow(26, pos);
+ retval += (Character.getNumericValue(thechar)-9) * shift;
+ pos++;
+ }
+ return retval-1;
+ }
- for (int k = lastIx; k > -1; k--) {
- char thechar = ref.charAt(k);
- // Character.getNumericValue() returns the values
- // 10-35 for the letter A-Z
- int shift = (int)Math.pow(26, pos);
- retval += (Character.getNumericValue(thechar)-9) * shift;
- pos++;
- }
- return retval-1;
+ /**
+ * Classifies an identifier as either a simple (2D) cell reference or a named range name
+ * @return one of the values from <tt>NameType</tt>
+ */
+ public static int classifyCellReference(String str) {
+ int len = str.length();
+ if (len < 1) {
+ throw new IllegalArgumentException("Empty string not allowed");
+ }
+ char firstChar = str.charAt(0);
+ switch (firstChar) {
+ case ABSOLUTE_REFERENCE_MARKER:
+ case '.':
+ case '_':
+ break;
+ default:
+ if (!Character.isLetter(firstChar)) {
+ throw new IllegalArgumentException("Invalid first char (" + firstChar
+ + ") of cell reference or named range. Letter expected");
+ }
+ }
+ if (!Character.isDigit(str.charAt(len-1))) {
+ // no digits at end of str
+ return validateNamedRangeName(str);
+ }
+ Matcher cellRefPatternMatcher = CELL_REF_PATTERN.matcher(str);
+ if (!cellRefPatternMatcher.matches()) {
+ return validateNamedRangeName(str);
+ }
+ String lettersGroup = cellRefPatternMatcher.group(1);
+ String digitsGroup = cellRefPatternMatcher.group(2);
+ if (cellReferenceIsWithinRange(lettersGroup, digitsGroup)) {
+ // valid cell reference
+ return NameType.CELL;
+ }
+ // If str looks like a cell reference, but is out of (row/col) range, it is a valid
+ // named range name
+ // This behaviour is a little weird. For example, "IW123" is a valid named range name
+ // because the column "IW" is beyond the maximum "IV". Note - this behaviour is version
+ // dependent. In Excel 2007, "IW123" is not a valid named range name.
+ if (str.indexOf(ABSOLUTE_REFERENCE_MARKER) >= 0) {
+ // Of course, named range names cannot have '$'
+ return NameType.BAD_CELL_OR_NAMED_RANGE;
+ }
+ return NameType.NAMED_RANGE;
}
+ private static int validateNamedRangeName(String str) {
+ if (!NAMED_RANGE_NAME_PATTERN.matcher(str).matches()) {
+ return NameType.BAD_CELL_OR_NAMED_RANGE;
+ }
+ return NameType.NAMED_RANGE;
+
+ }
+
+
+ /**
+ * Used to decide whether a name of the form "[A-Z]*[0-9]*" that appears in a formula can be
+ * interpreted as a cell reference. Names of that form can be also used for sheets and/or
+ * named ranges, and in those circumstances, the question of whether the potential cell
+ * reference is valid (in range) becomes important.
+ * <p/>
+ * Note - that the maximum sheet size varies across Excel versions:
+ * <p/>
+ * <blockquote><table border="0" cellpadding="1" cellspacing="0"
+ * summary="Notable cases.">
+ * <tr><th>Version&nbsp;&nbsp;</th><th>File Format&nbsp;&nbsp;</th>
+ * <th>Last Column&nbsp;&nbsp;</th><th>Last Row</th></tr>
+ * <tr><td>97-2003</td><td>BIFF8</td><td>"IV" (2^8)</td><td>65536 (2^14)</td></tr>
+ * <tr><td>2007</td><td>BIFF12</td><td>"XFD" (2^14)</td><td>1048576 (2^20)</td></tr>
+ * </table></blockquote>
+ * POI currently targets BIFF8 (Excel 97-2003), so the following behaviour can be observed for
+ * this method:
+ * <blockquote><table border="0" cellpadding="1" cellspacing="0"
+ * summary="Notable cases.">
+ * <tr><th>Input&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</th>
+ * <th>Result&nbsp;</th></tr>
+ * <tr><td>"A", "1"</td><td>true</td></tr>
+ * <tr><td>"a", "111"</td><td>true</td></tr>
+ * <tr><td>"A", "65536"</td><td>true</td></tr>
+ * <tr><td>"A", "65537"</td><td>false</td></tr>
+ * <tr><td>"iv", "1"</td><td>true</td></tr>
+ * <tr><td>"IW", "1"</td><td>false</td></tr>
+ * <tr><td>"AAA", "1"</td><td>false</td></tr>
+ * <tr><td>"a", "111"</td><td>true</td></tr>
+ * <tr><td>"Sheet", "1"</td><td>false</td></tr>
+ * </table></blockquote>
+ *
+ * @param colStr a string of only letter characters
+ * @param rowStr a string of only digit characters
+ * @return <code>true</code> if the row and col parameters are within range of a BIFF8 spreadsheet.
+ */
+ public static boolean cellReferenceIsWithinRange(String colStr, String rowStr) {
+ int numberOfLetters = colStr.length();
+ if(numberOfLetters > BIFF8_LAST_COLUMN_TEXT_LEN) {
+ // "Sheet1" case etc
+ return false; // that was easy
+ }
+ int nDigits = rowStr.length();
+ if(nDigits > BIFF8_LAST_ROW_TEXT_LEN) {
+ return false;
+ }
+ if(numberOfLetters == BIFF8_LAST_COLUMN_TEXT_LEN) {
+ if(colStr.toUpperCase().compareTo(BIFF8_LAST_COLUMN) > 0) {
+ return false;
+ }
+ } else {
+ // apparent column name has less chars than max
+ // no need to check range
+ }
+
+ if(nDigits == BIFF8_LAST_ROW_TEXT_LEN) {
+ // ASCII comparison is valid if digit count is same
+ if(rowStr.compareTo(BIFF8_LAST_ROW) > 0) {
+ return false;
+ }
+ } else {
+ // apparent row has less chars than max
+ // no need to check range
+ }
+
+ return true;
+ }
- /**
+ /**
* Separates the row from the columns and returns an array of three Strings. The first element
* is the sheet name. Only the first element may be null. The second element in is the column
* name still in ALPHA-26 number format. The third element is the row.
@@ -212,24 +356,24 @@ public class CellReference {
* eg column #3 -> D
*/
protected static String convertNumToColString(int col) {
- // Excel counts column A as the 1st column, we
- // treat it as the 0th one
- int excelColNum = col + 1;
-
- String colRef = "";
- int colRemain = excelColNum;
-
- while(colRemain > 0) {
- int thisPart = colRemain % 26;
- if(thisPart == 0) { thisPart = 26; }
- colRemain = (colRemain - thisPart) / 26;
-
- // The letter A is at 65
- char colChar = (char)(thisPart+64);
- colRef = colChar + colRef;
- }
-
- return colRef;
+ // Excel counts column A as the 1st column, we
+ // treat it as the 0th one
+ int excelColNum = col + 1;
+
+ String colRef = "";
+ int colRemain = excelColNum;
+
+ while(colRemain > 0) {
+ int thisPart = colRemain % 26;
+ if(thisPart == 0) { thisPart = 26; }
+ colRemain = (colRemain - thisPart) / 26;
+
+ // The letter A is at 65
+ char colChar = (char)(thisPart+64);
+ colRef = colChar + colRef;
+ }
+
+ return colRef;
}
/**
@@ -260,21 +404,21 @@ public class CellReference {
return sb.toString();
}
- /**
- * Returns the three parts of the cell reference, the
- * Sheet name (or null if none supplied), the 1 based
- * row number, and the A based column letter.
- * This will not include any markers for absolute
- * references, so use {@link #formatAsString()}
- * to properly turn references into strings.
- */
- public String[] getCellRefParts() {
- return new String[] {
- _sheetName,
- Integer.toString(_rowIndex+1),
- convertNumToColString(_colIndex)
- };
- }
+ /**
+ * Returns the three parts of the cell reference, the
+ * Sheet name (or null if none supplied), the 1 based
+ * row number, and the A based column letter.
+ * This will not include any markers for absolute
+ * references, so use {@link #formatAsString()}
+ * to properly turn references into strings.
+ */
+ public String[] getCellRefParts() {
+ return new String[] {
+ _sheetName,
+ Integer.toString(_rowIndex+1),
+ convertNumToColString(_colIndex)
+ };
+ }
/**
* Appends cell reference with '$' markers for absolute values as required.
diff --git a/src/testcases/org/apache/poi/hssf/HSSFTests.java b/src/testcases/org/apache/poi/hssf/HSSFTests.java
index 0a4fa66af7..ff15491d90 100644
--- a/src/testcases/org/apache/poi/hssf/HSSFTests.java
+++ b/src/testcases/org/apache/poi/hssf/HSSFTests.java
@@ -22,6 +22,7 @@ import junit.framework.TestSuite;
import org.apache.poi.hssf.eventmodel.TestEventRecordFactory;
import org.apache.poi.hssf.eventmodel.TestModelFactory;
+import org.apache.poi.hssf.eventusermodel.AllEventUserModelTests;
import org.apache.poi.hssf.model.AllModelTests;
import org.apache.poi.hssf.record.AllRecordTests;
import org.apache.poi.hssf.usermodel.AllUserModelTests;
@@ -48,6 +49,7 @@ public final class HSSFTests {
TestSuite suite = new TestSuite("Tests for org.apache.poi.hssf");
// $JUnit-BEGIN$
+ suite.addTest(AllEventUserModelTests.suite());
suite.addTest(AllModelTests.suite());
suite.addTest(AllUserModelTests.suite());
suite.addTest(AllRecordTests.suite());
diff --git a/src/testcases/org/apache/poi/hssf/data/45365.xls b/src/testcases/org/apache/poi/hssf/data/45365.xls
new file mode 100644
index 0000000000..fbf1d97c58
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/45365.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/data/testRVA.xls b/src/testcases/org/apache/poi/hssf/data/testRVA.xls
index f23821117e..17aa9fd710 100644
--- a/src/testcases/org/apache/poi/hssf/data/testRVA.xls
+++ b/src/testcases/org/apache/poi/hssf/data/testRVA.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/eventusermodel/AllEventUserModelTests.java b/src/testcases/org/apache/poi/hssf/eventusermodel/AllEventUserModelTests.java
new file mode 100644
index 0000000000..41e3b86322
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/eventusermodel/AllEventUserModelTests.java
@@ -0,0 +1,38 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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.eventusermodel;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Collects all tests for <tt>org.apache.poi.hssf.eventusermodel</tt>.
+ *
+ * @author Josh Micich
+ */
+public class AllEventUserModelTests {
+
+ public static Test suite() {
+ TestSuite result = new TestSuite(AllEventUserModelTests.class.getName());
+ result.addTestSuite(TestEventWorkbookBuilder.class);
+ result.addTestSuite(TestFormatTrackingHSSFListener.class);
+ result.addTestSuite(TestHSSFEventFactory.class);
+ result.addTestSuite(TestMissingRecordAwareHSSFListener.class);
+ return result;
+ }
+}
diff --git a/src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java b/src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java
index adf084331a..215732deb5 100644
--- a/src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java
+++ b/src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java
@@ -134,7 +134,7 @@ public final class TestEventWorkbookBuilder extends TestCase {
fr = (FormulaRecord)mockListen._frecs.get(5);
assertEquals(6, fr.getRow());
assertEquals(0, fr.getColumn());
- assertEquals("SUM('Sh3'!A1:A4)", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
+ assertEquals("SUM(Sh3!A1:A4)", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
// Now, load via Usermodel and re-check
@@ -142,7 +142,7 @@ public final class TestEventWorkbookBuilder extends TestCase {
POIFSFileSystem fs = new POIFSFileSystem(is);
HSSFWorkbook wb = new HSSFWorkbook(fs);
assertEquals("Sheet1!A1", wb.getSheetAt(0).getRow(1).getCell(0).getCellFormula());
- assertEquals("SUM('Sh3'!A1:A4)", wb.getSheetAt(0).getRow(6).getCell(0).getCellFormula());
+ assertEquals("SUM(Sh3!A1:A4)", wb.getSheetAt(0).getRow(6).getCell(0).getCellFormula());
}
private static final class MockHSSFListener implements HSSFListener {
diff --git a/src/testcases/org/apache/poi/hssf/eventusermodel/TestFormatTrackingHSSFListener.java b/src/testcases/org/apache/poi/hssf/eventusermodel/TestFormatTrackingHSSFListener.java
index e52a3bc961..60f5d2ca72 100644
--- a/src/testcases/org/apache/poi/hssf/eventusermodel/TestFormatTrackingHSSFListener.java
+++ b/src/testcases/org/apache/poi/hssf/eventusermodel/TestFormatTrackingHSSFListener.java
@@ -24,6 +24,9 @@ import java.util.List;
import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.record.CellValueRecordInterface;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
@@ -31,16 +34,17 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem;
*/
public final class TestFormatTrackingHSSFListener extends TestCase {
private FormatTrackingHSSFListener listener;
-
- public void setUp() {
+ private MockHSSFListener mockListen;
+
+ private void processFile(String filename) throws Exception {
HSSFRequest req = new HSSFRequest();
- MockHSSFListener mockListen = new MockHSSFListener();
+ mockListen = new MockHSSFListener();
listener = new FormatTrackingHSSFListener(mockListen);
req.addListenerForAllRecords(listener);
HSSFEventFactory factory = new HSSFEventFactory();
try {
- InputStream is = HSSFTestDataSamples.openSampleFileStream("MissingBits.xls");
+ InputStream is = HSSFTestDataSamples.openSampleFileStream(filename);
POIFSFileSystem fs = new POIFSFileSystem(is);
factory.processWorkbookEvents(req, fs);
} catch (IOException e) {
@@ -49,11 +53,42 @@ public final class TestFormatTrackingHSSFListener extends TestCase {
}
public void testFormats() throws Exception {
+ processFile("MissingBits.xls");
+
assertEquals("_(*#,##0_);_(*(#,##0);_(* \"-\"_);_(@_)", listener.getFormatString(41));
assertEquals("_($*#,##0_);_($*(#,##0);_($* \"-\"_);_(@_)", listener.getFormatString(42));
assertEquals("_(*#,##0.00_);_(*(#,##0.00);_(*\"-\"??_);_(@_)", listener.getFormatString(43));
}
+ /**
+ * Ensure that all number and formula records can be
+ * turned into strings without problems
+ */
+ public void testTurnToString() throws Exception {
+ processFile("45365.xls");
+
+ for(int i=0; i<mockListen._records.size(); i++) {
+ Record r = (Record)mockListen._records.get(i);
+ CellValueRecordInterface cvr = null;
+
+ if(r instanceof NumberRecord) {
+ cvr = (CellValueRecordInterface)r;
+ }
+ if(r instanceof FormulaRecord) {
+ cvr = (CellValueRecordInterface)r;
+ }
+
+ if(cvr != null) {
+ // Should always give us a string
+ String s = listener.formatNumberDateCell(cvr);
+ assertNotNull(s);
+ assertTrue(s.length() > 0);
+ }
+ }
+
+ // TODO - test some specific format strings
+ }
+
private static final class MockHSSFListener implements HSSFListener {
public MockHSSFListener() {}
private final List _records = new ArrayList();
diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
index 115a7e081b..a21850887c 100644
--- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
+++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
@@ -46,6 +46,7 @@ import org.apache.poi.hssf.record.formula.SubtractPtg;
import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFName;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
@@ -791,4 +792,37 @@ public final class TestFormulaParser extends TestCase {
assertEquals("ERROR.TYPE", funcPtg.getName());
}
+ public void testNamedRangeThatLooksLikeCell() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet("Sheet1");
+ HSSFName name = wb.createName();
+ name.setReference("Sheet1!B1");
+ name.setNameName("pfy1");
+
+ Ptg[] ptgs;
+ try {
+ ptgs = FormulaParser.parse("count(pfy1)", wb);
+ } catch (IllegalArgumentException e) {
+ if (e.getMessage().equals("Specified colIx (1012) is out of range")) {
+ throw new AssertionFailedError("Identified bug 45354");
+ }
+ throw e;
+ }
+ assertEquals(2, ptgs.length);
+ assertEquals(NamePtg.class, ptgs[0].getClass());
+
+ HSSFCell cell = sheet.createRow(0).createCell((short)0);
+ cell.setCellFormula("count(pfy1)");
+ assertEquals("COUNT(pfy1)", cell.getCellFormula());
+ try {
+ cell.setCellFormula("count(pf1)");
+ throw new AssertionFailedError("Expected formula parse execption");
+ } catch (FormulaParseException e) {
+ if (!e.getMessage().equals("Specified named range 'pf1' does not exist in the current workbook.")) {
+ throw e;
+ }
+ }
+ cell.setCellFormula("count(fp1)"); // plain cell ref, col is in range
+
+ }
}
diff --git a/src/testcases/org/apache/poi/hssf/model/TestOperandClassTransformer.java b/src/testcases/org/apache/poi/hssf/model/TestOperandClassTransformer.java
index 90a5d8b13f..47cfb574c1 100644
--- a/src/testcases/org/apache/poi/hssf/model/TestOperandClassTransformer.java
+++ b/src/testcases/org/apache/poi/hssf/model/TestOperandClassTransformer.java
@@ -57,6 +57,31 @@ public final class TestOperandClassTransformer extends TestCase {
confirmFuncClass(ptgs, 2, "INDEX", Ptg.CLASS_VALUE);
}
+ /**
+ * Even though count expects args of type R, because A1 is a direct operand of a
+ * value operator it must get type V
+ */
+ public void testDirectOperandOfValueOperator() {
+ String formula = "COUNT(A1*1)";
+ Ptg[] ptgs = FormulaParser.parse(formula, null);
+ if (ptgs[0].getPtgClass() == Ptg.CLASS_REF) {
+ throw new AssertionFailedError("Identified bug 45348");
+ }
+
+ confirmTokenClass(ptgs, 0, Ptg.CLASS_VALUE);
+ confirmTokenClass(ptgs, 3, Ptg.CLASS_VALUE);
+ }
+
+ /**
+ * A cell ref passed to a function expecting type V should be converted to type V
+ */
+ public void testRtoV() {
+
+ String formula = "lookup(A1, A3:A52, B3:B52)";
+ Ptg[] ptgs = FormulaParser.parse(formula, null);
+ confirmTokenClass(ptgs, 0, Ptg.CLASS_VALUE);
+ }
+
public void testComplexIRR_bug45041() {
String formula = "(1+IRR(SUMIF(A:A,ROW(INDIRECT(MIN(A:A)&\":\"&MAX(A:A))),B:B),0))^365-1";
Ptg[] ptgs = FormulaParser.parse(formula, null);
@@ -89,8 +114,11 @@ public final class TestOperandClassTransformer extends TestCase {
private void confirmTokenClass(Ptg[] ptgs, int i, byte operandClass) {
Ptg ptg = ptgs[i];
+ if (ptg.isBaseToken()) {
+ throw new AssertionFailedError("ptg[" + i + "] is a base token");
+ }
if (operandClass != ptg.getPtgClass()) {
- throw new AssertionFailedError("Wrong operand class for function ptg ("
+ throw new AssertionFailedError("Wrong operand class for ptg ("
+ ptg.toString() + "). Expected " + getOperandClassName(operandClass)
+ " but got " + getOperandClassName(ptg.getPtgClass()));
}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java b/src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java
index 768c429580..369c09583a 100644
--- a/src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java
+++ b/src/testcases/org/apache/poi/hssf/record/formula/TestSheetNameFormatter.java
@@ -14,7 +14,6 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.hssf.record.formula;
@@ -72,11 +71,14 @@ public final class TestSheetNameFormatter extends TestCase {
confirmCellNameMatch("aa1", true);
confirmCellNameMatch("A1A", false);
confirmCellNameMatch("A1A1", false);
+ confirmCellNameMatch("Sh3", false);
confirmCellNameMatch("SALES20080101", false); // out of range
}
private static void confirmCellRange(String text, int numberOfPrefixLetters, boolean expected) {
- assertEquals(expected, SheetNameFormatter.cellReferenceIsWithinRange(text, numberOfPrefixLetters));
+ String prefix = text.substring(0, numberOfPrefixLetters);
+ String suffix = text.substring(numberOfPrefixLetters);
+ assertEquals(expected, SheetNameFormatter.cellReferenceIsWithinRange(prefix, suffix));
}
/**
@@ -93,7 +95,7 @@ public final class TestSheetNameFormatter extends TestCase {
confirmCellRange("AAA1", 3, false);
confirmCellRange("a111", 1, true);
confirmCellRange("Sheet1", 6, false);
- confirmCellRange("iV65536", 2, true); // max cell in Excel 97-2003
- confirmCellRange("IW65537", 2, false);
+ confirmCellRange("iV65536", 2, true); // max cell in Excel 97-2003
+ confirmCellRange("IW65537", 2, false);
}
}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
index cf48573682..523d42ec9e 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
@@ -31,8 +31,12 @@ import org.apache.poi.ss.util.Region;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord;
+import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.NameRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
import org.apache.poi.util.TempFile;
@@ -1136,4 +1140,80 @@ public final class TestBugs extends TestCase {
)
);
}
+
+ /**
+ * From the mailing list - ensure we can handle a formula
+ * containing a zip code, eg ="70164"
+ * @throws Exception
+ */
+ public void testZipCodeFormulas() throws Exception {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet s = wb.createSheet();
+ s.createRow(0);
+ HSSFCell c1 = s.getRow(0).createCell((short)0);
+ HSSFCell c2 = s.getRow(0).createCell((short)1);
+
+ // As number and string
+ c1.setCellFormula("70164");
+ c2.setCellFormula("\"70164\"");
+
+ // Check the formulas
+ assertEquals("70164.0", c1.getCellFormula());
+ assertEquals("\"70164\"", c2.getCellFormula());
+
+ // And check the values - blank
+ assertEquals(0.0, c1.getNumericCellValue(), 0.00001);
+ assertEquals("", c1.getRichStringCellValue().getString());
+ assertEquals(0.0, c2.getNumericCellValue(), 0.00001);
+ assertEquals("", c2.getRichStringCellValue().getString());
+
+ // Now evaluate
+ HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(s, wb);
+ eval.setCurrentRow(s.getRow(0));
+ eval.evaluateFormulaCell(c1);
+ eval.evaluateFormulaCell(c2);
+
+ // Check
+ assertEquals(70164.0, c1.getNumericCellValue(), 0.00001);
+ assertEquals("", c1.getRichStringCellValue().getString());
+ assertEquals(0.0, c2.getNumericCellValue(), 0.00001);
+
+ // TODO - why isn't this working?
+// assertEquals("70164", c2.getRichStringCellValue().getString());
+
+
+ // Write and read
+ HSSFWorkbook nwb = writeOutAndReadBack(wb);
+ HSSFSheet ns = nwb.getSheetAt(0);
+ HSSFCell nc1 = ns.getRow(0).getCell((short)0);
+ HSSFCell nc2 = ns.getRow(0).getCell((short)1);
+
+ // Re-check
+ assertEquals(70164.0, nc1.getNumericCellValue(), 0.00001);
+ assertEquals("", nc1.getRichStringCellValue().getString());
+ assertEquals(0.0, nc2.getNumericCellValue(), 0.00001);
+ assertEquals("70164", nc2.getRichStringCellValue().getString());
+
+ // Now check record level stuff too
+ ns.getSheet().setLoc(0);
+ int fn = 0;
+ CellValueRecordInterface cvr;
+ while((cvr = ns.getSheet().getNextValueRecord()) != null) {
+ if(cvr instanceof FormulaRecordAggregate) {
+ FormulaRecordAggregate fr = (FormulaRecordAggregate)cvr;
+
+ if(fn == 0) {
+ assertEquals(70164.0, fr.getFormulaRecord().getValue(), 0.0001);
+ assertNull(fr.getStringRecord());
+ } else {
+ assertEquals(0.0, fr.getFormulaRecord().getValue(), 0.0001);
+ assertNotNull(fr.getStringRecord());
+ assertEquals("70164", fr.getStringRecord().getString());
+ }
+
+ fn++;
+ }
+ }
+ assertEquals(2, fn);
+ }
}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
index 3b154b2562..90971b3c03 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java
@@ -169,6 +169,20 @@ public final class TestHSSFSheet extends TestCase {
sheet.removeRow(row);
}
+ public void testRemoveZeroRow() {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("Sheet1");
+ HSSFRow row = sheet.createRow(0);
+ try {
+ sheet.removeRow(row);
+ } catch (IllegalArgumentException e) {
+ if (e.getMessage().equals("Invalid row number (-1) outside allowable range (0..65535)")) {
+ throw new AssertionFailedError("Identified bug 45367");
+ }
+ throw e;
+ }
+ }
+
public void testCloneSheet() {
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("Test Clone");
diff --git a/src/testcases/org/apache/poi/hssf/util/AllHSSFUtilTests.java b/src/testcases/org/apache/poi/hssf/util/AllHSSFUtilTests.java
index 1f7aa5f259..ec88e5c439 100755
--- a/src/testcases/org/apache/poi/hssf/util/AllHSSFUtilTests.java
+++ b/src/testcases/org/apache/poi/hssf/util/AllHSSFUtilTests.java
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
+
package org.apache.poi.hssf.util;
import junit.framework.Test;
@@ -28,12 +28,13 @@ import junit.framework.TestSuite;
public class AllHSSFUtilTests {
public static Test suite() {
- TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.util");
- result.addTestSuite(TestAreaReference.class);
- result.addTestSuite(TestCellReference.class);
- result.addTestSuite(TestRangeAddress.class);
- result.addTestSuite(TestRKUtil.class);
- result.addTestSuite(TestSheetReferences.class);
+ TestSuite result = new TestSuite(AllHSSFUtilTests.class.getName());
+ result.addTestSuite(TestAreaReference.class);
+ result.addTestSuite(TestCellReference.class);
+ result.addTestSuite(TestHSSFColor.class);
+ result.addTestSuite(TestRangeAddress.class);
+ result.addTestSuite(TestRKUtil.class);
+ result.addTestSuite(TestSheetReferences.class);
return result;
}
}
diff --git a/src/testcases/org/apache/poi/hssf/util/TestCellReference.java b/src/testcases/org/apache/poi/hssf/util/TestCellReference.java
index 648fb9a8e3..8ec8f99467 100644
--- a/src/testcases/org/apache/poi/hssf/util/TestCellReference.java
+++ b/src/testcases/org/apache/poi/hssf/util/TestCellReference.java
@@ -20,6 +20,8 @@ package org.apache.poi.hssf.util;
import junit.framework.TestCase;
+import org.apache.poi.hssf.util.CellReference.NameType;
+
public final class TestCellReference extends TestCase {
@@ -75,7 +77,6 @@ public final class TestCellReference extends TestCase {
confirmCell(cf, "Amazing!", 0, 0, false, false, "'Amazing!'!A1");
}
-
/* package */ static void confirmCell(CellReference cf, String expSheetName, int expRow,
int expCol, boolean expIsRowAbs, boolean expIsColAbs, String expText) {
@@ -87,8 +88,22 @@ public final class TestCellReference extends TestCase {
assertEquals("text is wrong", expText, cf.formatAsString());
}
- public static void main(String [] args) {
- System.out.println("Testing org.apache.poi.hssf.util.TestCellReference");
- junit.textui.TestRunner.run(TestCellReference.class);
+ public void testClassifyCellReference() {
+ confirmNameType("a1", NameType.CELL);
+ confirmNameType("pfy1", NameType.NAMED_RANGE);
+ confirmNameType("pf1", NameType.NAMED_RANGE); // (col) out of cell range
+ confirmNameType("fp1", NameType.CELL);
+ confirmNameType("pf$1", NameType.BAD_CELL_OR_NAMED_RANGE);
+ confirmNameType("_A1", NameType.NAMED_RANGE);
+ confirmNameType("A_1", NameType.NAMED_RANGE);
+ confirmNameType("A1_", NameType.NAMED_RANGE);
+ confirmNameType(".A1", NameType.BAD_CELL_OR_NAMED_RANGE);
+ confirmNameType("A.1", NameType.NAMED_RANGE);
+ confirmNameType("A1.", NameType.NAMED_RANGE);
+ }
+
+ private void confirmNameType(String ref, int expectedResult) {
+ int actualResult = CellReference.classifyCellReference(ref);
+ assertEquals(expectedResult, actualResult);
}
}
diff --git a/src/testcases/org/apache/poi/hssf/util/TestHSSFColor.java b/src/testcases/org/apache/poi/hssf/util/TestHSSFColor.java
index 42571eb323..d93f5d7b08 100644
--- a/src/testcases/org/apache/poi/hssf/util/TestHSSFColor.java
+++ b/src/testcases/org/apache/poi/hssf/util/TestHSSFColor.java
@@ -14,13 +14,15 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
+
package org.apache.poi.hssf.util;
import java.util.Hashtable;
import junit.framework.TestCase;
-
+/**
+ * @author Nick Burch
+ */
public final class TestHSSFColor extends TestCase {
public void testBasics() {
assertNotNull(HSSFColor.YELLOW.class);