You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by jo...@apache.org on 2008/05/23 08:43:54 UTC

svn commit: r659452 - in /poi/trunk/src: documentation/content/xdocs/changes.xml documentation/content/xdocs/status.xml java/org/apache/poi/hssf/model/FormulaParser.java testcases/org/apache/poi/hssf/model/TestFormulaParser.java

Author: josh
Date: Thu May 22 23:43:51 2008
New Revision: 659452

URL: http://svn.apache.org/viewvc?rev=659452&view=rev
Log:
Bug 45041 - improved FormulaParser parse error messages

Modified:
    poi/trunk/src/documentation/content/xdocs/changes.xml
    poi/trunk/src/documentation/content/xdocs/status.xml
    poi/trunk/src/java/org/apache/poi/hssf/model/FormulaParser.java
    poi/trunk/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java

Modified: poi/trunk/src/documentation/content/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/poi/trunk/src/documentation/content/xdocs/changes.xml?rev=659452&r1=659451&r2=659452&view=diff
==============================================================================
--- poi/trunk/src/documentation/content/xdocs/changes.xml (original)
+++ poi/trunk/src/documentation/content/xdocs/changes.xml Thu May 22 23:43:51 2008
@@ -37,6 +37,7 @@
 
 		<!-- Don't forget to update status.xml too! -->
         <release version="3.1-final" date="2008-06-??">
+           <action dev="POI-DEVELOPERS" type="add">45041 - improved FormulaParser parse error messages</action>
            <action dev="POI-DEVELOPERS" type="add">45046 - allowed EXTERNALBOOK(0x01AE) to be optional in the LinkTable</action>
            <action dev="POI-DEVELOPERS" type="add">45066 - fixed sheet encoding size mismatch problems</action>
            <action dev="POI-DEVELOPERS" type="add">45003 - Support embeded HDGF visio documents</action>

Modified: poi/trunk/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/trunk/src/documentation/content/xdocs/status.xml?rev=659452&r1=659451&r2=659452&view=diff
==============================================================================
--- poi/trunk/src/documentation/content/xdocs/status.xml (original)
+++ poi/trunk/src/documentation/content/xdocs/status.xml Thu May 22 23:43:51 2008
@@ -34,6 +34,7 @@
 	<!-- Don't forget to update changes.xml too! -->
     <changes>
         <release version="3.1-final" date="2008-06-??">
+           <action dev="POI-DEVELOPERS" type="add">45041 - improved FormulaParser parse error messages</action>
            <action dev="POI-DEVELOPERS" type="add">45046 - allowed EXTERNALBOOK(0x01AE) to be optional in the LinkTable</action>
            <action dev="POI-DEVELOPERS" type="add">45066 - fixed sheet encoding size mismatch problems</action>
            <action dev="POI-DEVELOPERS" type="add">45003 - Support embeded HDGF visio documents</action>

