]> source.dussan.org Git - poi.git/commitdiff
Bug 45041 - improved FormulaParser parse error messages
authorJosh Micich <josh@apache.org>
Fri, 23 May 2008 06:43:51 +0000 (06:43 +0000)
committerJosh Micich <josh@apache.org>
Fri, 23 May 2008 06:43:51 +0000 (06:43 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@659452 13f79535-47bb-0310-9956-ffa450edef68

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

index cc52e793bc57b2c1dc74cc0fd4dea5b1acbc45d2..720cce08082bce8d5db5d2fc48761f8e5aac2d55 100644 (file)
@@ -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>
index 85f5b8963b7b036177f623ec0a924d4201a7bd55..e68afab26857fa6da52c4d3776a59ce2e37bc58d 100644 (file)
@@ -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>
index 106fc5cde395dcf18a5e535ff4657feb04769928..c32df9396bd1ffa2f4835800f8457578b1edc3f2 100644 (file)
@@ -55,7 +55,7 @@ public final class FormulaParser {
      */
     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 @@ public final class FormulaParser {
             // 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 @@ public final class FormulaParser {
     /**
      *  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 @@ public final class FormulaParser {
         // 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 @@ public final class FormulaParser {
     /**
      * 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 @@ public final class FormulaParser {
     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 @@ public final class FormulaParser {
             addArgumentPointer(argumentPointers);
             numArgs++;
             missedPrevArg = false;
+            SkipWhite();
+            if (!isArgumentDelimiter(look)) {
+                throw expected("',' or ')'");
+            }
         }
         return numArgs;
     }
@@ -524,7 +527,7 @@ public final class FormulaParser {
             tokens.add(new PowerPtg());
         }
     }
-    
+
     private void percentFactor() {
         tokens.add(parseSimpleFactor());
         while(true) {
@@ -536,8 +539,8 @@ public final class FormulaParser {
             tokens.add(new PercentPtg());
         }
     }
-    
-    
+
+
     /**
      * factors (without ^ or % )
      */
@@ -710,7 +713,7 @@ public final class FormulaParser {
     private StringPtg parseStringLiteral()
     {
         Match('"');
-        
+
         StringBuffer token = new StringBuffer();
         while (true) {
             if (look == '"') {
@@ -745,7 +748,7 @@ public final class FormulaParser {
             return; // finished with Term
         }
     }
-    
+
     private void comparisonExpression() {
         concatExpression();
         while (true) {
@@ -787,7 +790,7 @@ public final class FormulaParser {
         }
         return new LessThanPtg();
     }
-    
+
 
     private void concatExpression() {
         additiveExpression();
@@ -801,7 +804,7 @@ public final class FormulaParser {
             tokens.add(new ConcatPtg());
         }
     }
-    
+
 
     /** Parse and Translate an Expression */
     private void additiveExpression() {
@@ -847,8 +850,8 @@ end;
         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 @@ end;
                     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 @@ end;
             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");
         }
index 1895705a564bb9bc961a66b5f85c56d362a88849..f4d6d626924695ce2b3fdb133ed21e23cf7ac2bf 100644 (file)
@@ -61,114 +61,94 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  */
 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 final class TestFormulaParser extends TestCase {
         *
         */
        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);
-               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 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);
+
+       public void testMacroFunction() {
+               HSSFWorkbook w = new HSSFWorkbook();
+               FormulaParser fp = new FormulaParser("FOO()", w);
                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 testLeadingSpaceInString()
-       {
+               // 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() {
+               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 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));
+               assertEquals(1, ptgs.length);
+               assertTrue("ptg0 is a StringPtg", ptgs[0] instanceof StringPtg);
+               assertTrue("ptg0 contains exact value", ((StringPtg)ptgs[0]).getValue().equals(value));
        }
-       
-       public void testLookupAndMatchFunctionArgs()
-       {
-               FormulaParser fp = new FormulaParser("lookup(A1, A3:A52, B3:B52)", null);
-               fp.parse();
-               Ptg[] ptg = fp.getRPNPtg();
 
-               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)");
+
+               assertEquals(4, ptgs.length);
+               assertTrue("ptg0 has Value class", ptgs[0].getPtgClass() == Ptg.CLASS_VALUE);
 
-               assertTrue("got 3 ptg", ptg.length == 3);
-               assertTrue("ptg0 has Value class", ptg[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));
+       }
+
+       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);
        }
 
-    // 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;
+       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.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(".1");
+               formula = cell.getCellFormula();
+               assertEquals("0.1", formula);
 
-        cell.setCellFormula("1322E21/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "1.322E24/3", formula);
+               cell.setCellFormula("+.1");
+               formula = cell.getCellFormula();
+               assertEquals("+0.1", formula);
 
-        cell.setCellFormula("-1322E21/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "-1.322E24/3", 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;
+       }
 
-        cell.setCellFormula("1.3E1/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "13.0/3", formula);
+       public void testParseNumber() {
+               IntPtg ip;
 
-        cell.setCellFormula("-1.3E1/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "-13.0/3", formula);
+               // bug 33160
+               ip = (IntPtg) parseSingleToken("40", IntPtg.class);
+               assertEquals(40, ip.getValue());
+               ip = (IntPtg) parseSingleToken("40000", IntPtg.class);
+               assertEquals(40000, ip.getValue());
 
-        cell.setCellFormula("1.3E-4/3");
-        formula = cell.getCellFormula();
-        assertEquals("Exponential formula string", "1.3E-4/3", formula);
+               // 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);
 
-        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());
-        }
-    }
+               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());
+               }
+       }
 }