+ * Title: Area 3D Ptg - 3D reference (Sheet + Area)
* Description: Defined a area in Extern Sheet.
* REFERENCE:
* @author Libin Roman (Vista Portal LDT. Developer)
@@ -35,7 +35,6 @@ import org.apache.poi.util.LittleEndian;
* @author Jason Height (jheight at chariot dot net dot au)
* @version 1.0-pre
*/
-
public class Area3DPtg extends Ptg implements AreaI
{
public final static byte sid = 0x3b;
@@ -84,23 +83,15 @@ public class Area3DPtg extends Ptg implements AreaI
setExternSheetIndex(externalSheetIndex);
}
- public String toString()
- {
- StringBuffer buffer = new StringBuffer();
-
- buffer.append( "AreaPtg\n" );
- buffer.append( "Index to Extern Sheet = " + getExternSheetIndex() ).append( "\n" );
- buffer.append( "firstRow = " + getFirstRow() ).append( "\n" );
- buffer.append( "lastRow = " + getLastRow() ).append( "\n" );
- buffer.append( "firstCol = " + getFirstColumn() ).append( "\n" );
- buffer.append( "lastCol = " + getLastColumn() ).append( "\n" );
- buffer.append( "firstColRel= "
- + isFirstRowRelative() ).append( "\n" );
- buffer.append( "lastColRowRel = "
- + isLastRowRelative() ).append( "\n" );
- buffer.append( "firstColRel = " + isFirstColRelative() ).append( "\n" );
- buffer.append( "lastColRel = " + isLastColRelative() ).append( "\n" );
- return buffer.toString();
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getClass().getName());
+ sb.append(" [");
+ sb.append("sheetIx=").append(getExternSheetIndex());
+ sb.append(" ! ");
+ sb.append(AreaReference.formatAsString(this));
+ sb.append("]");
+ return sb.toString();
}
public void writeBytes( byte[] array, int offset )
@@ -284,7 +275,7 @@ public class Area3DPtg extends Ptg implements AreaI
}
// Now the normal area bit
- retval.append( AreaPtg.toFormulaString(this, book) );
+ retval.append(AreaReference.formatAsString(this));
// All done
return retval.toString();
@@ -326,6 +317,7 @@ public class Area3DPtg extends Ptg implements AreaI
public int hashCode()
{
+ // TODO - hashCode seems to be unused
int result;
result = (int) field_1_index_extern_sheet;
result = 29 * result + (int) field_2_first_row;
diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java
index c4e7534c7..38cae39f4 100644
--- a/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java
+++ b/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java
@@ -114,23 +114,13 @@ public class AreaPtg extends Ptg implements AreaI {
return "AreaPtg";
}
- public String toString()
- {
- StringBuffer buffer = new StringBuffer();
-
- buffer.append(getAreaPtgName());
- buffer.append("\n");
- buffer.append("firstRow = " + getFirstRow()).append("\n");
- buffer.append("lastRow = " + getLastRow()).append("\n");
- buffer.append("firstCol = " + getFirstColumn()).append("\n");
- buffer.append("lastCol = " + getLastColumn()).append("\n");
- buffer.append("firstColRowRel= "
- + isFirstRowRelative()).append("\n");
- buffer.append("lastColRowRel = "
- + isLastRowRelative()).append("\n");
- buffer.append("firstColRel = " + isFirstColRelative()).append("\n");
- buffer.append("lastColRel = " + isLastColRelative()).append("\n");
- return buffer.toString();
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getClass().getName());
+ sb.append(" [");
+ sb.append(AreaReference.formatAsString(this));
+ sb.append("]");
+ return sb.toString();
}
public void writeBytes(byte [] array, int offset) {
@@ -307,19 +297,8 @@ public class AreaPtg extends Ptg implements AreaI {
field_4_last_column = column;
}
- public String toFormulaString(Workbook book)
- {
- return toFormulaString(this, book);
- }
- protected static String toFormulaString(AreaI area, Workbook book) {
- CellReference topLeft = new CellReference(area.getFirstRow(),area.getFirstColumn(),!area.isFirstRowRelative(),!area.isFirstColRelative());
- CellReference botRight = new CellReference(area.getLastRow(),area.getLastColumn(),!area.isLastRowRelative(),!area.isLastColRelative());
-
- if(AreaReference.isWholeColumnReference(topLeft, botRight)) {
- return (new AreaReference(topLeft, botRight)).formatAsString();
- } else {
- return topLeft.formatAsString() + ":" + botRight.formatAsString();
- }
+ public String toFormulaString(Workbook book) {
+ return AreaReference.formatAsString(this);
}
public byte getDefaultOperandClass() {
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
index 08ddb9656..32fce2f09 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
@@ -15,12 +15,6 @@
limitations under the License.
==================================================================== */
-
-/*
- * HSSFWorkbook.java
- *
- * Created on September 30, 2001, 3:37 PM
- */
package org.apache.poi.hssf.usermodel;
import java.io.ByteArrayInputStream;
@@ -81,7 +75,6 @@ import org.apache.poi.util.POILogger;
* @author Shawn Laubach (slaubach at apache dot org)
* @version 2.0-pre
*/
-
public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.usermodel.Workbook
{
private static final int DEBUG = POILogger.DEBUG;
@@ -105,7 +98,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
* this holds the HSSFSheet objects attached to this workbook
*/
- protected ArrayList sheets;
+ protected List _sheets;
/**
* this holds the HSSFName objects attached to this workbook
@@ -159,7 +152,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
{
super(null, null);
workbook = book;
- sheets = new ArrayList( INITIAL_CAPACITY );
+ _sheets = new ArrayList( INITIAL_CAPACITY );
names = new ArrayList( INITIAL_CAPACITY );
}
@@ -250,7 +243,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
this.directory = null;
}
- sheets = new ArrayList(INITIAL_CAPACITY);
+ _sheets = new ArrayList(INITIAL_CAPACITY);
names = new ArrayList(INITIAL_CAPACITY);
// Grab the data from the workbook stream, however
@@ -280,7 +273,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
HSSFSheet hsheet = new HSSFSheet(this, sheet);
- sheets.add(hsheet);
+ _sheets.add(hsheet);
// workbook.setSheetName(sheets.size() -1, "Sheet"+sheets.size());
}
@@ -378,12 +371,12 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
*/
public void setSheetOrder(String sheetname, int pos ) {
- sheets.add(pos,sheets.remove(getSheetIndex(sheetname)));
+ _sheets.add(pos,_sheets.remove(getSheetIndex(sheetname)));
workbook.setSheetOrder(sheetname, pos);
}
private void validateSheetIndex(int index) {
- int lastSheetIx = sheets.size() - 1;
+ int lastSheetIx = _sheets.size() - 1;
if (index < 0 || index > lastSheetIx) {
throw new IllegalArgumentException("Sheet index ("
+ index +") is out of range (0.." + lastSheetIx + ")");
@@ -397,7 +390,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
public void setSelectedTab(int index) {
validateSheetIndex(index);
- int nSheets = sheets.size();
+ int nSheets = _sheets.size();
for (int i=0; i Note: This class is NOT in the default HashTables returned by HSSFColor.
* The index is a special case which is interpreted in the various setXXXColor calls.
- *
+ *
* @author Jason
*
*/
public final static class AUTOMATIC extends HSSFColor
{
- private static HSSFColor instance = new AUTOMATIC();
-
+ private static HSSFColor instance = new AUTOMATIC();
+
public final static short index = 0x40;
public short getIndex()
@@ -1725,7 +1675,7 @@ public class HSSFColor implements Color
{
return BLACK.hexString;
}
-
+
public static HSSFColor getInstance() {
return instance;
}
diff --git a/src/java/org/apache/poi/ss/usermodel/DateUtil.java b/src/java/org/apache/poi/ss/usermodel/DateUtil.java
index 9335de35d..0a9bdcfe8 100644
--- a/src/java/org/apache/poi/ss/usermodel/DateUtil.java
+++ b/src/java/org/apache/poi/ss/usermodel/DateUtil.java
@@ -226,7 +226,9 @@ public class DateUtil
// Otherwise, check it's only made up, in any case, of:
// y m d h s - / , . :
- if(fs.matches("^[yYmMdDhHsS\\-/,. :]+$")) {
+ // optionally followed by AM/PM
+ // optionally followed by AM/PM
+ if(fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP]*$")) {
return true;
}
diff --git a/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt
index 475131e1c..8a85f4284 100644
--- a/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt
+++ b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata-asGenerated.txt
@@ -14,7 +14,7 @@
# limitations under the License.
# Created by (org.apache.poi.hssf.record.formula.function.ExcelFileFormatDocFunctionExtractor)
-# from source file 'excelfileformat.odt' (size=355750, crc=0x2FAEA65A)
+# from source file 'excelfileformat.odt' (size=356107, md5=0x8f789cb6e75594caf068f8e193004ef4)
#
#Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote )
@@ -37,7 +37,7 @@
15 SIN 1 1 V V
16 COS 1 1 V V
17 TAN 1 1 V V
-18 ARCTAN 1 1 V V
+18 ATAN 1 1 V V
19 PI 0 0 V -
20 SQRT 1 1 V V
21 EXP 1 1 V V
@@ -141,8 +141,8 @@
169 COUNTA 0 30 V R
183 PRODUCT 0 30 V R
184 FACT 1 1 V V
-191 DPRODUCT 3 3 V R R R
-192 ISNONTEXT 1 1 V V
+189 DPRODUCT 3 3 V R R R
+190 ISNONTEXT 1 1 V V
193 STDEVP 1 30 V R
194 VARP 1 30 V R
195 DSTDEVP 3 3 V R R R
@@ -184,6 +184,8 @@
244 INFO 1 1 V V
# New Built-In Sheet Functions in BIFF4
14 FIXED 2 3 V V V V x
+204 USDOLLAR 1 2 V V V x
+215 DBCS 1 1 V V x
216 RANK 2 3 V V R V
247 DB 4 5 V V V V V V
252 FREQUENCY 2 2 A R R
diff --git a/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt
index 6902027de..8a85f4284 100644
--- a/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt
+++ b/src/resources/main/org/apache/poi/hssf/record/formula/function/functionMetadata.txt
@@ -14,11 +14,9 @@
# limitations under the License.
# Created by (org.apache.poi.hssf.record.formula.function.ExcelFileFormatDocFunctionExtractor)
-# from source file 'excelfileformat.odt' (size=355750, crc=0x2FAEA65A)
+# from source file 'excelfileformat.odt' (size=356107, md5=0x8f789cb6e75594caf068f8e193004ef4)
#
#Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote )
-#
-# + some manual edits !
# Built-In Sheet Functions in BIFF2
0 COUNT 0 30 V R
@@ -186,7 +184,7 @@
244 INFO 1 1 V V
# New Built-In Sheet Functions in BIFF4
14 FIXED 2 3 V V V V x
-204 USDOLLAR 1 1 V V x
+204 USDOLLAR 1 2 V V V x
215 DBCS 1 1 V V x
216 RANK 2 3 V V R V
247 DB 4 5 V V V V V V
diff --git a/src/scratchpad/src/org/apache/poi/hdgf/HDGFDiagram.java b/src/scratchpad/src/org/apache/poi/hdgf/HDGFDiagram.java
index af6616307..764b8e3f5 100644
--- a/src/scratchpad/src/org/apache/poi/hdgf/HDGFDiagram.java
+++ b/src/scratchpad/src/org/apache/poi/hdgf/HDGFDiagram.java
@@ -18,6 +18,7 @@ package org.apache.poi.hdgf;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.OutputStream;
import org.apache.poi.POIDocument;
import org.apache.poi.hdgf.chunks.ChunkFactory;
@@ -27,6 +28,7 @@ import org.apache.poi.hdgf.streams.PointerContainingStream;
import org.apache.poi.hdgf.streams.Stream;
import org.apache.poi.hdgf.streams.StringsStream;
import org.apache.poi.hdgf.streams.TrailerStream;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.LittleEndian;
@@ -53,14 +55,17 @@ public class HDGFDiagram extends POIDocument {
private PointerFactory ptrFactory;
public HDGFDiagram(POIFSFileSystem fs) throws IOException {
- super(fs);
+ this(fs.getRoot(), fs);
+ }
+ public HDGFDiagram(DirectoryNode dir, POIFSFileSystem fs) throws IOException {
+ super(dir, fs);
DocumentEntry docProps =
- (DocumentEntry)filesystem.getRoot().getEntry("VisioDocument");
+ (DocumentEntry)dir.getEntry("VisioDocument");
// Grab the document stream
_docstream = new byte[docProps.getSize()];
- filesystem.createDocumentInputStream("VisioDocument").read(_docstream);
+ dir.createDocumentInputStream("VisioDocument").read(_docstream);
// Read in the common POI streams
readProperties();
@@ -149,6 +154,10 @@ public class HDGFDiagram extends POIDocument {
}
}
+ public void write(OutputStream out) {
+ throw new IllegalStateException("Writing is not yet implemented, see http://poi.apache.org/hdgf/");
+ }
+
/**
* For testing only
*/
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/FSPATable.java b/src/scratchpad/src/org/apache/poi/hwpf/model/FSPATable.java
index 58c69ff4b..812ab1a4a 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/model/FSPATable.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/FSPATable.java
@@ -21,17 +21,18 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
/**
* This class holds all the FSPA (File Shape Address) structures.
*
* @author Squeeself
*/
-public class FSPATable
+public final class FSPATable
{
- protected ArrayList shapes = new ArrayList();
- protected HashMap cps = new HashMap();
- protected List _text;
+ private final List _shapes = new ArrayList();
+ private final Map _shapeIndexesByPropertyStart = new HashMap();
+ private final List _text;
public FSPATable(byte[] tableStream, int fcPlcspa, int lcbPlcspa, List tpt)
{
@@ -46,32 +47,35 @@ public class FSPATable
GenericPropertyNode property = plex.getProperty(i);
FSPA fspa = new FSPA(property.getBytes(), 0);
- shapes.add(fspa);
- cps.put(Integer.valueOf(property.getStart()), Integer.valueOf(i));
+ _shapes.add(fspa);
+ _shapeIndexesByPropertyStart.put(new Integer(property.getStart()), new Integer(i));
}
}
public FSPA getFspaFromCp(int cp)
{
- Integer idx = (Integer)cps.get(Integer.valueOf(cp));
- if (idx == null)
+ Integer idx = (Integer)_shapeIndexesByPropertyStart.get(new Integer(cp));
+ if (idx == null) {
return null;
- return (FSPA)shapes.get(idx.intValue());
+ }
+ return (FSPA)_shapes.get(idx.intValue());
}
- public List getShapes()
+ public FSPA[] getShapes()
{
- return shapes;
+ FSPA[] result = new FSPA[_shapes.size()];
+ _shapes.toArray(result);
+ return result;
}
public String toString()
{
StringBuffer buf = new StringBuffer();
- buf.append("[FPSA PLC size=").append(shapes.size()).append("]\n");
- for (Iterator it = cps.keySet().iterator(); it.hasNext(); )
+ buf.append("[FPSA PLC size=").append(_shapes.size()).append("]\n");
+ for (Iterator it = _shapeIndexesByPropertyStart.keySet().iterator(); it.hasNext(); )
{
Integer i = (Integer) it.next();
- FSPA fspa = (FSPA) shapes.get(((Integer)cps.get(i)).intValue());
+ FSPA fspa = (FSPA) _shapes.get(((Integer)_shapeIndexesByPropertyStart.get(i)).intValue());
buf.append(" [FC: ").append(i.toString()).append("] ");
buf.append(fspa.toString());
buf.append("\n");
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java b/src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java
index 48e6d78b3..887e13d82 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/FileInformationBlock.java
@@ -294,6 +294,16 @@ public class FileInformationBlock extends FIBAbstractType
_longHandler.setLong(FIBLongHandler.CBMAC, cbMac);
}
+ public int getCcpText()
+ {
+ return _longHandler.getLong(FIBLongHandler.CCPTEXT);
+ }
+
+ public void setCcpText(int ccpText)
+ {
+ _longHandler.setLong(FIBLongHandler.CCPTEXT, ccpText);
+ }
+
public void clearOffsetsSizes()
{
_fieldHandler.clearFields();
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/TextPiece.java b/src/scratchpad/src/org/apache/poi/hwpf/model/TextPiece.java
index 67c634d9f..bc33954df 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/model/TextPiece.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/TextPiece.java
@@ -90,12 +90,20 @@ public class TextPiece extends PropertyNode implements Comparable
public void adjustForDelete(int start, int length)
{
+
+ if (usesUnicode()) {
+
+ start /= 2;
+ length /= 2;
+ }
+
int myStart = getStart();
int myEnd = getEnd();
int end = start + length;
/* do we have to delete from this text piece? */
if (start <= myEnd && end >= myStart) {
+
/* find where the deleted area overlaps with this text piece */
int overlapStart = Math.max(myStart, start);
int overlapEnd = Math.min(myEnd, end);
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java
index f2d9a615f..85592a92a 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java
@@ -226,6 +226,25 @@ public class Range
}
}
+ /**
+ * Does any TextPiece
in this Range use unicode?
+ *
+ * @return true if it does and false if it doesn't
+ */
+ public boolean usesUnicode() {
+
+ initText();
+
+ for (int i = _textStart; i < _textEnd; i++)
+ {
+ TextPiece piece = (TextPiece)_text.get(i);
+ if (piece.usesUnicode())
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Gets the text that this Range contains.
*
@@ -306,13 +325,19 @@ public class Range
// Since this is the first item in our list, it is safe to assume that
// _start >= tp.getStart()
int insertIndex = _start - tp.getStart();
+ if (tp.usesUnicode())
+ insertIndex /= 2;
sb.insert(insertIndex, text);
+
int adjustedLength = _doc.getTextTable().adjustForInsert(_textStart, text.length());
_doc.getCharacterTable().adjustForInsert(_charStart, adjustedLength);
_doc.getParagraphTable().adjustForInsert(_parStart, adjustedLength);
_doc.getSectionTable().adjustForInsert(_sectionStart, adjustedLength);
adjustForInsert(text.length());
+ // update the FIB.CCPText field
+ adjustFIB(text.length());
+
return getCharacterRun(0);
}
@@ -489,6 +514,7 @@ public class Range
public void delete()
{
+
initAll();
int numSections = _sections.size();
@@ -519,6 +545,12 @@ public class Range
TextPiece piece = (TextPiece)_text.get(x);
piece.adjustForDelete(_start, _end - _start);
}
+
+ // update the FIB.CCPText field
+ if (usesUnicode())
+ adjustFIB(-((_end - _start) / 2));
+ else
+ adjustFIB(-(_end - _start));
}
/**
@@ -827,6 +859,19 @@ public class Range
_sectionRangeFound = false;
}
+ /**
+ * Adjust the value of FIB.CCPText
after an insert or a delete...
+ *
+ * @param adjustment The (signed) value that should be added to FIB.CCPText
+ */
+ protected void adjustFIB(int adjustment) {
+
+ // update the FIB.CCPText field (this should happen once per adjustment, so we don't want it in
+ // adjustForInsert() or it would get updated multiple times if the range has a parent)
+ // without this, OpenOffice.org (v. 2.2.x) does not see all the text in the document
+ _doc.getFileInformationBlock().setCcpText(_doc.getFileInformationBlock().getCcpText() + adjustment);
+ }
+
/**
* adjust this range after an insert happens.
* @param length the length to adjust for
@@ -834,6 +879,7 @@ public class Range
private void adjustForInsert(int length)
{
_end += length;
+
reset();
Range parent = (Range)_parent.get();
if (parent != null)
@@ -842,4 +888,14 @@ public class Range
}
}
+
+ public int getStartOffset() {
+
+ return _start;
+ }
+
+ public int getEndOffset() {
+
+ return _end;
+ }
}
diff --git a/src/testcases/org/apache/poi/hssf/data/ex45046-21984.xls b/src/testcases/org/apache/poi/hssf/data/ex45046-21984.xls
new file mode 100644
index 000000000..96ef3ee7c
Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/ex45046-21984.xls differ
diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
index 1895705a5..f2821140f 100644
--- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
+++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
@@ -55,120 +55,99 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* Test the low level formula parser functionality. High level tests are to
- * be done via usermodel/HSSFCell.setFormulaValue() .
- * Some tests are also done in scratchpad, if they need
- * HSSFFormulaEvaluator, which is there
+ * be done via usermodel/HSSFCell.setFormulaValue().
*/
public final class TestFormulaParser extends TestCase {
- /**
- * @return parsed token array already confirmed not null
- */
- private static Ptg[] parseFormula(String s) {
- // TODO - replace multiple copies of this code with calls to this method
- FormulaParser fp = new FormulaParser(s, null);
- fp.parse();
- Ptg[] result = fp.getRPNPtg();
- assertNotNull("Ptg array should not be null", result);
- return result;
- }
-
- public void testSimpleFormula() {
- FormulaParser fp = new FormulaParser("2+2",null);
- fp.parse();
- Ptg[] ptgs = fp.getRPNPtg();
- assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
- }
- public void testFormulaWithSpace1() {
- FormulaParser fp = new FormulaParser(" 2 + 2 ",null);
- fp.parse();
- Ptg[] ptgs = fp.getRPNPtg();
- assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
- assertTrue("",(ptgs[0] instanceof IntPtg));
- assertTrue("",(ptgs[1] instanceof IntPtg));
- assertTrue("",(ptgs[2] instanceof AddPtg));
- }
-
- public void testFormulaWithSpace2() {
- Ptg[] ptgs;
- FormulaParser fp;
- fp = new FormulaParser("2+ sum( 3 , 4) ",null);
- fp.parse();
- ptgs = fp.getRPNPtg();
- assertTrue("five tokens expected, got "+ptgs.length,ptgs.length == 5);
- }
-
- public void testFormulaWithSpaceNRef() {
- Ptg[] ptgs;
- FormulaParser fp;
- fp = new FormulaParser("sum( A2:A3 )",null);
- fp.parse();
- ptgs = fp.getRPNPtg();
- assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
- }
-
- public void testFormulaWithString() {
- Ptg[] ptgs;
- FormulaParser fp;
- fp = new FormulaParser("\"hello\" & \"world\" ",null);
- fp.parse();
- ptgs = fp.getRPNPtg();
- assertTrue("three token expected, got " + ptgs.length, ptgs.length == 3);
- }
-
- public void testTRUE() throws Exception {
- FormulaParser fp = new FormulaParser("TRUE", null);
- fp.parse();
- Ptg[] asts = fp.getRPNPtg();
- assertEquals(1, asts.length);
- BoolPtg flag = (BoolPtg) asts[0];
- assertEquals(true, flag.getValue());
- }
-
- public void testYN() throws Exception {
- final String yn = "IF(TRUE,\"Y\",\"N\")";
- FormulaParser fp = new FormulaParser(yn, null);
- fp.parse();
- Ptg[] asts = fp.getRPNPtg();
- assertEquals(7, asts.length);
-
- BoolPtg flag = (BoolPtg) asts[0];
- AttrPtg funif = (AttrPtg) asts[1];
- StringPtg y = (StringPtg) asts[2];
- AttrPtg goto1 = (AttrPtg) asts[3];
- StringPtg n = (StringPtg) asts[4];
-
-
- assertEquals(true, flag.getValue());
- assertEquals("Y", y.getValue());
- assertEquals("N", n.getValue());
- assertEquals("IF", funif.toFormulaString((HSSFWorkbook) null));
- assertTrue("Goto ptg exists", goto1.isGoto());
- }
-
- public void testSimpleIf() throws Exception {
- final String simpleif = "IF(1=1,0,1)";
- FormulaParser fp = new FormulaParser(simpleif, null);
+ /**
+ * @return parsed token array already confirmed not null
+ */
+ private static Ptg[] parseFormula(String s) {
+ FormulaParser fp = new FormulaParser(s, null);
fp.parse();
- Ptg[] asts = fp.getRPNPtg();
- assertEquals(9, asts.length);
+ Ptg[] result = fp.getRPNPtg();
+ assertNotNull("Ptg array should not be null", result);
+ return result;
+ }
+
+ public void testSimpleFormula() {
+ Ptg[] ptgs = parseFormula("2+2");
+ assertEquals(3, ptgs.length);
+ }
+
+ public void testFormulaWithSpace1() {
+ Ptg[] ptgs = parseFormula(" 2 + 2 ");
+ assertEquals(3, ptgs.length);
+ assertTrue("",(ptgs[0] instanceof IntPtg));
+ assertTrue("",(ptgs[1] instanceof IntPtg));
+ assertTrue("",(ptgs[2] instanceof AddPtg));
+ }
+
+ public void testFormulaWithSpace2() {
+ Ptg[] ptgs = parseFormula("2+ sum( 3 , 4) ");
+ assertEquals(5, ptgs.length);
+ }
+
+ public void testFormulaWithSpaceNRef() {
+ Ptg[] ptgs = parseFormula("sum( A2:A3 )");
+ assertEquals(2, ptgs.length);
+ }
+
+ public void testFormulaWithString() {
+ Ptg[] ptgs = parseFormula("\"hello\" & \"world\" ");
+ assertEquals(3, ptgs.length);
+ }
+
+ public void testTRUE() {
+ Ptg[] ptgs = parseFormula("TRUE");
+ assertEquals(1, ptgs.length);
+ BoolPtg flag = (BoolPtg) ptgs[0];
+ assertEquals(true, flag.getValue());
+ }
+
+ public void testYN() {
+ Ptg[] ptgs = parseFormula("IF(TRUE,\"Y\",\"N\")");
+ assertEquals(7, ptgs.length);
+
+ BoolPtg flag = (BoolPtg) ptgs[0];
+ AttrPtg funif = (AttrPtg) ptgs[1];
+ StringPtg y = (StringPtg) ptgs[2];
+ AttrPtg goto1 = (AttrPtg) ptgs[3];
+ StringPtg n = (StringPtg) ptgs[4];
+
+
+ assertEquals(true, flag.getValue());
+ assertEquals("Y", y.getValue());
+ assertEquals("N", n.getValue());
+ assertEquals("IF", funif.toFormulaString((HSSFWorkbook) null));
+ assertTrue("Goto ptg exists", goto1.isGoto());
+ }
+
+ public void testSimpleIf() {
+ String formula = "IF(1=1,0,1)";
+
+ Class[] expectedClasses = {
+ IntPtg.class,
+ IntPtg.class,
+ EqualPtg.class,
+ AttrPtg.class,
+ IntPtg.class,
+ AttrPtg.class,
+ IntPtg.class,
+ AttrPtg.class,
+ FuncVarPtg.class,
+ };
+ confirmTokenClasses(formula, expectedClasses);
- IntPtg op1 = (IntPtg) asts[0];
- IntPtg op2 = (IntPtg) asts[1];
- EqualPtg eq = (EqualPtg) asts[2];
- AttrPtg ifPtg = (AttrPtg) asts[3];
- IntPtg res1 = (IntPtg) asts[4];
-
- AttrPtg ptgGoto= (AttrPtg) asts[5];
- assertEquals("Goto 1 Length", (short)10, ptgGoto.getData());
-
- IntPtg res2 = (IntPtg) asts[6];
- AttrPtg ptgGoto2 = (AttrPtg) asts[7];
- assertEquals("Goto 2 Length", (short)3, ptgGoto2.getData());
-
- assertEquals("If FALSE offset", (short)7, ifPtg.getData());
-
- FuncVarPtg funcPtg = (FuncVarPtg)asts[8];
+ Ptg[] ptgs = parseFormula(formula);
+
+ AttrPtg ifPtg = (AttrPtg) ptgs[3];
+ AttrPtg ptgGoto= (AttrPtg) ptgs[5];
+ assertEquals("Goto 1 Length", 10, ptgGoto.getData());
+
+ AttrPtg ptgGoto2 = (AttrPtg) ptgs[7];
+ assertEquals("Goto 2 Length", 3, ptgGoto2.getData());
+ assertEquals("If FALSE offset", 7, ifPtg.getData());
}
/**
@@ -176,753 +155,714 @@ public final class TestFormulaParser extends TestCase {
*
*/
public void testNestedFunctionIf() {
- String function = "IF(A1=B1,AVERAGE(A1:B1),AVERAGE(A2:B2))";
+ Ptg[] ptgs = parseFormula("IF(A1=B1,AVERAGE(A1:B1),AVERAGE(A2:B2))");
+ assertEquals(11, ptgs.length);
- FormulaParser fp = new FormulaParser(function, null);
- fp.parse();
- Ptg[] asts = fp.getRPNPtg();
- assertEquals("11 Ptgs expected", 11, asts.length);
-
- assertTrue("IF Attr set correctly", (asts[3] instanceof AttrPtg));
- AttrPtg ifFunc = (AttrPtg)asts[3];
+ assertTrue("IF Attr set correctly", (ptgs[3] instanceof AttrPtg));
+ AttrPtg ifFunc = (AttrPtg)ptgs[3];
assertTrue("It is not an if", ifFunc.isOptimizedIf());
-
- assertTrue("Average Function set correctly", (asts[5] instanceof FuncVarPtg));
+
+ assertTrue("Average Function set correctly", (ptgs[5] instanceof FuncVarPtg));
}
-
+
public void testIfSingleCondition(){
- String function = "IF(1=1,10)";
+ Ptg[] ptgs = parseFormula("IF(1=1,10)");
+ assertEquals(7, ptgs.length);
- FormulaParser fp = new FormulaParser(function, null);
- fp.parse();
- Ptg[] asts = fp.getRPNPtg();
- assertEquals("7 Ptgs expected", 7, asts.length);
-
- assertTrue("IF Attr set correctly", (asts[3] instanceof AttrPtg));
- AttrPtg ifFunc = (AttrPtg)asts[3];
+ assertTrue("IF Attr set correctly", (ptgs[3] instanceof AttrPtg));
+ AttrPtg ifFunc = (AttrPtg)ptgs[3];
assertTrue("It is not an if", ifFunc.isOptimizedIf());
-
- assertTrue("Single Value is not an IntPtg", (asts[4] instanceof IntPtg));
- IntPtg intPtg = (IntPtg)asts[4];
+
+ assertTrue("Single Value is not an IntPtg", (ptgs[4] instanceof IntPtg));
+ IntPtg intPtg = (IntPtg)ptgs[4];
assertEquals("Result", (short)10, intPtg.getValue());
-
- assertTrue("Ptg is not a Variable Function", (asts[6] instanceof FuncVarPtg));
- FuncVarPtg funcPtg = (FuncVarPtg)asts[6];
+
+ assertTrue("Ptg is not a Variable Function", (ptgs[6] instanceof FuncVarPtg));
+ FuncVarPtg funcPtg = (FuncVarPtg)ptgs[6];
assertEquals("Arguments", 2, funcPtg.getNumberOfOperands());
}
public void testSumIf() {
- String function ="SUMIF(A1:A5,\">4000\",B1:B5)";
- FormulaParser fp = new FormulaParser(function, null);
- fp.parse();
- Ptg[] asts = fp.getRPNPtg();
- assertEquals("4 Ptgs expected", 4, asts.length);
+ Ptg[] ptgs = parseFormula("SUMIF(A1:A5,\">4000\",B1:B5)");
+ assertEquals(4, ptgs.length);
}
-
+
/**
* Bug Reported by xt-jens.riis@nokia.com (Jens Riis)
* Refers to Bug #17582
*
*/
- public void testNonAlphaFormula(){
+ public void testNonAlphaFormula() {
String currencyCell = "F3";
- String function="\"TOTAL[\"&"+currencyCell+"&\"]\"";
+ Ptg[] ptgs = parseFormula("\"TOTAL[\"&"+currencyCell+"&\"]\"");
+ assertEquals(5, ptgs.length);
+ assertTrue ("Ptg[0] is a string", (ptgs[0] instanceof StringPtg));
+ StringPtg firstString = (StringPtg)ptgs[0];
- Ptg[] asts = parseFormula(function);
- assertEquals("5 ptgs expected", 5, asts.length);
- assertTrue ("Ptg[0] is a string", (asts[0] instanceof StringPtg));
- StringPtg firstString = (StringPtg)asts[0];
-
assertEquals("TOTAL[", firstString.getValue());
//the PTG order isn't 100% correct but it still works - dmui
}
public void testSimpleLogical() {
- Ptg[] ptgs = parseFormula("IF(A1
- * a formula consisting of a single no-arg function got rendered without the function braces
- */
- public void testToFormulaStringZeroArgFunction() {
- HSSFWorkbook book = new HSSFWorkbook();
-
- Ptg[] ptgs = {
- new FuncPtg(10),
- };
- assertEquals("NA()", FormulaParser.toFormulaString(book, ptgs));
- }
-
- public void testPercent() {
- Ptg[] ptgs;
- ptgs = parseFormula("5%");
- assertEquals(2, ptgs.length);
- assertEquals(ptgs[0].getClass(), IntPtg.class);
- assertEquals(ptgs[1].getClass(), PercentPtg.class);
-
- // spaces OK
- ptgs = parseFormula(" 250 % ");
- assertEquals(2, ptgs.length);
- assertEquals(ptgs[0].getClass(), IntPtg.class);
- assertEquals(ptgs[1].getClass(), PercentPtg.class);
-
-
- // double percent OK
- ptgs = parseFormula("12345.678%%");
- assertEquals(3, ptgs.length);
- assertEquals(ptgs[0].getClass(), NumberPtg.class);
- assertEquals(ptgs[1].getClass(), PercentPtg.class);
- assertEquals(ptgs[2].getClass(), PercentPtg.class);
-
- // percent of a bracketed expression
- ptgs = parseFormula("(A1+35)%*B1%");
- assertEquals(8, ptgs.length);
- assertEquals(ptgs[4].getClass(), PercentPtg.class);
- assertEquals(ptgs[6].getClass(), PercentPtg.class);
-
- // percent of a text quantity
- ptgs = parseFormula("\"8.75\"%");
- assertEquals(2, ptgs.length);
- assertEquals(ptgs[0].getClass(), StringPtg.class);
- assertEquals(ptgs[1].getClass(), PercentPtg.class);
-
- // percent to the power of
- ptgs = parseFormula("50%^3");
- assertEquals(4, ptgs.length);
- assertEquals(ptgs[0].getClass(), IntPtg.class);
- assertEquals(ptgs[1].getClass(), PercentPtg.class);
- assertEquals(ptgs[2].getClass(), IntPtg.class);
- assertEquals(ptgs[3].getClass(), PowerPtg.class);
-
- //
- // things that parse OK but would *evaluate* to an error
-
- ptgs = parseFormula("\"abc\"%");
- assertEquals(2, ptgs.length);
- assertEquals(ptgs[0].getClass(), StringPtg.class);
- assertEquals(ptgs[1].getClass(), PercentPtg.class);
-
- ptgs = parseFormula("#N/A%");
- assertEquals(2, ptgs.length);
- assertEquals(ptgs[0].getClass(), ErrPtg.class);
- assertEquals(ptgs[1].getClass(), PercentPtg.class);
- }
-
- /**
- * Tests combinations of various operators in the absence of brackets
- */
- public void testPrecedenceAndAssociativity() {
-
- Class[] expClss;
-
- // TRUE=TRUE=2=2 evaluates to FALSE
- expClss = new Class[] { BoolPtg.class, BoolPtg.class, EqualPtg.class,
- IntPtg.class, EqualPtg.class, IntPtg.class, EqualPtg.class, };
- confirmTokenClasses("TRUE=TRUE=2=2", expClss);
-
-
- // 2^3^2 evaluates to 64 not 512
- expClss = new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class,
- IntPtg.class, PowerPtg.class, };
- confirmTokenClasses("2^3^2", expClss);
-
- // "abc" & 2 + 3 & "def" evaluates to "abc5def"
- expClss = new Class[] { StringPtg.class, IntPtg.class, IntPtg.class,
- AddPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class, };
- confirmTokenClasses("\"abc\"&2+3&\"def\"", expClss);
-
-
- // (1 / 2) - (3 * 4)
- expClss = new Class[] { IntPtg.class, IntPtg.class, DividePtg.class,
- IntPtg.class, IntPtg.class, MultiplyPtg.class, SubtractPtg.class, };
- confirmTokenClasses("1/2-3*4", expClss);
-
- // 2 * (2^2)
- expClss = new Class[] { IntPtg.class, IntPtg.class, IntPtg.class, PowerPtg.class, MultiplyPtg.class, };
- // NOT: (2 *2) ^ 2 -> int int multiply int power
- confirmTokenClasses("2*2^2", expClss);
-
- // 2^200% -> 2 not 1.6E58
- expClss = new Class[] { IntPtg.class, IntPtg.class, PercentPtg.class, PowerPtg.class, };
- confirmTokenClasses("2^200%", expClss);
- }
-
- private static void confirmTokenClasses(String formula, Class[] expectedClasses) {
- Ptg[] ptgs = parseFormula(formula);
- assertEquals(expectedClasses.length, ptgs.length);
- for (int i = 0; i < expectedClasses.length; i++) {
- if(expectedClasses[i] != ptgs[i].getClass()) {
- fail("difference at token[" + i + "]: expected ("
- + expectedClasses[i].getName() + ") but got ("
- + ptgs[i].getClass().getName() + ")");
- }
- }
- }
-
- public void testPower() {
- confirmTokenClasses("2^5", new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class, });
- }
-
- private static Ptg parseSingleToken(String formula, Class ptgClass) {
- Ptg[] ptgs = parseFormula(formula);
- assertEquals(1, ptgs.length);
- Ptg result = ptgs[0];
- assertEquals(ptgClass, result.getClass());
- return result;
- }
-
- public void testParseNumber() {
- IntPtg ip;
-
- // bug 33160
- ip = (IntPtg) parseSingleToken("40", IntPtg.class);
- assertEquals(40, ip.getValue());
- ip = (IntPtg) parseSingleToken("40000", IntPtg.class);
- assertEquals(40000, ip.getValue());
-
- // check the upper edge of the IntPtg range:
- ip = (IntPtg) parseSingleToken("65535", IntPtg.class);
- assertEquals(65535, ip.getValue());
- NumberPtg np = (NumberPtg) parseSingleToken("65536", NumberPtg.class);
- assertEquals(65536, np.getValue(), 0);
-
- np = (NumberPtg) parseSingleToken("65534.6", NumberPtg.class);
- assertEquals(65534.6, np.getValue(), 0);
- }
-
- public void testMissingArgs() {
-
- Class[] expClss;
-
- expClss = new Class[] { ReferencePtg.class, MissingArgPtg.class, ReferencePtg.class,
- FuncVarPtg.class, };
- confirmTokenClasses("if(A1, ,C1)", expClss);
-
- expClss = new Class[] { MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class,
- FuncVarPtg.class, };
- confirmTokenClasses("counta( , A1:B2, )", expClss);
- }
-
- public void testParseErrorLiterals() {
-
- confirmParseErrorLiteral(ErrPtg.NULL_INTERSECTION, "#NULL!");
- confirmParseErrorLiteral(ErrPtg.DIV_ZERO, "#DIV/0!");
- confirmParseErrorLiteral(ErrPtg.VALUE_INVALID, "#VALUE!");
- confirmParseErrorLiteral(ErrPtg.REF_INVALID, "#REF!");
- confirmParseErrorLiteral(ErrPtg.NAME_INVALID, "#NAME?");
- confirmParseErrorLiteral(ErrPtg.NUM_ERROR, "#NUM!");
- confirmParseErrorLiteral(ErrPtg.N_A, "#N/A");
- }
-
- private static void confirmParseErrorLiteral(ErrPtg expectedToken, String formula) {
- assertEquals(expectedToken, parseSingleToken(formula, ErrPtg.class));
- }
-
- /**
- * To aid readability the parameters have been encoded with single quotes instead of double
- * quotes. This method converts single quotes to double quotes before performing the parse
- * and result check.
- */
- private static void confirmStringParse(String singleQuotedValue) {
- // formula: internal quotes become double double, surround with double quotes
- String formula = '"' + singleQuotedValue.replaceAll("'", "\"\"") + '"';
- String expectedValue = singleQuotedValue.replace('\'', '"');
-
- StringPtg sp = (StringPtg) parseSingleToken(formula, StringPtg.class);
- assertEquals(expectedValue, sp.getValue());
- }
- public void testParseStringLiterals_bug28754() {
-
- StringPtg sp;
- try {
- sp = (StringPtg) parseSingleToken("\"test\"\"ing\"", StringPtg.class);
- } catch (RuntimeException e) {
- if(e.getMessage().startsWith("Cannot Parse")) {
- throw new AssertionFailedError("Identified bug 28754a");
- }
- throw e;
- }
- assertEquals("test\"ing", sp.getValue());
-
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet sheet = wb.createSheet();
- wb.setSheetName(0, "Sheet1");
-
- HSSFRow row = sheet.createRow(0);
- HSSFCell cell = row.createCell((short)0);
- cell.setCellFormula("right(\"test\"\"ing\", 3)");
- String actualCellFormula = cell.getCellFormula();
- if("RIGHT(\"test\"ing\",3)".equals(actualCellFormula)) {
- throw new AssertionFailedError("Identified bug 28754b");
- }
- assertEquals("RIGHT(\"test\"\"ing\",3)", actualCellFormula);
- }
-
- public void testParseStringLiterals() {
- confirmStringParse("goto considered harmful");
-
- confirmStringParse("goto 'considered' harmful");
-
- confirmStringParse("");
- confirmStringParse("'");
- confirmStringParse("''");
- confirmStringParse("' '");
- confirmStringParse(" ' ");
- }
-
- public void testParseSumIfSum() {
- String formulaString;
- Ptg[] ptgs;
- ptgs = parseFormula("sum(5, 2, if(3>2, sum(A1:A2), 6))");
- formulaString = FormulaParser.toFormulaString(null, ptgs);
- assertEquals("SUM(5,2,IF(3>2,SUM(A1:A2),6))", formulaString);
-
- ptgs = parseFormula("if(1<2,sum(5, 2, if(3>2, sum(A1:A2), 6)),4)");
- formulaString = FormulaParser.toFormulaString(null, ptgs);
- assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString);
- }
- public void testParserErrors() {
- parseExpectedException("1 2");
- parseExpectedException(" 12 . 345 ");
- parseExpectedException("1 .23 ");
-
- parseExpectedException("sum(#NAME)");
- parseExpectedException("1 + #N / A * 2");
- parseExpectedException("#value?");
- parseExpectedException("#DIV/ 0+2");
-
-
- parseExpectedException("IF(TRUE)");
- parseExpectedException("countif(A1:B5, C1, D1)");
- }
-
- private static void parseExpectedException(String formula) {
- try {
- parseFormula(formula);
- throw new AssertionFailedError("expected parse exception");
- } catch (FormulaParseException e) {
- // expected during successful test
- assertNotNull(e.getMessage());
- } catch (RuntimeException e) {
- e.printStackTrace();
- fail("Wrong exception:" + e.getMessage());
- }
- }
-
- public void testSetFormulaWithRowBeyond32768_Bug44539() {
-
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet sheet = wb.createSheet();
- wb.setSheetName(0, "Sheet1");
-
- HSSFRow row = sheet.createRow(0);
- HSSFCell cell = row.createCell((short)0);
- cell.setCellFormula("SUM(A32769:A32770)");
- if("SUM(A-32767:A-32766)".equals(cell.getCellFormula())) {
- fail("Identified bug 44539");
- }
- assertEquals("SUM(A32769:A32770)", cell.getCellFormula());
- }
-
- public void testSpaceAtStartOfFormula() {
- // Simulating cell formula of "= 4" (note space)
- // The same Ptg array can be observed if an excel file is saved with that exact formula
-
- AttrPtg spacePtg = AttrPtg.createSpace(AttrPtg.SpaceType.SPACE_BEFORE, 1);
- Ptg[] ptgs = { spacePtg, new IntPtg(4), };
- String formulaString;
- try {
- formulaString = FormulaParser.toFormulaString(null, ptgs);
- } catch (IllegalStateException e) {
- if(e.getMessage().equalsIgnoreCase("too much stuff left on the stack")) {
- throw new AssertionFailedError("Identified bug 44609");
- }
- // else some unexpected error
- throw e;
- }
- // FormulaParser strips spaces anyway
- assertEquals("4", formulaString);
-
- ptgs = new Ptg[] { new IntPtg(3), spacePtg, new IntPtg(4), spacePtg, new AddPtg()};
- formulaString = FormulaParser.toFormulaString(null, ptgs);
- assertEquals("3+4", formulaString);
- }
-
- /**
- * Checks some internal error detecting logic ('stack underflow error' in toFormulaString)
- */
- public void testTooFewOperandArgs() {
- // Simulating badly encoded cell formula of "=/1"
- // Not sure if Excel could ever produce this
- Ptg[] ptgs = {
- // Excel would probably have put tMissArg here
- new IntPtg(1),
- new DividePtg(),
- };
- try {
- FormulaParser.toFormulaString(null, ptgs);
- fail("Expected exception was not thrown");
- } catch (IllegalStateException e) {
- // expected during successful test
- assertTrue(e.getMessage().startsWith("Too few arguments suppled to operation token"));
- }
- }
- /**
- * Make sure that POI uses the right Func Ptg when encoding formulas. Functions with variable
- * number of args should get FuncVarPtg, functions with fixed args should get FuncPtg.
- *
- * Prior to the fix for bug 44675 POI would encode FuncVarPtg for all functions. In many cases
- * Excel tolerates the wrong Ptg and evaluates the formula OK (e.g. SIN), but in some cases
- * (e.g. COUNTIF) Excel fails to evaluate the formula, giving '#VALUE!' instead.
- */
- public void testFuncPtgSelection() {
- HSSFWorkbook book = new HSSFWorkbook();
- Ptg[] ptgs;
- ptgs = FormulaParser.parse("countif(A1:A2, 1)", book);
- assertEquals(3, ptgs.length);
- if(FuncVarPtg.class == ptgs[2].getClass()) {
- throw new AssertionFailedError("Identified bug 44675");
- }
- assertEquals(FuncPtg.class, ptgs[2].getClass());
- ptgs = FormulaParser.parse("sin(1)", book);
- assertEquals(2, ptgs.length);
- assertEquals(FuncPtg.class, ptgs[1].getClass());
- }
-
- public void testWrongNumberOfFunctionArgs() {
- confirmArgCountMsg("sin()", "Too few arguments to function 'SIN'. Expected 1 but got 0.");
- confirmArgCountMsg("countif(1, 2, 3, 4)", "Too many arguments to function 'COUNTIF'. Expected 2 but got 4.");
- confirmArgCountMsg("index(1, 2, 3, 4, 5, 6)", "Too many arguments to function 'INDEX'. At most 4 were expected but got 6.");
- confirmArgCountMsg("vlookup(1, 2)", "Too few arguments to function 'VLOOKUP'. At least 3 were expected but got 2.");
- }
-
- private static void confirmArgCountMsg(String formula, String expectedMessage) {
- HSSFWorkbook book = new HSSFWorkbook();
- try {
- FormulaParser.parse(formula, book);
- throw new AssertionFailedError("Didn't get parse exception as expected");
- } catch (FormulaParseException e) {
- assertEquals(expectedMessage, e.getMessage());
- }
- }
+ // bug 38396 : Formula with exponential numbers not parsed correctly.
+ public void testExponentialParsing() {
+ Ptg[] ptgs;
+ ptgs = parseFormula("1.3E21/2");
+ assertEquals(3, ptgs.length);
+ assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
+ assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
+ assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
+
+ ptgs = parseFormula("1322E21/2");
+ assertEquals(3, ptgs.length);
+ assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
+ assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
+ assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
+
+ ptgs = parseFormula("1.3E1/2");
+ assertEquals(3, ptgs.length);
+ assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
+ assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
+ assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
+ }
+
+ public void testExponentialInSheet() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+
+ wb.createSheet("Cash_Flow");
+
+ HSSFSheet sheet = wb.createSheet("Test");
+ HSSFRow row = sheet.createRow(0);
+ HSSFCell cell = row.createCell((short)0);
+ String formula = null;
+
+ cell.setCellFormula("1.3E21/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "1.3E21/3", formula);
+
+ cell.setCellFormula("-1.3E21/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "-1.3E21/3", formula);
+
+ cell.setCellFormula("1322E21/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "1.322E24/3", formula);
+
+ cell.setCellFormula("-1322E21/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "-1.322E24/3", formula);
+
+ cell.setCellFormula("1.3E1/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "13.0/3", formula);
+
+ cell.setCellFormula("-1.3E1/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "-13.0/3", formula);
+
+ cell.setCellFormula("1.3E-4/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "1.3E-4/3", formula);
+
+ cell.setCellFormula("-1.3E-4/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "-1.3E-4/3", formula);
+
+ cell.setCellFormula("13E-15/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "1.3E-14/3", formula);
+
+ cell.setCellFormula("-13E-15/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "-1.3E-14/3", formula);
+
+ cell.setCellFormula("1.3E3/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "1300.0/3", formula);
+
+ cell.setCellFormula("-1.3E3/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "-1300.0/3", formula);
+
+ cell.setCellFormula("1300000000000000/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "1.3E15/3", formula);
+
+ cell.setCellFormula("-1300000000000000/3");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "-1.3E15/3", formula);
+
+ cell.setCellFormula("-10E-1/3.1E2*4E3/3E4");
+ formula = cell.getCellFormula();
+ assertEquals("Exponential formula string", "-1.0/310.0*4000.0/30000.0", formula);
+ }
+
+ public void testNumbers() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+
+ wb.createSheet("Cash_Flow");
+
+ HSSFSheet sheet = wb.createSheet("Test");
+ HSSFRow row = sheet.createRow(0);
+ HSSFCell cell = row.createCell((short)0);
+ String formula = null;
+
+ // starts from decimal point
+
+ cell.setCellFormula(".1");
+ formula = cell.getCellFormula();
+ assertEquals("0.1", formula);
+
+ cell.setCellFormula("+.1");
+ formula = cell.getCellFormula();
+ assertEquals("+0.1", formula);
+
+ cell.setCellFormula("-.1");
+ formula = cell.getCellFormula();
+ assertEquals("-0.1", formula);
+
+ // has exponent
+
+ cell.setCellFormula("10E1");
+ formula = cell.getCellFormula();
+ assertEquals("100.0", formula);
+
+ cell.setCellFormula("10E+1");
+ formula = cell.getCellFormula();
+ assertEquals("100.0", formula);
+
+ cell.setCellFormula("10E-1");
+ formula = cell.getCellFormula();
+ assertEquals("1.0", formula);
+ }
+
+ public void testRanges() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+
+ wb.createSheet("Cash_Flow");
+
+ HSSFSheet sheet = wb.createSheet("Test");
+ HSSFRow row = sheet.createRow(0);
+ HSSFCell cell = row.createCell((short)0);
+ String formula = null;
+
+ cell.setCellFormula("A1.A2");
+ formula = cell.getCellFormula();
+ assertEquals("A1:A2", formula);
+
+ cell.setCellFormula("A1..A2");
+ formula = cell.getCellFormula();
+ assertEquals("A1:A2", formula);
+
+ cell.setCellFormula("A1...A2");
+ formula = cell.getCellFormula();
+ assertEquals("A1:A2", formula);
+ }
+
+ /**
+ * Test for bug observable at svn revision 618865 (5-Feb-2008)
+ * a formula consisting of a single no-arg function got rendered without the function braces
+ */
+ public void testToFormulaStringZeroArgFunction() {
+ HSSFWorkbook book = new HSSFWorkbook();
+
+ Ptg[] ptgs = {
+ new FuncPtg(10),
+ };
+ assertEquals("NA()", FormulaParser.toFormulaString(book, ptgs));
+ }
+
+ public void testPercent() {
+ Ptg[] ptgs;
+ ptgs = parseFormula("5%");
+ assertEquals(2, ptgs.length);
+ assertEquals(ptgs[0].getClass(), IntPtg.class);
+ assertEquals(ptgs[1].getClass(), PercentPtg.class);
+
+ // spaces OK
+ ptgs = parseFormula(" 250 % ");
+ assertEquals(2, ptgs.length);
+ assertEquals(ptgs[0].getClass(), IntPtg.class);
+ assertEquals(ptgs[1].getClass(), PercentPtg.class);
+
+
+ // double percent OK
+ ptgs = parseFormula("12345.678%%");
+ assertEquals(3, ptgs.length);
+ assertEquals(ptgs[0].getClass(), NumberPtg.class);
+ assertEquals(ptgs[1].getClass(), PercentPtg.class);
+ assertEquals(ptgs[2].getClass(), PercentPtg.class);
+
+ // percent of a bracketed expression
+ ptgs = parseFormula("(A1+35)%*B1%");
+ assertEquals(8, ptgs.length);
+ assertEquals(ptgs[4].getClass(), PercentPtg.class);
+ assertEquals(ptgs[6].getClass(), PercentPtg.class);
+
+ // percent of a text quantity
+ ptgs = parseFormula("\"8.75\"%");
+ assertEquals(2, ptgs.length);
+ assertEquals(ptgs[0].getClass(), StringPtg.class);
+ assertEquals(ptgs[1].getClass(), PercentPtg.class);
+
+ // percent to the power of
+ ptgs = parseFormula("50%^3");
+ assertEquals(4, ptgs.length);
+ assertEquals(ptgs[0].getClass(), IntPtg.class);
+ assertEquals(ptgs[1].getClass(), PercentPtg.class);
+ assertEquals(ptgs[2].getClass(), IntPtg.class);
+ assertEquals(ptgs[3].getClass(), PowerPtg.class);
+
+ //
+ // things that parse OK but would *evaluate* to an error
+
+ ptgs = parseFormula("\"abc\"%");
+ assertEquals(2, ptgs.length);
+ assertEquals(ptgs[0].getClass(), StringPtg.class);
+ assertEquals(ptgs[1].getClass(), PercentPtg.class);
+
+ ptgs = parseFormula("#N/A%");
+ assertEquals(2, ptgs.length);
+ assertEquals(ptgs[0].getClass(), ErrPtg.class);
+ assertEquals(ptgs[1].getClass(), PercentPtg.class);
+ }
+
+ /**
+ * Tests combinations of various operators in the absence of brackets
+ */
+ public void testPrecedenceAndAssociativity() {
+
+ Class[] expClss;
+
+ // TRUE=TRUE=2=2 evaluates to FALSE
+ expClss = new Class[] { BoolPtg.class, BoolPtg.class, EqualPtg.class,
+ IntPtg.class, EqualPtg.class, IntPtg.class, EqualPtg.class, };
+ confirmTokenClasses("TRUE=TRUE=2=2", expClss);
+
+
+ // 2^3^2 evaluates to 64 not 512
+ expClss = new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class,
+ IntPtg.class, PowerPtg.class, };
+ confirmTokenClasses("2^3^2", expClss);
+
+ // "abc" & 2 + 3 & "def" evaluates to "abc5def"
+ expClss = new Class[] { StringPtg.class, IntPtg.class, IntPtg.class,
+ AddPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class, };
+ confirmTokenClasses("\"abc\"&2+3&\"def\"", expClss);
+
+
+ // (1 / 2) - (3 * 4)
+ expClss = new Class[] { IntPtg.class, IntPtg.class, DividePtg.class,
+ IntPtg.class, IntPtg.class, MultiplyPtg.class, SubtractPtg.class, };
+ confirmTokenClasses("1/2-3*4", expClss);
+
+ // 2 * (2^2)
+ expClss = new Class[] { IntPtg.class, IntPtg.class, IntPtg.class, PowerPtg.class, MultiplyPtg.class, };
+ // NOT: (2 *2) ^ 2 -> int int multiply int power
+ confirmTokenClasses("2*2^2", expClss);
+
+ // 2^200% -> 2 not 1.6E58
+ expClss = new Class[] { IntPtg.class, IntPtg.class, PercentPtg.class, PowerPtg.class, };
+ confirmTokenClasses("2^200%", expClss);
+ }
+
+ private static void confirmTokenClasses(String formula, Class[] expectedClasses) {
+ Ptg[] ptgs = parseFormula(formula);
+ assertEquals(expectedClasses.length, ptgs.length);
+ for (int i = 0; i < expectedClasses.length; i++) {
+ if(expectedClasses[i] != ptgs[i].getClass()) {
+ fail("difference at token[" + i + "]: expected ("
+ + expectedClasses[i].getName() + ") but got ("
+ + ptgs[i].getClass().getName() + ")");
+ }
+ }
+ }
+
+ public void testPower() {
+ confirmTokenClasses("2^5", new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class, });
+ }
+
+ private static Ptg parseSingleToken(String formula, Class ptgClass) {
+ Ptg[] ptgs = parseFormula(formula);
+ assertEquals(1, ptgs.length);
+ Ptg result = ptgs[0];
+ assertEquals(ptgClass, result.getClass());
+ return result;
+ }
+
+ public void testParseNumber() {
+ IntPtg ip;
+
+ // bug 33160
+ ip = (IntPtg) parseSingleToken("40", IntPtg.class);
+ assertEquals(40, ip.getValue());
+ ip = (IntPtg) parseSingleToken("40000", IntPtg.class);
+ assertEquals(40000, ip.getValue());
+
+ // check the upper edge of the IntPtg range:
+ ip = (IntPtg) parseSingleToken("65535", IntPtg.class);
+ assertEquals(65535, ip.getValue());
+ NumberPtg np = (NumberPtg) parseSingleToken("65536", NumberPtg.class);
+ assertEquals(65536, np.getValue(), 0);
+
+ np = (NumberPtg) parseSingleToken("65534.6", NumberPtg.class);
+ assertEquals(65534.6, np.getValue(), 0);
+ }
+
+ public void testMissingArgs() {
+
+ Class[] expClss;
+
+ expClss = new Class[] { ReferencePtg.class, MissingArgPtg.class, ReferencePtg.class,
+ FuncVarPtg.class, };
+ confirmTokenClasses("if(A1, ,C1)", expClss);
+
+ expClss = new Class[] { MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class,
+ FuncVarPtg.class, };
+ confirmTokenClasses("counta( , A1:B2, )", expClss);
+ }
+
+ public void testParseErrorLiterals() {
+
+ confirmParseErrorLiteral(ErrPtg.NULL_INTERSECTION, "#NULL!");
+ confirmParseErrorLiteral(ErrPtg.DIV_ZERO, "#DIV/0!");
+ confirmParseErrorLiteral(ErrPtg.VALUE_INVALID, "#VALUE!");
+ confirmParseErrorLiteral(ErrPtg.REF_INVALID, "#REF!");
+ confirmParseErrorLiteral(ErrPtg.NAME_INVALID, "#NAME?");
+ confirmParseErrorLiteral(ErrPtg.NUM_ERROR, "#NUM!");
+ confirmParseErrorLiteral(ErrPtg.N_A, "#N/A");
+ }
+
+ private static void confirmParseErrorLiteral(ErrPtg expectedToken, String formula) {
+ assertEquals(expectedToken, parseSingleToken(formula, ErrPtg.class));
+ }
+
+ /**
+ * To aid readability the parameters have been encoded with single quotes instead of double
+ * quotes. This method converts single quotes to double quotes before performing the parse
+ * and result check.
+ */
+ private static void confirmStringParse(String singleQuotedValue) {
+ // formula: internal quotes become double double, surround with double quotes
+ String formula = '"' + singleQuotedValue.replaceAll("'", "\"\"") + '"';
+ String expectedValue = singleQuotedValue.replace('\'', '"');
+
+ StringPtg sp = (StringPtg) parseSingleToken(formula, StringPtg.class);
+ assertEquals(expectedValue, sp.getValue());
+ }
+ public void testParseStringLiterals_bug28754() {
+
+ StringPtg sp;
+ try {
+ sp = (StringPtg) parseSingleToken("\"test\"\"ing\"", StringPtg.class);
+ } catch (RuntimeException e) {
+ if(e.getMessage().startsWith("Cannot Parse")) {
+ throw new AssertionFailedError("Identified bug 28754a");
+ }
+ throw e;
+ }
+ assertEquals("test\"ing", sp.getValue());
+
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet();
+ wb.setSheetName(0, "Sheet1");
+
+ HSSFRow row = sheet.createRow(0);
+ HSSFCell cell = row.createCell((short)0);
+ cell.setCellFormula("right(\"test\"\"ing\", 3)");
+ String actualCellFormula = cell.getCellFormula();
+ if("RIGHT(\"test\"ing\",3)".equals(actualCellFormula)) {
+ throw new AssertionFailedError("Identified bug 28754b");
+ }
+ assertEquals("RIGHT(\"test\"\"ing\",3)", actualCellFormula);
+ }
+
+ public void testParseStringLiterals() {
+ confirmStringParse("goto considered harmful");
+
+ confirmStringParse("goto 'considered' harmful");
+
+ confirmStringParse("");
+ confirmStringParse("'");
+ confirmStringParse("''");
+ confirmStringParse("' '");
+ confirmStringParse(" ' ");
+ }
+
+ public void testParseSumIfSum() {
+ String formulaString;
+ Ptg[] ptgs;
+ ptgs = parseFormula("sum(5, 2, if(3>2, sum(A1:A2), 6))");
+ formulaString = FormulaParser.toFormulaString(null, ptgs);
+ assertEquals("SUM(5,2,IF(3>2,SUM(A1:A2),6))", formulaString);
+
+ ptgs = parseFormula("if(1<2,sum(5, 2, if(3>2, sum(A1:A2), 6)),4)");
+ formulaString = FormulaParser.toFormulaString(null, ptgs);
+ assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString);
+ }
+ public void testParserErrors() {
+ parseExpectedException("1 2");
+ parseExpectedException(" 12 . 345 ");
+ parseExpectedException("1 .23 ");
+
+ parseExpectedException("sum(#NAME)");
+ parseExpectedException("1 + #N / A * 2");
+ parseExpectedException("#value?");
+ parseExpectedException("#DIV/ 0+2");
+
+
+ parseExpectedException("IF(TRUE)");
+ parseExpectedException("countif(A1:B5, C1, D1)");
+ }
+
+ private static void parseExpectedException(String formula) {
+ try {
+ parseFormula(formula);
+ throw new AssertionFailedError("expected parse exception");
+ } catch (FormulaParseException e) {
+ // expected during successful test
+ assertNotNull(e.getMessage());
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ fail("Wrong exception:" + e.getMessage());
+ }
+ }
+
+ public void testSetFormulaWithRowBeyond32768_Bug44539() {
+
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet();
+ wb.setSheetName(0, "Sheet1");
+
+ HSSFRow row = sheet.createRow(0);
+ HSSFCell cell = row.createCell((short)0);
+ cell.setCellFormula("SUM(A32769:A32770)");
+ if("SUM(A-32767:A-32766)".equals(cell.getCellFormula())) {
+ fail("Identified bug 44539");
+ }
+ assertEquals("SUM(A32769:A32770)", cell.getCellFormula());
+ }
+
+ public void testSpaceAtStartOfFormula() {
+ // Simulating cell formula of "= 4" (note space)
+ // The same Ptg array can be observed if an excel file is saved with that exact formula
+
+ AttrPtg spacePtg = AttrPtg.createSpace(AttrPtg.SpaceType.SPACE_BEFORE, 1);
+ Ptg[] ptgs = { spacePtg, new IntPtg(4), };
+ String formulaString;
+ try {
+ formulaString = FormulaParser.toFormulaString(null, ptgs);
+ } catch (IllegalStateException e) {
+ if(e.getMessage().equalsIgnoreCase("too much stuff left on the stack")) {
+ throw new AssertionFailedError("Identified bug 44609");
+ }
+ // else some unexpected error
+ throw e;
+ }
+ // FormulaParser strips spaces anyway
+ assertEquals("4", formulaString);
+
+ ptgs = new Ptg[] { new IntPtg(3), spacePtg, new IntPtg(4), spacePtg, new AddPtg()};
+ formulaString = FormulaParser.toFormulaString(null, ptgs);
+ assertEquals("3+4", formulaString);
+ }
+
+ /**
+ * Checks some internal error detecting logic ('stack underflow error' in toFormulaString)
+ */
+ public void testTooFewOperandArgs() {
+ // Simulating badly encoded cell formula of "=/1"
+ // Not sure if Excel could ever produce this
+ Ptg[] ptgs = {
+ // Excel would probably have put tMissArg here
+ new IntPtg(1),
+ new DividePtg(),
+ };
+ try {
+ FormulaParser.toFormulaString(null, ptgs);
+ fail("Expected exception was not thrown");
+ } catch (IllegalStateException e) {
+ // expected during successful test
+ assertTrue(e.getMessage().startsWith("Too few arguments suppled to operation token"));
+ }
+ }
+ /**
+ * Make sure that POI uses the right Func Ptg when encoding formulas. Functions with variable
+ * number of args should get FuncVarPtg, functions with fixed args should get FuncPtg.
+ *
+ * Prior to the fix for bug 44675 POI would encode FuncVarPtg for all functions. In many cases
+ * Excel tolerates the wrong Ptg and evaluates the formula OK (e.g. SIN), but in some cases
+ * (e.g. COUNTIF) Excel fails to evaluate the formula, giving '#VALUE!' instead.
+ */
+ public void testFuncPtgSelection() {
+
+ Ptg[] ptgs;
+ ptgs = parseFormula("countif(A1:A2, 1)");
+ assertEquals(3, ptgs.length);
+ if(FuncVarPtg.class == ptgs[2].getClass()) {
+ throw new AssertionFailedError("Identified bug 44675");
+ }
+ assertEquals(FuncPtg.class, ptgs[2].getClass());
+ ptgs = parseFormula("sin(1)");
+ assertEquals(2, ptgs.length);
+ assertEquals(FuncPtg.class, ptgs[1].getClass());
+ }
+
+ public void testWrongNumberOfFunctionArgs() {
+ confirmArgCountMsg("sin()", "Too few arguments to function 'SIN'. Expected 1 but got 0.");
+ confirmArgCountMsg("countif(1, 2, 3, 4)", "Too many arguments to function 'COUNTIF'. Expected 2 but got 4.");
+ confirmArgCountMsg("index(1, 2, 3, 4, 5, 6)", "Too many arguments to function 'INDEX'. At most 4 were expected but got 6.");
+ confirmArgCountMsg("vlookup(1, 2)", "Too few arguments to function 'VLOOKUP'. At least 3 were expected but got 2.");
+ }
+
+ private static void confirmArgCountMsg(String formula, String expectedMessage) {
+ HSSFWorkbook book = new HSSFWorkbook();
+ try {
+ FormulaParser.parse(formula, book);
+ throw new AssertionFailedError("Didn't get parse exception as expected");
+ } catch (FormulaParseException e) {
+ assertEquals(expectedMessage, e.getMessage());
+ }
+ }
+
+ public void testParseErrorExpecteMsg() {
+
+ try {
+ parseFormula("round(3.14;2)");
+ throw new AssertionFailedError("Didn't get parse exception as expected");
+ } catch (FormulaParseException e) {
+ assertEquals("Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'", e.getMessage());
+ }
+ }
}
diff --git a/src/testcases/org/apache/poi/hssf/model/TestSheet.java b/src/testcases/org/apache/poi/hssf/model/TestSheet.java
index 9281eb80d..3bdcae57c 100644
--- a/src/testcases/org/apache/poi/hssf/model/TestSheet.java
+++ b/src/testcases/org/apache/poi/hssf/model/TestSheet.java
@@ -17,6 +17,7 @@
package org.apache.poi.hssf.model;
+import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
@@ -351,5 +352,25 @@ public final class TestSheet extends TestCase {
xfindex = sheet.getXFIndexForColAt((short) 10);
assertEquals(DEFAULT_IDX, xfindex);
}
+
+ /**
+ * Prior to bug 45066, POI would get the estimated sheet size wrong
+ * when an UncalcedRecord was present.
+ */
+ public void testUncalcSize_bug45066() {
+
+ List records = new ArrayList();
+ records.add(new BOFRecord());
+ records.add(new UncalcedRecord());
+ records.add(new EOFRecord());
+ Sheet sheet = Sheet.createSheet( records, 0, 0 );
+
+ int estimatedSize = sheet.getSize();
+ int serializedSize = sheet.serialize(0, new byte[estimatedSize]);
+ if (serializedSize != estimatedSize) {
+ throw new AssertionFailedError("Identified bug 45066 b");
+ }
+ assertEquals(50, serializedSize);
+ }
}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java b/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java
index 47137df4f..7702fce3d 100644
--- a/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java
+++ b/src/testcases/org/apache/poi/hssf/record/formula/function/ExcelFileFormatDocFunctionExtractor.java
@@ -26,9 +26,12 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -37,7 +40,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
-import java.util.zip.CRC32;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
@@ -149,7 +151,6 @@ public final class ExcelFileFormatDocFunctionExtractor {
private static final class FunctionDataCollector {
-
private final Map _allFunctionsByIndex;
private final Map _allFunctionsByName;
private final Set _groupFunctionIndexes;
@@ -184,25 +185,29 @@ public final class ExcelFileFormatDocFunctionExtractor {
_allFunctionsByName.put(funcName, fd);
}
+ /**
+ * Some extra validation here.
+ * Any function which changes definition will have a footnote in the source document
+ */
private void checkRedefinedFunction(boolean hasNote, String funcName, Integer funcIxKey) {
FunctionData fdPrev;
+ // check by index
fdPrev = (FunctionData) _allFunctionsByIndex.get(funcIxKey);
if(fdPrev != null) {
- if(fdPrev.hasFootnote() && hasNote) {
- // func def can change if both have a foot-note
- _allFunctionsByName.remove(fdPrev.getName());
- } else {
- throw new RuntimeException("changing function definition without foot-note");
+ if(!fdPrev.hasFootnote() || !hasNote) {
+ throw new RuntimeException("changing function ["
+ + funcIxKey + "] definition without foot-note");
}
+ _allFunctionsByName.remove(fdPrev.getName());
}
+ // check by name
fdPrev = (FunctionData) _allFunctionsByName.get(funcName);
if(fdPrev != null) {
- if(fdPrev.hasFootnote() && hasNote) {
- // func def can change if both have a foot-note
- _allFunctionsByIndex.remove(new Integer(fdPrev.getIndex()));
- } else {
- throw new RuntimeException("changing function definition without foot-note");
+ if(!fdPrev.hasFootnote() || !hasNote) {
+ throw new RuntimeException("changing function '"
+ + funcName + "' definition without foot-note");
}
+ _allFunctionsByIndex.remove(new Integer(fdPrev.getIndex()));
}
}
@@ -237,9 +242,13 @@ public final class ExcelFileFormatDocFunctionExtractor {
private static final String[] TABLE_CELL_RELPATH_NAMES = {
"table:table-row", "table:table-cell", "text:p",
};
- private static final String[] NOTE_REF_RELPATH_NAMES = {
+ // after May 2008 there was one more style applied to the footnotes
+ private static final String[] NOTE_REF_RELPATH_NAMES_OLD = {
"table:table-row", "table:table-cell", "text:p", "text:span", "text:note-ref",
};
+ private static final String[] NOTE_REF_RELPATH_NAMES = {
+ "table:table-row", "table:table-cell", "text:p", "text:span", "text:span", "text:note-ref",
+ };
private final Stack _elemNameStack;
@@ -368,6 +377,8 @@ public final class ExcelFileFormatDocFunctionExtractor {
} else if(matchesRelPath(TABLE_CELL_RELPATH_NAMES)) {
_textNodeBuffer.setLength(0);
_cellHasNote = false;
+ } else if(matchesRelPath(NOTE_REF_RELPATH_NAMES_OLD)) {
+ _cellHasNote = true;
} else if(matchesRelPath(NOTE_REF_RELPATH_NAMES)) {
_cellHasNote = true;
}
@@ -456,6 +467,9 @@ public final class ExcelFileFormatDocFunctionExtractor {
}
private static void processFile(File effDocFile, File outFile) {
+ if(!effDocFile.exists()) {
+ throw new RuntimeException("file '" + effDocFile.getAbsolutePath() + "' does not exist");
+ }
OutputStream os;
try {
os = new FileOutputStream(outFile);
@@ -475,7 +489,7 @@ public final class ExcelFileFormatDocFunctionExtractor {
ps.println("# Created by (" + genClass.getName() + ")");
// identify the source file
ps.print("# from source file '" + SOURCE_DOC_FILE_NAME + "'");
- ps.println(" (size=" + effDocFile.length() + ", crc=" + getFileCRC(effDocFile) + ")");
+ ps.println(" (size=" + effDocFile.length() + ", md5=" + getFileMD5(effDocFile) + ")");
ps.println("#");
ps.println("#Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote )");
ps.println("");
@@ -490,6 +504,14 @@ public final class ExcelFileFormatDocFunctionExtractor {
throw new RuntimeException(e);
}
ps.close();
+
+ String canonicalOutputFileName;
+ try {
+ canonicalOutputFileName = outFile.getCanonicalPath();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ System.out.println("Successfully output to '" + canonicalOutputFileName + "'");
}
private static void outputLicenseHeader(PrintStream ps) {
@@ -519,8 +541,14 @@ public final class ExcelFileFormatDocFunctionExtractor {
/**
* Helps identify the source file
*/
- private static String getFileCRC(File f) {
- CRC32 crc = new CRC32();
+ private static String getFileMD5(File f) {
+ MessageDigest m;
+ try {
+ m = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+
byte[]buf = new byte[2048];
try {
InputStream is = new FileInputStream(f);
@@ -529,21 +557,17 @@ public final class ExcelFileFormatDocFunctionExtractor {
if(bytesRead<1) {
break;
}
- crc.update(buf, 0, bytesRead);
+ m.update(buf, 0, bytesRead);
}
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
- return "0x" + Long.toHexString(crc.getValue()).toUpperCase();
+
+ return "0x" + new BigInteger(1, m.digest()).toString(16);
}
- private static File getSourceFile() {
- if (false) {
- File dir = new File("c:/temp");
- File effDocFile = new File(dir, SOURCE_DOC_FILE_NAME);
- return effDocFile;
- }
+ private static File downloadSourceFile() {
URL url;
try {
url = new URL("http://sc.openoffice.org/" + SOURCE_DOC_FILE_NAME);
@@ -557,7 +581,7 @@ public final class ExcelFileFormatDocFunctionExtractor {
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
System.out.println("downloading " + url.toExternalForm());
- result = File.createTempFile("excelfileformat", "odt");
+ result = File.createTempFile("excelfileformat", ".odt");
OutputStream os = new FileOutputStream(result);
while(true) {
int bytesRead = is.read(buf);
@@ -577,12 +601,17 @@ public final class ExcelFileFormatDocFunctionExtractor {
public static void main(String[] args) {
- File effDocFile = getSourceFile();
- if(!effDocFile.exists()) {
- throw new RuntimeException("file '" + effDocFile.getAbsolutePath() + "' does not exist");
- }
-
File outFile = new File("functionMetadata-asGenerated.txt");
- processFile(effDocFile, outFile);
+
+ if (false) { // set true to use local file
+ File dir = new File("c:/temp");
+ File effDocFile = new File(dir, SOURCE_DOC_FILE_NAME);
+ processFile(effDocFile, outFile);
+ return;
+ }
+
+ File tempEFFDocFile = downloadSourceFile();
+ processFile(tempEFFDocFile, outFile);
+ tempEFFDocFile.delete();
}
}
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java b/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java
index 1e26fa706..7030c5a50 100644
--- a/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java
+++ b/src/testcases/org/apache/poi/hssf/record/formula/function/TestParseMissingBuiltInFuncs.java
@@ -21,7 +21,6 @@ import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.model.FormulaParser;
-import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
import org.apache.poi.hssf.record.formula.FuncPtg;
import org.apache.poi.hssf.record.formula.FuncVarPtg;
@@ -29,7 +28,7 @@ import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* Tests parsing of some built-in functions that were not properly
- * registered in POI as bug #44675, #44733 (March/April 2008).
+ * registered in POI as of bug #44675, #44733 (March/April 2008).
*
* @author Josh Micich
*/
@@ -76,7 +75,7 @@ public final class TestParseMissingBuiltInFuncs extends TestCase {
}
public void testUsdollar() {
- confirmFunc("USDOLLAR(1)", 2, false, 204);
+ confirmFunc("USDOLLAR(1)", 2, true, 204);
}
public void testDBCS() {
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java b/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java
index 6766f2fc0..0a62d64cd 100644
--- a/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java
+++ b/src/testcases/org/apache/poi/hssf/record/formula/function/TestReadMissingBuiltInFuncs.java
@@ -17,22 +17,18 @@
package org.apache.poi.hssf.record.formula.function;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.TestCase;
/**
* Tests reading from a sample spreadsheet some built-in functions that were not properly
- * registered in POI as bug #44675, #44733 (March/April 2008).
+ * registered in POI as of bug #44675, #44733 (March/April 2008).
*
* @author Josh Micich
*/
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java b/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java
index 0e18e21e5..363e58c14 100755
--- a/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java
@@ -28,7 +28,7 @@ import junit.framework.TestSuite;
public class AllUserModelTests {
public static Test suite() {
- TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.usermodel");
+ TestSuite result = new TestSuite(AllUserModelTests.class.getName());
result.addTestSuite(TestBugs.class);
result.addTestSuite(TestCellStyle.class);
@@ -58,6 +58,7 @@ public class AllUserModelTests {
result.addTestSuite(TestHSSFSheetSetOrder.class);
result.addTestSuite(TestHSSFTextbox.class);
result.addTestSuite(TestHSSFWorkbook.class);
+ result.addTestSuite(TestLinkTable.class);
result.addTestSuite(TestNamedRange.class);
result.addTestSuite(TestOLE2Embeding.class);
result.addTestSuite(TestPOIFSProperties.class);
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java
index a2e8bd3ba..7f4375847 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java
@@ -58,6 +58,30 @@ public final class TestHSSFCell extends TestCase {
}
}
+ public void testSetValues() throws Exception {
+ HSSFWorkbook book = new HSSFWorkbook();
+ HSSFSheet sheet = book.createSheet("test");
+ HSSFRow row = sheet.createRow(0);
+
+ HSSFCell cell = row.createCell((short)0);
+
+ cell.setCellValue(1.2);
+ assertEquals(1.2, cell.getNumericCellValue(), 0.0001);
+ assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cell.getCellType());
+
+ cell.setCellValue(false);
+ assertEquals(false, cell.getBooleanCellValue());
+ assertEquals(HSSFCell.CELL_TYPE_BOOLEAN, cell.getCellType());
+
+ cell.setCellValue(new HSSFRichTextString("Foo"));
+ assertEquals("Foo", cell.getRichStringCellValue().getString());
+ assertEquals(HSSFCell.CELL_TYPE_STRING, cell.getCellType());
+
+ cell.setCellValue(new HSSFRichTextString("345"));
+ assertEquals("345", cell.getRichStringCellValue().getString());
+ assertEquals(HSSFCell.CELL_TYPE_STRING, cell.getCellType());
+ }
+
/**
* test that Boolean and Error types (BoolErrRecord) are supported properly.
*/
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java
index cd0901a29..76c098da2 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java
@@ -266,6 +266,8 @@ public class TestHSSFDateUtil extends TestCase {
formats = new String[] {
"yyyy-mm-dd hh:mm:ss", "yyyy/mm/dd HH:MM:SS",
"mm/dd HH:MM", "yy/mmm/dd SS",
+ "mm/dd HH:MM AM", "mm/dd HH:MM am",
+ "mm/dd HH:MM PM", "mm/dd HH:MM pm"
};
for(int i=0; i
+ * TODO get OOO documentation updated to reflect this (that EXTERNALBOOK is optional).
+ *
+ * It's not clear what exact steps need to be taken in Excel to create such a workbook
+ */
+ public void testLinkTableWithoutExternalBookRecord_bug45046() {
+ HSSFWorkbook wb;
+
+ try {
+ wb = HSSFTestDataSamples.openSampleWorkbook("ex45046-21984.xls");
+ } catch (RuntimeException e) {
+ if ("DEFINEDNAME is part of LinkTable".equals(e.getMessage())) {
+ throw new AssertionFailedError("Identified bug 45046 b");
+ }
+ throw e;
+ }
+ // some other sanity checks
+ assertEquals(3, wb.getNumberOfSheets());
+ String formula = wb.getSheetAt(0).getRow(4).getCell(13).getCellFormula();
+
+ if ("ipcSummenproduktIntern($P5,N$6,$A$9,N$5)".equals(formula)) {
+ // The reported symptom of this bugzilla is an earlier bug (already fixed)
+ throw new AssertionFailedError("Identified bug 41726");
+ // This is observable in version 3.0
+ }
+
+ assertEquals("ipcSummenproduktIntern($C5,N$2,$A$9,N$1)", formula);
+ }
+}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetHiding.java b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetHiding.java
index fc2a24b78..62a26e90b 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetHiding.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetHiding.java
@@ -45,8 +45,8 @@ public final class TestSheetHiding extends TestCase {
*/
public void testTextSheets() throws Exception {
// Both should have two sheets
- assertEquals(2, wbH.sheets.size());
- assertEquals(2, wbU.sheets.size());
+ assertEquals(2, wbH.getNumberOfSheets());
+ assertEquals(2, wbU.getNumberOfSheets());
// All sheets should have one row
assertEquals(0, wbH.getSheetAt(0).getLastRowNum());