Modified: poi/trunk/src/java/org/apache/poi/hssf/model/FormulaParser.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/model/FormulaParser.java?rev=659452&r1=659451&r2=659452&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/model/FormulaParser.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/model/FormulaParser.java Thu May 22 23:43:51 2008
@@ -55,7 +55,7 @@
      */
     static final class FormulaParseException extends RuntimeException {
         // This class was given package scope until it would become clear that it is useful to
-        // general client code. 
+        // general client code.
         public FormulaParseException(String msg) {
             super(msg);
         }
@@ -127,14 +127,14 @@
             // Just return if so and reset 'look' to something to keep
             // SkipWhitespace from spinning
             look = (char)0;
-        }    
+        }
         pointer++;
         //System.out.println("Got char: "+ look);
     }
 
     /** Report What Was Expected */
     private RuntimeException expected(String s) {
-        String msg = "Parse error near char " + (pointer-1) + "'" + look + "'" 
+        String msg = "Parse error near char " + (pointer-1) + " '" + look + "'"
             + " in specified formula '" + formulaString + "'. Expected "
             + s;
         return new FormulaParseException(msg);
@@ -178,7 +178,7 @@
     /**
      *  Consumes the next input character if it is equal to the one specified otherwise throws an
      *  unchecked exception. This method does <b>not</b> consume whitespace (before or after the
-     *  matched character). 
+     *  matched character).
      */
     private void Match(char x) {
         if (look != x) {
@@ -281,18 +281,18 @@
         // This can be either a cell ref or a named range
         // Try to spot which it is
         boolean cellRef = CELL_REFERENCE_PATTERN.matcher(name).matches();
- 
+
         if (cellRef) {
             return new ReferencePtg(name);
         }
 
         for(int i = 0; i < book.getNumberOfNames(); i++) {
             // named range name matching is case insensitive
-        	if(book.getNameAt(i).getNameName().equalsIgnoreCase(name)) {
+            if(book.getNameAt(i).getNameName().equalsIgnoreCase(name)) {
                 return new NamePtg(name, book);
             }
         }
-        throw new FormulaParseException("Found reference to named range \"" 
+        throw new FormulaParseException("Found reference to named range \""
                     + name + "\", but that named range wasn't defined!");
     }
 
@@ -307,19 +307,19 @@
     /**
      * 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
-     * Excel function, and has not been encountered before. 
-     * 
-     * @param name case preserved function name (as it was entered/appeared in the formula). 
+     * Excel function, and has not been encountered before.
+     *
+     * @param name case preserved function name (as it was entered/appeared in the formula).
      */
     private Ptg function(String name) {
         int numArgs =0 ;
-        // Note regarding parameter - 
+        // Note regarding parameter -
         if(!AbstractFunctionPtg.isInternalFunctionName(name)) {
             // external functions get a Name token which points to a defined name record
             NamePtg nameToken = new NamePtg(name, this.book);
-            
+
             // in the token tree, the name is more or less the first argument
-            numArgs++;  
+            numArgs++;
             tokens.add(nameToken);
         }
         //average 2 args per function
@@ -477,26 +477,25 @@
     private static boolean isArgumentDelimiter(char ch) {
         return ch ==  ',' || ch == ')';
     }
-    
+
     /** get arguments to a function */
     private int Arguments(List argumentPointers) {
         SkipWhite();
         if(look == ')') {
             return 0;
         }
-        
+
         boolean missedPrevArg = true;
-        
         int numArgs = 0;
-        while(true) {
+        while (true) {
             SkipWhite();
-            if(isArgumentDelimiter(look)) {
-                if(missedPrevArg) {
+            if (isArgumentDelimiter(look)) {
+                if (missedPrevArg) {
                     tokens.add(new MissingArgPtg());
                     addArgumentPointer(argumentPointers);
                     numArgs++;
                 }
-                if(look == ')') {
+                if (look == ')') {
                     break;
                 }
                 Match(',');
@@ -507,6 +506,10 @@
             addArgumentPointer(argumentPointers);
             numArgs++;
             missedPrevArg = false;
+            SkipWhite();
+            if (!isArgumentDelimiter(look)) {
+                throw expected("',' or ')'");
+            }
         }
         return numArgs;
     }
@@ -524,7 +527,7 @@
             tokens.add(new PowerPtg());
         }
     }
-    
+
     private void percentFactor() {
         tokens.add(parseSimpleFactor());
         while(true) {
@@ -536,8 +539,8 @@
             tokens.add(new PercentPtg());
         }
     }
-    
-    
+
+
     /**
      * factors (without ^ or % )
      */
@@ -710,7 +713,7 @@
     private StringPtg parseStringLiteral()
     {
         Match('"');
-        
+
         StringBuffer token = new StringBuffer();
         while (true) {
             if (look == '"') {
@@ -745,7 +748,7 @@
             return; // finished with Term
         }
     }
-    
+
     private void comparisonExpression() {
         concatExpression();
         while (true) {
@@ -787,7 +790,7 @@
         }
         return new LessThanPtg();
     }
-    
+
 
     private void concatExpression() {
         additiveExpression();
@@ -801,7 +804,7 @@
             tokens.add(new ConcatPtg());
         }
     }
-    
+
 
     /** Parse and Translate an Expression */
     private void additiveExpression() {
@@ -847,8 +850,8 @@
         comparisonExpression();
 
         if(pointer <= formulaLength) {
-            String msg = "Unused input [" + formulaString.substring(pointer-1) 
-                + "] after attempting to parse the formula [" + formulaString + "]"; 
+            String msg = "Unused input [" + formulaString.substring(pointer-1)
+                + "] after attempting to parse the formula [" + formulaString + "]";
             throw new FormulaParseException(msg);
         }
     }
@@ -1011,7 +1014,7 @@
                     continue;
                     // but if it ever did, care must be taken:
                     // tAttrSpace comes *before* the operand it applies to, which may be consistent
-                    // with how the formula text appears but is against the RPN ordering assumed here 
+                    // with how the formula text appears but is against the RPN ordering assumed here
                 }
                 if (attrPtg.isSemiVolatile()) {
                     // similar to tAttrSpace - RPN is violated
@@ -1038,7 +1041,7 @@
             stack.push(o.toFormulaString(operands));
         }
         if(stack.isEmpty()) {
-            // inspection of the code above reveals that every stack.pop() is followed by a 
+            // inspection of the code above reveals that every stack.pop() is followed by a
             // stack.push(). So this is either an internal error or impossible.
             throw new IllegalStateException("Stack underflow");
         }

Modified: poi/trunk/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java?rev=659452&r1=659451&r2=659452&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java Thu May 22 23:43:51 2008
@@ -61,114 +61,94 @@
  */
 public final class TestFormulaParser extends TestCase {
 
-    /**
-     * @return parsed token array already confirmed not <code>null</code>
-     */
-    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 <code>null</code>
+	 */
+	private static Ptg[] parseFormula(String s) {
+		FormulaParser fp = new FormulaParser(s, null);
 		fp.parse();
-		Ptg[] asts = fp.getRPNPtg();
-		assertEquals(9, asts.length);
-		
-		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());
+		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);
 		
-		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 +156,714 @@
 	 *
 	 */
 	public void testNestedFunctionIf() {
-		String function = "IF(A1=B1,AVERAGE(A1:B1),AVERAGE(A2:B2))";
-
-		FormulaParser fp = new FormulaParser(function, null);
-		fp.parse();
-		Ptg[] asts = fp.getRPNPtg();
-		assertEquals("11 Ptgs expected", 11, asts.length);
+		Ptg[] ptgs = parseFormula("IF(A1=B1,AVERAGE(A1:B1),AVERAGE(A2:B2))");
+		assertEquals(11, ptgs.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)";
 
-		FormulaParser fp = new FormulaParser(function, null);
-		fp.parse();
-		Ptg[] asts = fp.getRPNPtg();
-		assertEquals("7 Ptgs expected", 7, asts.length);
+	public void testIfSingleCondition(){
+		Ptg[] ptgs = parseFormula("IF(1=1,10)");
+		assertEquals(7, ptgs.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 <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=17582">#17582</a>
 	 *
 	 */
-	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<A2,B1,B2)");
-      assertEquals("Ptg array length", 9, ptgs.length);
-      assertEquals("3rd Ptg is less than", LessThanPtg.class, ptgs[2].getClass());
+	 Ptg[] ptgs = parseFormula("IF(A1<A2,B1,B2)");
+	 assertEquals(9, ptgs.length);
+	 assertEquals("3rd Ptg is less than", LessThanPtg.class, ptgs[2].getClass());
 	}
-	
+
 	public void testParenIf() {
 		Ptg[] ptgs = parseFormula("IF((A1+A2)<=3,\"yes\",\"no\")");
-		assertEquals("Ptg array length", 12, ptgs.length);
+		assertEquals(12, ptgs.length);
 		assertEquals("6th Ptg is less than equal",LessEqualPtg.class,ptgs[5].getClass());
 		assertEquals("11th Ptg is not a goto (Attr) ptg",AttrPtg.class,ptgs[10].getClass());
 	}
-	
+
 	public void testEmbeddedIf() {
 		Ptg[] ptgs = parseFormula("IF(3>=1,\"*\",IF(4<>1,\"first\",\"second\"))");
-		assertEquals("Ptg array length", 17, ptgs.length);
-		
+		assertEquals(17, ptgs.length);
+
 		assertEquals("6th Ptg is not a goto (Attr) ptg",AttrPtg.class,ptgs[5].getClass());
 		assertEquals("9th Ptg is not a not equal ptg",NotEqualPtg.class,ptgs[8].getClass());
 		assertEquals("15th Ptg is not the inner IF variable function ptg",FuncVarPtg.class,ptgs[14].getClass());
 	}
-	
-    public void testMacroFunction() {
-    	HSSFWorkbook w = new HSSFWorkbook();
-        FormulaParser fp = new FormulaParser("FOO()", w);
-        fp.parse();
-        Ptg[] ptg = fp.getRPNPtg();
-
-        // the name gets encoded as the first arg
-        NamePtg tname = (NamePtg) ptg[0];
-        assertEquals("FOO", tname.toFormulaString(w));
-
-        AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1];
-        assertTrue(tfunc.isExternalFunction());
-    }
-
-    public void testEmbeddedSlash() {
-        FormulaParser fp = new FormulaParser("HYPERLINK(\"http://www.jakarta.org\",\"Jakarta\")",null);
-        fp.parse();
-        Ptg[] ptg = fp.getRPNPtg();
-        assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
-        assertTrue("second ptg is string",ptg[1] instanceof StringPtg);
-    }
 
-    public void testConcatenate() {
-		FormulaParser fp = new FormulaParser("CONCATENATE(\"first\",\"second\")", null);
+	public void testMacroFunction() {
+		HSSFWorkbook w = new HSSFWorkbook();
+		FormulaParser fp = new FormulaParser("FOO()", w);
 		fp.parse();
 		Ptg[] ptg = fp.getRPNPtg();
-		assertTrue("first ptg is string", ptg[0] instanceof StringPtg);
-		assertTrue("second ptg is string", ptg[1] instanceof StringPtg);
+
+		// the name gets encoded as the first arg
+		NamePtg tname = (NamePtg) ptg[0];
+		assertEquals("FOO", tname.toFormulaString(w));
+
+		AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1];
+		assertTrue(tfunc.isExternalFunction());
 	}
 
-    public void testWorksheetReferences()
-    {
-    	HSSFWorkbook wb = new HSSFWorkbook();
-    	
-    	wb.createSheet("NoQuotesNeeded");
-    	wb.createSheet("Quotes Needed Here &#$@");
-    	
-    	HSSFSheet sheet = wb.createSheet("Test");
-    	HSSFRow row = sheet.createRow(0);
-    	HSSFCell cell;
-    	
-    	cell = row.createCell((short)0);
-    	cell.setCellFormula("NoQuotesNeeded!A1");
-    	
-    	cell = row.createCell((short)1);
-    	cell.setCellFormula("'Quotes Needed Here &#$@'!A1");
-    }
-
-    public void testUnaryMinus()
-    {
-		FormulaParser fp = new FormulaParser("-A1", null);
-		fp.parse();
-		Ptg[] ptg = fp.getRPNPtg();
-		assertTrue("got 2 ptgs", ptg.length == 2);
-		assertTrue("first ptg is reference",ptg[0] instanceof ReferencePtg);
-		assertTrue("second ptg is Minus",ptg[1] instanceof UnaryMinusPtg);
-     }
-
-    public void testUnaryPlus()
-    {
-		FormulaParser fp = new FormulaParser("+A1", null);
-		fp.parse();
-		Ptg[] ptg = fp.getRPNPtg();
-		assertTrue("got 2 ptgs", ptg.length == 2);
-		assertTrue("first ptg is reference",ptg[0] instanceof ReferencePtg);
-		assertTrue("second ptg is Plus",ptg[1] instanceof UnaryPlusPtg);
-     }
+	public void testEmbeddedSlash() {
+		Ptg[] ptgs = parseFormula("HYPERLINK(\"http://www.jakarta.org\",\"Jakarta\")");
+		assertTrue("first ptg is string", ptgs[0] instanceof StringPtg);
+		assertTrue("second ptg is string", ptgs[1] instanceof StringPtg);
+	}
+
+	public void testConcatenate() {
+		Ptg[] ptgs = parseFormula("CONCATENATE(\"first\",\"second\")");
+		assertTrue("first ptg is string", ptgs[0] instanceof StringPtg);
+		assertTrue("second ptg is string", ptgs[1] instanceof StringPtg);
+	}
+
+	public void testWorksheetReferences() {
+		HSSFWorkbook wb = new HSSFWorkbook();
+
+		wb.createSheet("NoQuotesNeeded");
+		wb.createSheet("Quotes Needed Here &#$@");
+
+		HSSFSheet sheet = wb.createSheet("Test");
+		HSSFRow row = sheet.createRow(0);
+		HSSFCell cell;
+
+		cell = row.createCell((short)0);
+		cell.setCellFormula("NoQuotesNeeded!A1");
+
+		cell = row.createCell((short)1);
+		cell.setCellFormula("'Quotes Needed Here &#$@'!A1");
+	}
+
+	public void testUnaryMinus() {
+		Ptg[] ptgs = parseFormula("-A1");
+		assertEquals(2, ptgs.length);
+		assertTrue("first ptg is reference",ptgs[0] instanceof ReferencePtg);
+		assertTrue("second ptg is Minus",ptgs[1] instanceof UnaryMinusPtg);
+	}
 
-	public void testLeadingSpaceInString()
-	{
+	public void testUnaryPlus() {
+		Ptg[] ptgs = parseFormula("+A1");
+		assertEquals(2, ptgs.length);
+		assertTrue("first ptg is reference",ptgs[0] instanceof ReferencePtg);
+		assertTrue("second ptg is Plus",ptgs[1] instanceof UnaryPlusPtg);
+	}
+
+	public void testLeadingSpaceInString() {
 		String value = "  hi  ";
-		FormulaParser fp = new FormulaParser("\"" + value + "\"", null);
-		fp.parse();
-		Ptg[] ptg = fp.getRPNPtg();
+		Ptg[] ptgs = parseFormula("\"" + value + "\"");
 
-		assertTrue("got 1 ptg", ptg.length == 1);
-		assertTrue("ptg0 is a StringPtg", ptg[0] instanceof StringPtg);
-		assertTrue("ptg0 contains exact value", ((StringPtg)ptg[0]).getValue().equals(value));
-	}
-	
-	public void testLookupAndMatchFunctionArgs()
-	{
-		FormulaParser fp = new FormulaParser("lookup(A1, A3:A52, B3:B52)", null);
-		fp.parse();
-		Ptg[] ptg = fp.getRPNPtg();
+		assertEquals(1, ptgs.length);
+		assertTrue("ptg0 is a StringPtg", ptgs[0] instanceof StringPtg);
+		assertTrue("ptg0 contains exact value", ((StringPtg)ptgs[0]).getValue().equals(value));
+	}
 
-		assertTrue("got 4 ptg", ptg.length == 4);
-		assertTrue("ptg0 has Value class", ptg[0].getPtgClass() == Ptg.CLASS_VALUE);
-		
-		fp = new FormulaParser("match(A1, A3:A52)", null);
-		fp.parse();
-		ptg = fp.getRPNPtg();
+	public void testLookupAndMatchFunctionArgs() {
+		Ptg[] ptgs = parseFormula("lookup(A1, A3:A52, B3:B52)");
 
-		assertTrue("got 3 ptg", ptg.length == 3);
-		assertTrue("ptg0 has Value class", ptg[0].getPtgClass() == Ptg.CLASS_VALUE);
+		assertEquals(4, ptgs.length);
+		assertTrue("ptg0 has Value class", ptgs[0].getPtgClass() == Ptg.CLASS_VALUE);
+
+		ptgs = parseFormula("match(A1, A3:A52)");
+
+		assertEquals(3, ptgs.length);
+		assertTrue("ptg0 has Value class", ptgs[0].getPtgClass() == Ptg.CLASS_VALUE);
 	}
-	
+
 	/** bug 33160*/
 	public void testLargeInt() {
-		FormulaParser fp = new FormulaParser("40", null);
-		fp.parse();
-		Ptg[] ptg=fp.getRPNPtg();
-		assertTrue("ptg is Int, is "+ptg[0].getClass(),ptg[0] instanceof IntPtg);
-		
-		fp = new FormulaParser("40000", null);
-		fp.parse();
-		ptg=fp.getRPNPtg();
-		assertTrue("ptg should be  IntPtg, is "+ptg[0].getClass(), ptg[0] instanceof IntPtg);
+		Ptg[] ptgs = parseFormula("40");
+		assertTrue("ptg is Int, is "+ptgs[0].getClass(),ptgs[0] instanceof IntPtg);
+
+		ptgs = parseFormula("40000");
+		assertTrue("ptg should be  IntPtg, is "+ptgs[0].getClass(), ptgs[0] instanceof IntPtg);
 	}
 
 	/** bug 33160, testcase by Amol Deshmukh*/
 	public void testSimpleLongFormula() {
-		FormulaParser fp = new FormulaParser("40000/2", null);
-		fp.parse();
-		Ptg[] ptgs = fp.getRPNPtg();
-		assertTrue("three tokens expected, got " + ptgs.length, ptgs.length == 3);
+		Ptg[] ptgs = parseFormula("40000/2");
+		assertEquals(3, ptgs.length);
 		assertTrue("IntPtg", (ptgs[0] instanceof IntPtg));
 		assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
 		assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
 	}
-	
+
 	/** bug 35027, underscore in sheet name */
 	public void testUnderscore() {
 		HSSFWorkbook wb = new HSSFWorkbook();
-    	
-    	wb.createSheet("Cash_Flow");
-    	
-    	HSSFSheet sheet = wb.createSheet("Test");
-    	HSSFRow row = sheet.createRow(0);
-    	HSSFCell cell;
-    	
-    	cell = row.createCell((short)0);
-    	cell.setCellFormula("Cash_Flow!A1");
-		
+
+		wb.createSheet("Cash_Flow");
+
+		HSSFSheet sheet = wb.createSheet("Test");
+		HSSFRow row = sheet.createRow(0);
+		HSSFCell cell;
+
+		cell = row.createCell((short)0);
+		cell.setCellFormula("Cash_Flow!A1");
+	}
+
+	// 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));
 	}
 
-    // bug 38396 : Formula with exponential numbers not parsed correctly.
-    public void testExponentialParsing() {
-        FormulaParser fp = new FormulaParser("1.3E21/2", null);
-        fp.parse();
-        Ptg[] ptgs = fp.getRPNPtg();
-        assertTrue("three tokens expected, got " + ptgs.length, ptgs.length == 3);
-        assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
-        assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
-        assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
-
-        fp = new FormulaParser("1322E21/2", null);
-        fp.parse();
-        ptgs = fp.getRPNPtg();
-        assertTrue("three tokens expected, got " + ptgs.length, ptgs.length == 3);
-        assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
-        assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
-        assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
-
-        fp = new FormulaParser("1.3E1/2", null);
-        fp.parse();
-        ptgs = fp.getRPNPtg();
-        assertTrue("three tokens expected, got " + ptgs.length, ptgs.length == 3);
-        assertTrue("NumberPtg", (ptgs[0] instanceof NumberPtg));
-        assertTrue("IntPtg", (ptgs[1] instanceof IntPtg));
-        assertTrue("DividePtg", (ptgs[2] instanceof DividePtg));
-
-    }
-    public void testExponentialInSheet() throws Exception {
-        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 static void main(String [] args) {
-        System.out.println("Testing org.apache.poi.hssf.record.formula.FormulaParser");
-        junit.textui.TestRunner.run(TestFormulaParser.class);
-    }
-
-    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)<br/>
-     * 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.<p/>
-     * 
-     * 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());
-        }
-    }
+	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)<br/>
+	 * 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.<p/>
+	 * 
+	 * 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());
+		}
+	}
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org