]> source.dussan.org Git - poi.git/commitdiff
whitespace (tabs to spaces)
authorJaven O'Neal <onealj@apache.org>
Fri, 10 Jun 2016 02:01:15 +0000 (02:01 +0000)
committerJaven O'Neal <onealj@apache.org>
Fri, 10 Jun 2016 02:01:15 +0000 (02:01 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/xssf_structured_references@1747623 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaParser.java
src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java

index 563cfd54285da6d80becea20dc2c88ae01eb7c92..baf2b97d0cf659307af5a386806c1a898c99d8d9 100644 (file)
@@ -67,7 +67,7 @@ public final class HSSFFormulaParser {
      * @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid
      */
     public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType, int sheetIndex) throws FormulaParseException {
-        return FormulaParser.parse(formula, createParsingWorkbook(workbook), formulaType, sheetIndex, -1);
+        return FormulaParser.parse(formula, createParsingWorkbook(workbook), formulaType, sheetIndex);
     }
 
     /**
index a9f697ba9e51e71905bd6820f2c011acf60c3ca8..ac7f03a3801e0c3d65377b2bbed067ba8eecb9cb 100644 (file)
@@ -41,11 +41,11 @@ import org.junit.Test;
 import java.util.Arrays;
 
 public final class TestXSSFFormulaParser {
-       private static Ptg[] parse(FormulaParsingWorkbook fpb, String fmla) {
-               return FormulaParser.parse(fmla, fpb, FormulaType.CELL, -1, -1);
-       }
+    private static Ptg[] parse(FormulaParsingWorkbook fpb, String fmla) {
+        return FormulaParser.parse(fmla, fpb, FormulaType.CELL, -1, -1);
+    }
 
-       @Test
+    @Test
     public void basicParsing() {
         XSSFWorkbook wb = new XSSFWorkbook();
         XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
@@ -120,7 +120,7 @@ public final class TestXSSFFormulaParser {
         assertEquals("SUM",          ptgs[1].toFormulaString());
     }
 
-       @Test
+    @Test
     public void builtInFormulas() {
         XSSFWorkbook wb = new XSSFWorkbook();
         XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
@@ -155,7 +155,7 @@ public final class TestXSSFFormulaParser {
         assertEquals("[0]!NR_Global_B2",((NameXPxg)ptgs[0]).toFormulaString());
     }
    
-       @Test
+    @Test
     public void formulaReferencesOtherSheets() {
         // Use a test file with the named ranges in place
         XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("56737.xlsx");
index f99672b7cfff005a7f87eee7cb5d736cc5585357..7337a1ad8970e7d141246cd976464cb09232f198 100644 (file)
@@ -87,271 +87,271 @@ import org.junit.Test;
  */
 public final class TestFormulaParser {
 
-       /**
-        * @return parsed token array already confirmed not <code>null</code>
-        */
-       /* package */ static Ptg[] parseFormula(String formula) {
-               Ptg[] result = HSSFFormulaParser.parse(formula, (HSSFWorkbook)null);
-               assertNotNull("Ptg array should not be null", result);
-               return result;
-       }
-       private static String toFormulaString(Ptg[] ptgs) {
-               return HSSFFormulaParser.toFormulaString((HSSFWorkbook)null, ptgs);
-       }
-
-       @Test
-       public void testSimpleFormula() {
-               confirmTokenClasses("2+2",IntPtg.class, IntPtg.class, AddPtg.class);
-       }
-
-       @Test
-       public void testFormulaWithSpace1() {
-               confirmTokenClasses(" 2 + 2 ",IntPtg.class, IntPtg.class, AddPtg.class);
-       }
-
-       @Test
-       public void testFormulaWithSpace2() {
-               Ptg[] ptgs = parseFormula("2+ sum( 3 , 4) ");
-               assertEquals(5, ptgs.length);
-       }
-
-       @Test
-       public void testFormulaWithSpaceNRef() {
-               Ptg[] ptgs = parseFormula("sum( A2:A3 )");
-               assertEquals(2, ptgs.length);
-       }
-
-       @Test
-       public void testFormulaWithString() {
-               Ptg[] ptgs = parseFormula("\"hello\" & \"world\" ");
-               assertEquals(3, ptgs.length);
-       }
-
-       @Test
-       public void testTRUE() {
-               Ptg[] ptgs = parseFormula("TRUE");
-               assertEquals(1, ptgs.length);
-               BoolPtg flag  = (BoolPtg) ptgs[0];
-               assertEquals(true, flag.getValue());
-       }
-
-       @Test
-       public void testSumIf() {
-               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>
-        *
-        */
-       @Test
-       public void testNonAlphaFormula() {
-               Ptg[] ptgs = parseFormula("\"TOTAL[\"&F3&\"]\"");
-               confirmTokenClasses(ptgs, StringPtg.class, RefPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class);
-               assertEquals("TOTAL[", ((StringPtg)ptgs[0]).getValue());
-       }
-
-       @Test
-       public void testMacroFunction() throws IOException {
-               // testNames.xls contains a VB function called 'myFunc'
-               final String testFile = "testNames.xls";
-               HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(testFile);
-               try {
-                       HSSFEvaluationWorkbook book = HSSFEvaluationWorkbook.create(wb);
-
-                       //Expected ptg stack: [NamePtg(myFunc), StringPtg(arg), (additional operands go here...), FunctionPtg(myFunc)]
-                       Ptg[] ptg = FormulaParser.parse("myFunc(\"arg\")", book, FormulaType.CELL, -1);
-                       assertEquals(3, ptg.length); 
-
-                       // the name gets encoded as the first operand on the stack
-                       NamePtg tname = (NamePtg) ptg[0];
-                       assertEquals("myFunc", tname.toFormulaString(book));
-
-                       // the function's arguments are pushed onto the stack from left-to-right as OperandPtgs
-                       StringPtg arg = (StringPtg) ptg[1];
-                       assertEquals("arg", arg.getValue());
-
-                       // The external FunctionPtg is the last Ptg added to the stack
-                       // During formula evaluation, this Ptg pops off the the appropriate number of
-                       // arguments (getNumberOfOperands()) and pushes the result on the stack
-                       AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[2]; //FuncVarPtg
-                       assertTrue(tfunc.isExternalFunction());
-
-                       // confirm formula parsing is case-insensitive
-                       FormulaParser.parse("mYfUnC(\"arg\")", book, FormulaType.CELL, -1);
-
-                       // confirm formula parsing doesn't care about argument count or type
-                       // this should only throw an error when evaluating the formula.
-                       FormulaParser.parse("myFunc()", book, FormulaType.CELL, -1);
-                       FormulaParser.parse("myFunc(\"arg\", 0, TRUE)", book, FormulaType.CELL, -1);
-
-                       // A completely unknown formula name (not saved in workbook) should still be parseable and renderable
-                       // but will throw an NotImplementedFunctionException or return a #NAME? error value if evaluated.
-                       FormulaParser.parse("yourFunc(\"arg\")", book, FormulaType.CELL, -1);
-
-                       // Verify that myFunc and yourFunc were successfully added to Workbook names
-                       HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb);
-                       try {
-                           // HSSFWorkbook/EXCEL97-specific side-effects user-defined function names must be added to Workbook's defined names in order to be saved.
-                               assertNotNull(wb2.getName("myFunc"));
-                               assertEqualsIgnoreCase("myFunc", wb2.getName("myFunc").getNameName());
-                               assertNotNull(wb2.getName("yourFunc"));
-                               assertEqualsIgnoreCase("yourFunc", wb2.getName("yourFunc").getNameName());
-
-                               // Manually check to make sure file isn't corrupted
-                               // TODO: develop a process for occasionally manually reviewing workbooks
-                               // to verify workbooks are not corrupted
-                               /*
-                               final File fileIn = HSSFTestDataSamples.getSampleFile(testFile);
-                               final File reSavedFile = new File(fileIn.getParentFile(), fileIn.getName().replace(".xls", "-saved.xls"));
-                               FileOutputStream fos = new FileOutputStream(reSavedFile);
-                               wb2.write(fos);
-                               fos.close();
-                               */
-                       } finally {
-                               wb2.close();
-                       }
-               } finally {
-                       wb.close();
-               }
-       }
-       
-       private final static void assertEqualsIgnoreCase(String expected, String actual) {
-           assertEquals(expected.toLowerCase(Locale.ROOT), actual.toLowerCase(Locale.ROOT));
-       }
-
-       @Test
-       public void testEmbeddedSlash() {
-               confirmTokenClasses("HYPERLINK(\"http://www.jakarta.org\",\"Jakarta\")",
-                                               StringPtg.class, StringPtg.class, FuncVarPtg.class);
-       }
-
-       @Test
-       public void testConcatenate() {
-               confirmTokenClasses("CONCATENATE(\"first\",\"second\")",
-                               StringPtg.class, StringPtg.class, FuncVarPtg.class);
-       }
-
-       @Test
-       public void testWorksheetReferences() throws IOException {
-               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(0);
-               cell.setCellFormula("NoQuotesNeeded!A1");
-
-               cell = row.createCell(1);
-               cell.setCellFormula("'Quotes Needed Here &#$@'!A1");
-               
-               wb.close();
-       }
-
-       @Test
-       public void testUnaryMinus() {
-               confirmTokenClasses("-A1", RefPtg.class, UnaryMinusPtg.class);
-       }
-
-       @Test
-       public void testUnaryPlus() {
-               confirmTokenClasses("+A1", RefPtg.class, UnaryPlusPtg.class);
-       }
-
-       /**
-        * There may be multiple ways to encode an expression involving {@link UnaryPlusPtg}
-        * or {@link UnaryMinusPtg}.  These may be perfectly equivalent from a formula
-        * evaluation perspective, or formula rendering.  However, differences in the way
-        * POI encodes formulas may cause unnecessary confusion.  These non-critical tests
-        * check that POI follows the same encoding rules as Excel.
-        */
-       @Test
-       public void testExactEncodingOfUnaryPlusAndMinus() {
-               // as tested in Excel:
-               confirmUnary("-3", -3, NumberPtg.class);
-               confirmUnary("--4", -4, NumberPtg.class, UnaryMinusPtg.class);
-               confirmUnary("+++5", 5, IntPtg.class, UnaryPlusPtg.class, UnaryPlusPtg.class);
-               confirmUnary("++-6", -6, NumberPtg.class, UnaryPlusPtg.class, UnaryPlusPtg.class);
-
-               // Spaces muck things up a bit.  It would be clearer why the following cases are
-               // reasonable if POI encoded tAttrSpace in the right places.
-               // Otherwise these differences look capricious.
-               confirmUnary("+ 12", 12, IntPtg.class, UnaryPlusPtg.class);
-               confirmUnary("- 13", 13, IntPtg.class, UnaryMinusPtg.class);
-       }
-
-       private static void confirmUnary(String formulaText, double val, Class<?>...expectedTokenTypes) {
-               Ptg[] ptgs = parseFormula(formulaText);
-               confirmTokenClasses(ptgs, expectedTokenTypes);
-               Ptg ptg0 = ptgs[0];
-               if (ptg0 instanceof IntPtg) {
-                       IntPtg intPtg = (IntPtg) ptg0;
-                       assertEquals((int)val, intPtg.getValue());
-               } else if (ptg0 instanceof NumberPtg) {
-                       NumberPtg numberPtg = (NumberPtg) ptg0;
-                       assertEquals(val, numberPtg.getValue(), 0.0);
-               } else {
-                       fail("bad ptg0 " + ptg0);
-               }
-       }
-
-       @Test
-       public void testLeadingSpaceInString() {
-               String value = "  hi  ";
-               Ptg[] ptgs = parseFormula("\"" + value + "\"");
-               confirmTokenClasses(ptgs, StringPtg.class);
-               assertTrue("ptg0 contains exact value", ((StringPtg)ptgs[0]).getValue().equals(value));
-       }
-
-       @Test
-       public void testLookupAndMatchFunctionArgs() {
-               Ptg[] ptgs = parseFormula("lookup(A1, A3:A52, B3:B52)");
-               confirmTokenClasses(ptgs, RefPtg.class, AreaPtg.class, AreaPtg.class, FuncVarPtg.class);
-               assertTrue("ptg0 has Value class", ptgs[0].getPtgClass() == Ptg.CLASS_VALUE);
-
-               ptgs = parseFormula("match(A1, A3:A52)");
-               confirmTokenClasses(ptgs, RefPtg.class, AreaPtg.class, FuncVarPtg.class);
-               assertTrue("ptg0 has Value class", ptgs[0].getPtgClass() == Ptg.CLASS_VALUE);
-       }
-
-       /** bug 33160*/
-       @Test
-       public void testLargeInt() {
-               confirmTokenClasses("40", IntPtg.class);
-               confirmTokenClasses("40000", IntPtg.class);
-       }
-
-       /** bug 33160 */
-       @Test
-       public void testSimpleLongFormula() {
-               confirmTokenClasses("40000/2", IntPtg.class, IntPtg.class, DividePtg.class);
-       }
-
-       /** bug 35027, underscore in sheet name */
-       @Test
-       public void testUnderscore() throws IOException {
-               HSSFWorkbook wb = new HSSFWorkbook();
-
-               wb.createSheet("Cash_Flow");
-
-               HSSFSheet sheet = wb.createSheet("Test");
-               HSSFRow row = sheet.createRow(0);
-               HSSFCell cell;
-
-               cell = row.createCell(0);
-               cell.setCellFormula("Cash_Flow!A1");
-               
-               wb.close();
-       }
+    /**
+     * @return parsed token array already confirmed not <code>null</code>
+     */
+    /* package */ static Ptg[] parseFormula(String formula) {
+        Ptg[] result = HSSFFormulaParser.parse(formula, (HSSFWorkbook)null);
+        assertNotNull("Ptg array should not be null", result);
+        return result;
+    }
+    private static String toFormulaString(Ptg[] ptgs) {
+        return HSSFFormulaParser.toFormulaString((HSSFWorkbook)null, ptgs);
+    }
+
+    @Test
+    public void testSimpleFormula() {
+        confirmTokenClasses("2+2",IntPtg.class, IntPtg.class, AddPtg.class);
+    }
+
+    @Test
+    public void testFormulaWithSpace1() {
+        confirmTokenClasses(" 2 + 2 ",IntPtg.class, IntPtg.class, AddPtg.class);
+    }
+
+    @Test
+    public void testFormulaWithSpace2() {
+        Ptg[] ptgs = parseFormula("2+ sum( 3 , 4) ");
+        assertEquals(5, ptgs.length);
+    }
+
+    @Test
+    public void testFormulaWithSpaceNRef() {
+        Ptg[] ptgs = parseFormula("sum( A2:A3 )");
+        assertEquals(2, ptgs.length);
+    }
+
+    @Test
+    public void testFormulaWithString() {
+        Ptg[] ptgs = parseFormula("\"hello\" & \"world\" ");
+        assertEquals(3, ptgs.length);
+    }
+
+    @Test
+    public void testTRUE() {
+        Ptg[] ptgs = parseFormula("TRUE");
+        assertEquals(1, ptgs.length);
+        BoolPtg flag  = (BoolPtg) ptgs[0];
+        assertEquals(true, flag.getValue());
+    }
+
+    @Test
+    public void testSumIf() {
+        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>
+     *
+     */
+    @Test
+    public void testNonAlphaFormula() {
+        Ptg[] ptgs = parseFormula("\"TOTAL[\"&F3&\"]\"");
+        confirmTokenClasses(ptgs, StringPtg.class, RefPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class);
+        assertEquals("TOTAL[", ((StringPtg)ptgs[0]).getValue());
+    }
+
+    @Test
+    public void testMacroFunction() throws IOException {
+        // testNames.xls contains a VB function called 'myFunc'
+        final String testFile = "testNames.xls";
+        HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(testFile);
+        try {
+            HSSFEvaluationWorkbook book = HSSFEvaluationWorkbook.create(wb);
+
+            //Expected ptg stack: [NamePtg(myFunc), StringPtg(arg), (additional operands go here...), FunctionPtg(myFunc)]
+            Ptg[] ptg = FormulaParser.parse("myFunc(\"arg\")", book, FormulaType.CELL, -1);
+            assertEquals(3, ptg.length); 
+
+            // the name gets encoded as the first operand on the stack
+            NamePtg tname = (NamePtg) ptg[0];
+            assertEquals("myFunc", tname.toFormulaString(book));
+
+            // the function's arguments are pushed onto the stack from left-to-right as OperandPtgs
+            StringPtg arg = (StringPtg) ptg[1];
+            assertEquals("arg", arg.getValue());
+
+            // The external FunctionPtg is the last Ptg added to the stack
+            // During formula evaluation, this Ptg pops off the the appropriate number of
+            // arguments (getNumberOfOperands()) and pushes the result on the stack
+            AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[2]; //FuncVarPtg
+            assertTrue(tfunc.isExternalFunction());
+
+            // confirm formula parsing is case-insensitive
+            FormulaParser.parse("mYfUnC(\"arg\")", book, FormulaType.CELL, -1);
+
+            // confirm formula parsing doesn't care about argument count or type
+            // this should only throw an error when evaluating the formula.
+            FormulaParser.parse("myFunc()", book, FormulaType.CELL, -1);
+            FormulaParser.parse("myFunc(\"arg\", 0, TRUE)", book, FormulaType.CELL, -1);
+
+            // A completely unknown formula name (not saved in workbook) should still be parseable and renderable
+            // but will throw an NotImplementedFunctionException or return a #NAME? error value if evaluated.
+            FormulaParser.parse("yourFunc(\"arg\")", book, FormulaType.CELL, -1);
+
+            // Verify that myFunc and yourFunc were successfully added to Workbook names
+            HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb);
+            try {
+                // HSSFWorkbook/EXCEL97-specific side-effects user-defined function names must be added to Workbook's defined names in order to be saved.
+                assertNotNull(wb2.getName("myFunc"));
+                assertEqualsIgnoreCase("myFunc", wb2.getName("myFunc").getNameName());
+                assertNotNull(wb2.getName("yourFunc"));
+                assertEqualsIgnoreCase("yourFunc", wb2.getName("yourFunc").getNameName());
+
+                // Manually check to make sure file isn't corrupted
+                // TODO: develop a process for occasionally manually reviewing workbooks
+                // to verify workbooks are not corrupted
+                /*
+                final File fileIn = HSSFTestDataSamples.getSampleFile(testFile);
+                final File reSavedFile = new File(fileIn.getParentFile(), fileIn.getName().replace(".xls", "-saved.xls"));
+                FileOutputStream fos = new FileOutputStream(reSavedFile);
+                wb2.write(fos);
+                fos.close();
+                */
+            } finally {
+                wb2.close();
+            }
+        } finally {
+            wb.close();
+        }
+    }
+    
+    private final static void assertEqualsIgnoreCase(String expected, String actual) {
+        assertEquals(expected.toLowerCase(Locale.ROOT), actual.toLowerCase(Locale.ROOT));
+    }
+
+    @Test
+    public void testEmbeddedSlash() {
+        confirmTokenClasses("HYPERLINK(\"http://www.jakarta.org\",\"Jakarta\")",
+                        StringPtg.class, StringPtg.class, FuncVarPtg.class);
+    }
+
+    @Test
+    public void testConcatenate() {
+        confirmTokenClasses("CONCATENATE(\"first\",\"second\")",
+                StringPtg.class, StringPtg.class, FuncVarPtg.class);
+    }
+
+    @Test
+    public void testWorksheetReferences() throws IOException {
+        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(0);
+        cell.setCellFormula("NoQuotesNeeded!A1");
+
+        cell = row.createCell(1);
+        cell.setCellFormula("'Quotes Needed Here &#$@'!A1");
+        
+        wb.close();
+    }
+
+    @Test
+    public void testUnaryMinus() {
+        confirmTokenClasses("-A1", RefPtg.class, UnaryMinusPtg.class);
+    }
+
+    @Test
+    public void testUnaryPlus() {
+        confirmTokenClasses("+A1", RefPtg.class, UnaryPlusPtg.class);
+    }
+
+    /**
+     * There may be multiple ways to encode an expression involving {@link UnaryPlusPtg}
+     * or {@link UnaryMinusPtg}.  These may be perfectly equivalent from a formula
+     * evaluation perspective, or formula rendering.  However, differences in the way
+     * POI encodes formulas may cause unnecessary confusion.  These non-critical tests
+     * check that POI follows the same encoding rules as Excel.
+     */
+    @Test
+    public void testExactEncodingOfUnaryPlusAndMinus() {
+        // as tested in Excel:
+        confirmUnary("-3", -3, NumberPtg.class);
+        confirmUnary("--4", -4, NumberPtg.class, UnaryMinusPtg.class);
+        confirmUnary("+++5", 5, IntPtg.class, UnaryPlusPtg.class, UnaryPlusPtg.class);
+        confirmUnary("++-6", -6, NumberPtg.class, UnaryPlusPtg.class, UnaryPlusPtg.class);
+
+        // Spaces muck things up a bit.  It would be clearer why the following cases are
+        // reasonable if POI encoded tAttrSpace in the right places.
+        // Otherwise these differences look capricious.
+        confirmUnary("+ 12", 12, IntPtg.class, UnaryPlusPtg.class);
+        confirmUnary("- 13", 13, IntPtg.class, UnaryMinusPtg.class);
+    }
+
+    private static void confirmUnary(String formulaText, double val, Class<?>...expectedTokenTypes) {
+        Ptg[] ptgs = parseFormula(formulaText);
+        confirmTokenClasses(ptgs, expectedTokenTypes);
+        Ptg ptg0 = ptgs[0];
+        if (ptg0 instanceof IntPtg) {
+            IntPtg intPtg = (IntPtg) ptg0;
+            assertEquals((int)val, intPtg.getValue());
+        } else if (ptg0 instanceof NumberPtg) {
+            NumberPtg numberPtg = (NumberPtg) ptg0;
+            assertEquals(val, numberPtg.getValue(), 0.0);
+        } else {
+            fail("bad ptg0 " + ptg0);
+        }
+    }
+
+    @Test
+    public void testLeadingSpaceInString() {
+        String value = "  hi  ";
+        Ptg[] ptgs = parseFormula("\"" + value + "\"");
+        confirmTokenClasses(ptgs, StringPtg.class);
+        assertTrue("ptg0 contains exact value", ((StringPtg)ptgs[0]).getValue().equals(value));
+    }
+
+    @Test
+    public void testLookupAndMatchFunctionArgs() {
+        Ptg[] ptgs = parseFormula("lookup(A1, A3:A52, B3:B52)");
+        confirmTokenClasses(ptgs, RefPtg.class, AreaPtg.class, AreaPtg.class, FuncVarPtg.class);
+        assertTrue("ptg0 has Value class", ptgs[0].getPtgClass() == Ptg.CLASS_VALUE);
+
+        ptgs = parseFormula("match(A1, A3:A52)");
+        confirmTokenClasses(ptgs, RefPtg.class, AreaPtg.class, FuncVarPtg.class);
+        assertTrue("ptg0 has Value class", ptgs[0].getPtgClass() == Ptg.CLASS_VALUE);
+    }
+
+    /** bug 33160*/
+    @Test
+    public void testLargeInt() {
+        confirmTokenClasses("40", IntPtg.class);
+        confirmTokenClasses("40000", IntPtg.class);
+    }
+
+    /** bug 33160 */
+    @Test
+    public void testSimpleLongFormula() {
+        confirmTokenClasses("40000/2", IntPtg.class, IntPtg.class, DividePtg.class);
+    }
+
+    /** bug 35027, underscore in sheet name */
+    @Test
+    public void testUnderscore() throws IOException {
+        HSSFWorkbook wb = new HSSFWorkbook();
+
+        wb.createSheet("Cash_Flow");
+
+        HSSFSheet sheet = wb.createSheet("Test");
+        HSSFRow row = sheet.createRow(0);
+        HSSFCell cell;
+
+        cell = row.createCell(0);
+        cell.setCellFormula("Cash_Flow!A1");
+        
+        wb.close();
+    }
 
     /** bug 49725, defined names with underscore */
-       @Test
+    @Test
     public void testNamesWithUnderscore() throws IOException {
         HSSFWorkbook wb = new HSSFWorkbook(); //or new XSSFWorkbook();
         HSSFSheet sheet = wb.createSheet("NamesWithUnderscore");
@@ -398,162 +398,162 @@ public final class TestFormulaParser {
     }
 
     // bug 38396 : Formula with exponential numbers not parsed correctly.
-       @Test
-       public void testExponentialParsing() {
-               confirmTokenClasses("1.3E21/2",  NumberPtg.class, IntPtg.class, DividePtg.class);
-               confirmTokenClasses("1322E21/2", NumberPtg.class, IntPtg.class, DividePtg.class);
-               confirmTokenClasses("1.3E1/2",   NumberPtg.class, IntPtg.class, DividePtg.class);
-       }
+    @Test
+    public void testExponentialParsing() {
+        confirmTokenClasses("1.3E21/2",  NumberPtg.class, IntPtg.class, DividePtg.class);
+        confirmTokenClasses("1322E21/2", NumberPtg.class, IntPtg.class, DividePtg.class);
+        confirmTokenClasses("1.3E1/2",   NumberPtg.class, IntPtg.class, DividePtg.class);
+    }
 
-       @Test
-       public void testExponentialInSheet() throws IOException {
-               HSSFWorkbook wb = new HSSFWorkbook();
+    @Test
+    public void testExponentialInSheet() throws IOException {
+        HSSFWorkbook wb = new HSSFWorkbook();
 
-               wb.createSheet("Cash_Flow");
+        wb.createSheet("Cash_Flow");
 
-               HSSFSheet sheet = wb.createSheet("Test");
-               HSSFRow row = sheet.createRow(0);
-               HSSFCell cell = row.createCell(0);
-               String formula = null;
+        HSSFSheet sheet = wb.createSheet("Test");
+        HSSFRow row = sheet.createRow(0);
+        HSSFCell cell = row.createCell(0);
+        String formula = null;
 
-               cell.setCellFormula("1.3E21/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "1.3E+21/3", formula);
-
-               cell.setCellFormula("-1.3E21/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "-1.3E+21/3", formula);
-
-               cell.setCellFormula("1322E21/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "1.322E+24/3", formula);
+        cell.setCellFormula("1.3E21/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "1.3E+21/3", formula);
 
-               cell.setCellFormula("-1322E21/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "-1.322E+24/3", formula);
+        cell.setCellFormula("-1.3E21/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "-1.3E+21/3", formula);
 
-               cell.setCellFormula("1.3E1/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "13/3", formula);
+        cell.setCellFormula("1322E21/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "1.322E+24/3", formula);
 
-               cell.setCellFormula("-1.3E1/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "-13/3", formula);
+        cell.setCellFormula("-1322E21/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "-1.322E+24/3", formula);
 
-               cell.setCellFormula("1.3E-4/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "0.00013/3", formula);
-
-               cell.setCellFormula("-1.3E-4/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "-0.00013/3", formula);
-
-               cell.setCellFormula("13E-15/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "0.000000000000013/3", formula);
-
-               cell.setCellFormula("-13E-15/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "-0.000000000000013/3", formula);
-
-               cell.setCellFormula("1.3E3/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "1300/3", formula);
-
-               cell.setCellFormula("-1.3E3/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "-1300/3", formula);
-
-               cell.setCellFormula("1300000000000000/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "1300000000000000/3", formula);
-
-               cell.setCellFormula("-1300000000000000/3");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "-1300000000000000/3", formula);
-
-               cell.setCellFormula("-10E-1/3.1E2*4E3/3E4");
-               formula = cell.getCellFormula();
-               assertEquals("Exponential formula string", "-1/310*4000/30000", formula);
-               
-               wb.close();
-       }
-
-       @Test
-       public void testNumbers() throws IOException {
-               HSSFWorkbook wb = new HSSFWorkbook();
-
-               wb.createSheet("Cash_Flow");
-
-               HSSFSheet sheet = wb.createSheet("Test");
-               HSSFRow row = sheet.createRow(0);
-               HSSFCell cell = row.createCell(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);
+        cell.setCellFormula("1.3E1/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "13/3", formula);
 
-               // has exponent
-
-               cell.setCellFormula("10E1");
-               formula = cell.getCellFormula();
-               assertEquals("100", formula);
-
-               cell.setCellFormula("10E+1");
-               formula = cell.getCellFormula();
-               assertEquals("100", formula);
-
-               cell.setCellFormula("10E-1");
-               formula = cell.getCellFormula();
-               assertEquals("1", formula);
-               
-               wb.close();
-       }
-
-       @Test
-       public void testRanges() throws IOException {
-               HSSFWorkbook wb = new HSSFWorkbook();
-
-               wb.createSheet("Cash_Flow");
+        cell.setCellFormula("-1.3E1/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "-13/3", formula);
 
-               HSSFSheet sheet = wb.createSheet("Test");
-               HSSFRow row = sheet.createRow(0);
-               HSSFCell cell = row.createCell(0);
-               String formula = null;
+        cell.setCellFormula("1.3E-4/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "0.00013/3", formula);
 
-               cell.setCellFormula("A1.A2");
-               formula = cell.getCellFormula();
-               assertEquals("A1:A2", formula);
+        cell.setCellFormula("-1.3E-4/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "-0.00013/3", formula);
 
-               cell.setCellFormula("A1..A2");
-               formula = cell.getCellFormula();
-               assertEquals("A1:A2", formula);
+        cell.setCellFormula("13E-15/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "0.000000000000013/3", formula);
 
-               cell.setCellFormula("A1...A2");
-               formula = cell.getCellFormula();
-               assertEquals("A1:A2", formula);
-               
-               wb.close();
-       }
+        cell.setCellFormula("-13E-15/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "-0.000000000000013/3", formula);
+
+        cell.setCellFormula("1.3E3/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "1300/3", formula);
+
+        cell.setCellFormula("-1.3E3/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "-1300/3", formula);
+
+        cell.setCellFormula("1300000000000000/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "1300000000000000/3", formula);
 
-       @Test
-       public void testMultiSheetReference() throws IOException {
+        cell.setCellFormula("-1300000000000000/3");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "-1300000000000000/3", formula);
+
+        cell.setCellFormula("-10E-1/3.1E2*4E3/3E4");
+        formula = cell.getCellFormula();
+        assertEquals("Exponential formula string", "-1/310*4000/30000", formula);
+        
+        wb.close();
+    }
+
+    @Test
+    public void testNumbers() throws IOException {
+        HSSFWorkbook wb = new HSSFWorkbook();
+
+        wb.createSheet("Cash_Flow");
+
+        HSSFSheet sheet = wb.createSheet("Test");
+        HSSFRow row = sheet.createRow(0);
+        HSSFCell cell = row.createCell(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", formula);
+
+        cell.setCellFormula("10E+1");
+        formula = cell.getCellFormula();
+        assertEquals("100", formula);
+
+        cell.setCellFormula("10E-1");
+        formula = cell.getCellFormula();
+        assertEquals("1", formula);
+        
+        wb.close();
+    }
+
+    @Test
+    public void testRanges() throws IOException {
+        HSSFWorkbook wb = new HSSFWorkbook();
+
+        wb.createSheet("Cash_Flow");
+
+        HSSFSheet sheet = wb.createSheet("Test");
+        HSSFRow row = sheet.createRow(0);
+        HSSFCell cell = row.createCell(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);
+        
+        wb.close();
+    }
+
+    @Test
+    public void testMultiSheetReference() throws IOException {
         HSSFWorkbook wb = new HSSFWorkbook();
 
         wb.createSheet("Cash_Flow");
         wb.createSheet("Test Sheet");
-           
+        
         HSSFSheet sheet = wb.createSheet("Test");
         HSSFRow row = sheet.createRow(0);
         HSSFCell cell = row.createCell(0);
@@ -596,632 +596,632 @@ public final class TestFormulaParser {
         assertEquals("Cash_Flow:\'Test Sheet\'!A1:B2", formula);
         
         wb.close();
-       }
-       
-       /**
-        * 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
-        */
-       @Test
-       public void testToFormulaStringZeroArgFunction() throws IOException {
-               HSSFWorkbook book = new HSSFWorkbook();
-
-               Ptg[] ptgs = {
-                               FuncPtg.create(10),
-               };
-               assertEquals("NA()", HSSFFormulaParser.toFormulaString(book, ptgs));
-               
-               book.close();
-       }
-
-       @Test
-       public void testPercent() {
-
-               confirmTokenClasses("5%", IntPtg.class, PercentPtg.class);
-               // spaces OK
-               confirmTokenClasses(" 250 % ", IntPtg.class, PercentPtg.class);
-               // double percent OK
-               confirmTokenClasses("12345.678%%", NumberPtg.class, PercentPtg.class, PercentPtg.class);
-
-               // percent of a bracketed expression
-               confirmTokenClasses("(A1+35)%*B1%", RefPtg.class, IntPtg.class, AddPtg.class, ParenthesisPtg.class,
-                               PercentPtg.class, RefPtg.class, PercentPtg.class, MultiplyPtg.class);
-
-               // percent of a text quantity
-               confirmTokenClasses("\"8.75\"%", StringPtg.class, PercentPtg.class);
-
-               // percent to the power of
-               confirmTokenClasses("50%^3", IntPtg.class, PercentPtg.class, IntPtg.class, PowerPtg.class);
-
-               // things that parse OK but would *evaluate* to an error
-               confirmTokenClasses("\"abc\"%", StringPtg.class, PercentPtg.class);
-               confirmTokenClasses("#N/A%", ErrPtg.class, PercentPtg.class);
-       }
-
-       /**
-        * Tests combinations of various operators in the absence of brackets
-        */
-       @Test
-       public void testPrecedenceAndAssociativity() {
-
-               // TRUE=TRUE=2=2  evaluates to FALSE
-               confirmTokenClasses("TRUE=TRUE=2=2", BoolPtg.class, BoolPtg.class, EqualPtg.class,
-                               IntPtg.class, EqualPtg.class, IntPtg.class, EqualPtg.class);
-
-               //  2^3^2       evaluates to 64 not 512
-               confirmTokenClasses("2^3^2", IntPtg.class, IntPtg.class, PowerPtg.class,
-                               IntPtg.class, PowerPtg.class);
-
-               // "abc" & 2 + 3 & "def"   evaluates to "abc5def"
-               confirmTokenClasses("\"abc\"&2+3&\"def\"", StringPtg.class, IntPtg.class, IntPtg.class,
-                               AddPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class);
-
-               //  (1 / 2) - (3 * 4)
-               confirmTokenClasses("1/2-3*4", IntPtg.class, IntPtg.class, DividePtg.class,
-                               IntPtg.class, IntPtg.class, MultiplyPtg.class, SubtractPtg.class);
-
-               // 2 * (2^2)
-               // NOT: (2 *2) ^ 2 -> int int multiply int power
-               confirmTokenClasses("2*2^2", IntPtg.class, IntPtg.class, IntPtg.class, PowerPtg.class, MultiplyPtg.class);
-
-               //  2^200% -> 2 not 1.6E58
-               confirmTokenClasses("2^200%", IntPtg.class, IntPtg.class, PercentPtg.class, PowerPtg.class);
-       }
-
-       /* package */ static Ptg[] confirmTokenClasses(String formula, Class<?>...expectedClasses) {
-               Ptg[] ptgs = parseFormula(formula);
-               confirmTokenClasses(ptgs, expectedClasses);
-               return ptgs;
-       }
-
-       private static void confirmTokenClasses(Ptg[] ptgs, Class<?>...expectedClasses) {
-               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() + ")");
-                       }
-               }
-       }
-
-       @Test
-       public void testPower() {
-               confirmTokenClasses("2^5", IntPtg.class, IntPtg.class, PowerPtg.class);
-       }
-
-       private static Ptg parseSingleToken(String formula, Class<? extends Ptg> ptgClass) {
-               Ptg[] ptgs = parseFormula(formula);
-               assertEquals(1, ptgs.length);
-               Ptg result = ptgs[0];
-               assertEquals(ptgClass, result.getClass());
-               return result;
-       }
-
-       @Test
-       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() {
-
-               confirmTokenClasses("if(A1, ,C1)",
-                               RefPtg.class,
-                               AttrPtg.class, // tAttrIf
-                               MissingArgPtg.class,
-                               AttrPtg.class, // tAttrSkip
-                               RefPtg.class,
-                               AttrPtg.class, // tAttrSkip
-                               FuncVarPtg.class
-               );
-
-               confirmTokenClasses("counta( , A1:B2, )", MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class,
-                               FuncVarPtg.class);
-       }
-
-       @Test
-       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");
-               parseFormula("HLOOKUP(F7,#REF!,G7,#REF!)");
-       }
-
-       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());
-       }
-       
-       @Test
-       public void testParseStringLiterals_bug28754() throws IOException {
-
-               StringPtg sp;
-               try {
-                       sp = (StringPtg) parseSingleToken("\"test\"\"ing\"", StringPtg.class);
-               } catch (RuntimeException e) {
-                       if(e.getMessage().startsWith("Cannot Parse")) {
-                               fail("Identified bug 28754a");
-                       }
-                       throw e;
-               }
-               assertEquals("test\"ing", sp.getValue());
-
-               HSSFWorkbook wb = new HSSFWorkbook();
-               try {
-               HSSFSheet sheet = wb.createSheet();
-               wb.setSheetName(0, "Sheet1");
+    }
     
-               HSSFRow row = sheet.createRow(0);
-               HSSFCell cell = row.createCell(0);
-               cell.setCellFormula("right(\"test\"\"ing\", 3)");
-               String actualCellFormula = cell.getCellFormula();
-               if("RIGHT(\"test\"ing\",3)".equals(actualCellFormula)) {
-                       fail("Identified bug 28754b");
-               }
-               assertEquals("RIGHT(\"test\"\"ing\",3)", actualCellFormula);
-               } finally {
-                   wb.close();
-               }
-       }
-
-       @Test
-       public void testParseStringLiterals() {
-               confirmStringParse("goto considered harmful");
-
-               confirmStringParse("goto 'considered' harmful");
-
-               confirmStringParse("");
-               confirmStringParse("'");
-               confirmStringParse("''");
-               confirmStringParse("' '");
-               confirmStringParse(" ' ");
-       }
-
-       @Test
-       public void testParseSumIfSum() {
-               String formulaString;
-               Ptg[] ptgs;
-               ptgs = parseFormula("sum(5, 2, if(3>2, sum(A1:A2), 6))");
-               formulaString = toFormulaString(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 = toFormulaString(ptgs);
-               assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString);
-       }
-       
-       @Test
-       public void testParserErrors() {
-               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)");
-               
-               parseExpectedException("(");
-               parseExpectedException(")");
-               parseExpectedException("+");
-               parseExpectedException("42+");
-               
-               parseExpectedException("IF(");
-       }
-
-       private static void parseExpectedException(String formula) {
-               try {
-                       parseFormula(formula);
-                       fail("Expected FormulaParseException: " + formula);
-               } catch (FormulaParseException e) {
-                       // expected during successful test
-                       assertNotNull(e.getMessage());
-               }
-       }
-
-       @Test
-       public void testSetFormulaWithRowBeyond32768_Bug44539() throws IOException {
-
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet sheet = wb.createSheet();
-               wb.setSheetName(0, "Sheet1");
-
-               HSSFRow row = sheet.createRow(0);
-               HSSFCell cell = row.createCell(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());
-               
-               wb.close();
-       }
-
-       @Test
-       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 = toFormulaString(ptgs);
-               } catch (IllegalStateException e) {
-                       if(e.getMessage().equalsIgnoreCase("too much stuff left on the stack")) {
-                               fail("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, AddPtg.instance, };
-               formulaString = toFormulaString(ptgs);
-               assertEquals("3+4", formulaString);
-       }
-
-       /**
-        * Checks some internal error detecting logic ('stack underflow error' in toFormulaString)
-        */
-       @Test
-       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),
-                               DividePtg.instance,
-               };
-               try {
-                       toFormulaString(ptgs);
-                       fail("Expected exception was not thrown");
-               } catch (IllegalStateException e) {
-                       // expected during successful test
-                       assertTrue(e.getMessage().startsWith("Too few arguments supplied to operation"));
-               }
-       }
-       /**
-        * 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.
-        */
-       @Test
-       public void testFuncPtgSelection() {
-
-               Ptg[] ptgs = parseFormula("countif(A1:A2, 1)");
-               assertEquals(3, ptgs.length);
-               if(ptgs[2] instanceof FuncVarPtg) {
-                       fail("Identified bug 44675");
-               }
-               confirmTokenClasses(ptgs, AreaPtg.class, IntPtg.class, FuncPtg.class);
-
-               confirmTokenClasses("sin(1)", IntPtg.class, FuncPtg.class);
-       }
-
-       @Test
-       public void testWrongNumberOfFunctionArgs() throws IOException {
-               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) throws IOException {
-               HSSFWorkbook book = new HSSFWorkbook();
-               try {
-                       HSSFFormulaParser.parse(formula, book);
-                       fail("Didn't get parse exception as expected");
-               } catch (FormulaParseException e) {
-                       confirmParseException(e, expectedMessage);
-               }
-               book.close();
-       }
-
-       @Test
-       public void testParseErrorExpectedMsg() {
-
-               try {
-                       parseFormula("round(3.14;2)");
-                       fail("Didn't get parse exception as expected");
-               } catch (FormulaParseException e) {
-                       confirmParseException(e,
-                                       "Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'");
-               }
-
-               try {
-                       parseFormula(" =2+2");
-                       fail("Didn't get parse exception as expected");
-               } catch (FormulaParseException e) {
-                       confirmParseException(e,
-                                       "The specified formula ' =2+2' starts with an equals sign which is not allowed.");
-               }
-       }
-
-       /**
-        * this function name has a dot in it.
-        */
-       @Test
-       public void testParseErrorTypeFunction() {
-
-               Ptg[] ptgs;
-               try {
-                       ptgs = parseFormula("error.type(A1)");
-               } catch (IllegalArgumentException e) {
-                       if (e.getMessage().equals("Invalid Formula cell reference: 'error'")) {
-                               fail("Identified bug 45334");
-                       }
-                       throw e;
-               }
-               confirmTokenClasses(ptgs, RefPtg.class, FuncPtg.class);
-               assertEquals("ERROR.TYPE", ((FuncPtg) ptgs[1]).getName());
-       }
-
-       @Test
-       public void testNamedRangeThatLooksLikeCell() throws IOException {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet sheet = wb.createSheet("Sheet1");
-               HSSFName name = wb.createName();
-               name.setRefersToFormula("Sheet1!B1");
-               name.setNameName("pfy1");
-
-               Ptg[] ptgs;
-               try {
-                       ptgs = HSSFFormulaParser.parse("count(pfy1)", wb);
-               } catch (IllegalArgumentException e) {
-                       if (e.getMessage().equals("Specified colIx (1012) is out of range")) {
-                               fail("Identified bug 45354");
-                       }
-                       wb.close();
-                       throw e;
-               }
-               confirmTokenClasses(ptgs, NamePtg.class, FuncVarPtg.class);
-
-               HSSFCell cell = sheet.createRow(0).createCell(0);
-               cell.setCellFormula("count(pfy1)");
-               assertEquals("COUNT(pfy1)", cell.getCellFormula());
-               try {
-                       cell.setCellFormula("count(pf1)");
-                       fail("Expected formula parse execption");
-               } catch (FormulaParseException e) {
-                       confirmParseException(e,
-                                       "Specified named range 'pf1' does not exist in the current workbook.");
-               }
-               cell.setCellFormula("count(fp1)"); // plain cell ref, col is in range
-               wb.close();
-       }
-
-       @Test
-       public void testParseAreaRefHighRow_bug45358() throws IOException {
-               Ptg[] ptgs;
-               AreaI aptg;
-
-               HSSFWorkbook book = new HSSFWorkbook();
-               book.createSheet("Sheet1");
-
-               ptgs = HSSFFormulaParser.parse("Sheet1!A10:A40000", book);
-               aptg = (AreaI) ptgs[0];
-               if (aptg.getLastRow() == -25537) {
-                       fail("Identified bug 45358");
-               }
-               assertEquals(39999, aptg.getLastRow());
-
-               ptgs = HSSFFormulaParser.parse("Sheet1!A10:A65536", book);
-               aptg = (AreaI) ptgs[0];
-               assertEquals(65535, aptg.getLastRow());
-
-               // plain area refs should be ok too
-               ptgs = parseFormula("A10:A65536");
-               aptg = (AreaI) ptgs[0];
-               assertEquals(65535, aptg.getLastRow());
-
-               book.close();
-       }
-       
-       @Test
-       public void testParseArray()  {
-               Ptg[] ptgs;
-               ptgs = parseFormula("mode({1,2,2,#REF!;FALSE,3,3,2})");
-               confirmTokenClasses(ptgs, ArrayPtg.class, FuncVarPtg.class);
-               assertEquals("{1,2,2,#REF!;FALSE,3,3,2}", ptgs[0].toFormulaString());
-
-               ArrayPtg aptg = (ArrayPtg) ptgs[0];
-               Object[][] values = aptg.getTokenArrayValues();
-               assertEquals(ErrorConstant.valueOf(FormulaError.REF.getCode()), values[0][3]);
-               assertEquals(Boolean.FALSE, values[1][0]);
-       }
-
-       @Test
-       public void testParseStringElementInArray() {
-               Ptg[] ptgs;
-               ptgs = parseFormula("MAX({\"5\"},3)");
-               confirmTokenClasses(ptgs, ArrayPtg.class, IntPtg.class, FuncVarPtg.class);
-               Object element = ((ArrayPtg)ptgs[0]).getTokenArrayValues()[0][0];
-               if (element instanceof UnicodeString) {
-                       // this would cause ClassCastException below
-                       fail("Wrong encoding of array element value");
-               }
-               assertEquals(String.class, element.getClass());
-
-               // make sure the formula encodes OK
-               int encSize = Ptg.getEncodedSize(ptgs);
-               byte[] data = new byte[encSize];
-               Ptg.serializePtgs(ptgs, data, 0);
-               byte[] expData = HexRead.readFromString(
-                               "20 00 00 00 00 00 00 00 " // tArray
-                               + "1E 03 00 "      // tInt(3)
-                               + "42 02 07 00 "   // tFuncVar(MAX) 2-arg
-                               + "00 00 00 "      // Array data: 1 col, 1 row
-                               + "02 01 00 00 35" // elem (type=string, len=1, "5")
-               );
-               assertArrayEquals(expData, data);
-               int initSize = Ptg.getEncodedSizeWithoutArrayData(ptgs);
-               Ptg[] ptgs2 = Ptg.readTokens(initSize, new LittleEndianByteArrayInputStream(data));
-               confirmTokenClasses(ptgs2, ArrayPtg.class, IntPtg.class, FuncVarPtg.class);
-       }
-
-       @Test
-       public void testParseArrayNegativeElement() {
-               Ptg[] ptgs;
-               try {
-                       ptgs = parseFormula("{-42}");
-               } catch (FormulaParseException e) {
-                       if (e.getMessage().equals("Parse error near char 1 '-' in specified formula '{-42}'. Expected Integer")) {
-                               fail("Identified bug - failed to parse negative array element.");
-                       }
-                       throw e;
-               }
-               confirmTokenClasses(ptgs, ArrayPtg.class);
-               Object element = ((ArrayPtg)ptgs[0]).getTokenArrayValues()[0][0];
-
-               assertEquals(-42.0, ((Double)element).doubleValue(), 0.0);
-
-               // Should be able to handle whitespace between unary minus and digits (Excel
-               // accepts this formula after presenting the user with a confirmation dialog).
-               ptgs = parseFormula("{- 5}");
-               element = ((ArrayPtg)ptgs[0]).getTokenArrayValues()[0][0];
-               assertEquals(-5.0, ((Double)element).doubleValue(), 0.0);
-       }
-
-       @Test
-       public void testRangeOperator() throws IOException {
-
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet sheet = wb.createSheet();
-               HSSFCell cell = sheet.createRow(0).createCell(0);
-
-               wb.setSheetName(0, "Sheet1");
-               cell.setCellFormula("Sheet1!B$4:Sheet1!$C1"); // explicit range ':' operator
-               assertEquals("Sheet1!B$4:Sheet1!$C1", cell.getCellFormula());
-
-               cell.setCellFormula("Sheet1!B$4:$C1"); // plain area ref
-               assertEquals("Sheet1!B1:$C$4", cell.getCellFormula()); // note - area ref is normalised
-
-               cell.setCellFormula("Sheet1!$C1...B$4"); // different syntax for plain area ref
-               assertEquals("Sheet1!B1:$C$4", cell.getCellFormula());
-
-               // with funny sheet name
-               wb.setSheetName(0, "A1...A2");
-               cell.setCellFormula("A1...A2!B1");
-               assertEquals("A1...A2!B1", cell.getCellFormula());
-               
-               wb.close();
-       }
-
-       @Test
-       public void testBooleanNamedSheet() throws IOException {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               HSSFSheet sheet = wb.createSheet("true");
-               HSSFCell cell = sheet.createRow(0).createCell(0);
-               cell.setCellFormula("'true'!B2");
-
-               assertEquals("'true'!B2", cell.getCellFormula());
-               
-               wb.close();
-       }
-
-       @Test
-       public void testParseExternalWorkbookReference() throws IOException {
-               HSSFWorkbook wbA = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaA.xls");
-               HSSFCell cell = wbA.getSheetAt(0).getRow(0).getCell(0);
-
-               // make sure formula in sample is as expected
-               assertEquals("[multibookFormulaB.xls]BSheet1!B1", cell.getCellFormula());
-               Ptg[] expectedPtgs = FormulaExtractor.getPtgs(cell);
-               confirmSingle3DRef(expectedPtgs, 1);
-
-               // now try (re-)parsing the formula
-               Ptg[] actualPtgs = HSSFFormulaParser.parse("[multibookFormulaB.xls]BSheet1!B1", wbA);
-               confirmSingle3DRef(actualPtgs, 1); // externalSheetIndex 1 -> BSheet1
-
-               // try parsing a formula pointing to a different external sheet
-               Ptg[] otherPtgs = HSSFFormulaParser.parse("[multibookFormulaB.xls]AnotherSheet!B1", wbA);
-               confirmSingle3DRef(otherPtgs, 0); // externalSheetIndex 0 -> AnotherSheet
-
-               // try setting the same formula in a cell
-               cell.setCellFormula("[multibookFormulaB.xls]AnotherSheet!B1");
-               assertEquals("[multibookFormulaB.xls]AnotherSheet!B1", cell.getCellFormula());
-               
-               wbA.close();
-       }
-       
-       private static void confirmSingle3DRef(Ptg[] ptgs, int expectedExternSheetIndex) {
-               assertEquals(1, ptgs.length);
-               Ptg ptg0 = ptgs[0];
-               assertTrue(ptg0 instanceof Ref3DPtg);
-               assertEquals(expectedExternSheetIndex, ((Ref3DPtg)ptg0).getExternSheetIndex());
-       }
-
-       @Test
-       public void testUnion() throws IOException {
-               String formula = "Sheet1!$B$2:$C$3,OFFSET(Sheet1!$E$2:$E$4,1,Sheet1!$A$1),Sheet1!$D$6";
-               HSSFWorkbook wb = new HSSFWorkbook();
-               wb.createSheet("Sheet1");
-               Ptg[] ptgs = FormulaParser.parse(formula, HSSFEvaluationWorkbook.create(wb), FormulaType.CELL, -1,-1);
-
-               confirmTokenClasses(ptgs,
-                               // TODO - AttrPtg.class, // Excel prepends this
-                               MemFuncPtg.class,
-                               Area3DPtg.class,
-                               Area3DPtg.class,
-                               IntPtg.class,
-                               Ref3DPtg.class,
-                               FuncVarPtg.class,
-                               UnionPtg.class,
-                               Ref3DPtg.class,
-                               UnionPtg.class
-               );
-               MemFuncPtg mf = (MemFuncPtg)ptgs[0];
-               assertEquals(45, mf.getLenRefSubexpression());
+    /**
+     * 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
+     */
+    @Test
+    public void testToFormulaStringZeroArgFunction() throws IOException {
+        HSSFWorkbook book = new HSSFWorkbook();
+
+        Ptg[] ptgs = {
+                FuncPtg.create(10),
+        };
+        assertEquals("NA()", HSSFFormulaParser.toFormulaString(book, ptgs));
+        
+        book.close();
+    }
+
+    @Test
+    public void testPercent() {
+
+        confirmTokenClasses("5%", IntPtg.class, PercentPtg.class);
+        // spaces OK
+        confirmTokenClasses(" 250 % ", IntPtg.class, PercentPtg.class);
+        // double percent OK
+        confirmTokenClasses("12345.678%%", NumberPtg.class, PercentPtg.class, PercentPtg.class);
+
+        // percent of a bracketed expression
+        confirmTokenClasses("(A1+35)%*B1%", RefPtg.class, IntPtg.class, AddPtg.class, ParenthesisPtg.class,
+                PercentPtg.class, RefPtg.class, PercentPtg.class, MultiplyPtg.class);
+
+        // percent of a text quantity
+        confirmTokenClasses("\"8.75\"%", StringPtg.class, PercentPtg.class);
+
+        // percent to the power of
+        confirmTokenClasses("50%^3", IntPtg.class, PercentPtg.class, IntPtg.class, PowerPtg.class);
+
+        // things that parse OK but would *evaluate* to an error
+        confirmTokenClasses("\"abc\"%", StringPtg.class, PercentPtg.class);
+        confirmTokenClasses("#N/A%", ErrPtg.class, PercentPtg.class);
+    }
+
+    /**
+     * Tests combinations of various operators in the absence of brackets
+     */
+    @Test
+    public void testPrecedenceAndAssociativity() {
+
+        // TRUE=TRUE=2=2  evaluates to FALSE
+        confirmTokenClasses("TRUE=TRUE=2=2", BoolPtg.class, BoolPtg.class, EqualPtg.class,
+                IntPtg.class, EqualPtg.class, IntPtg.class, EqualPtg.class);
+
+        //  2^3^2    evaluates to 64 not 512
+        confirmTokenClasses("2^3^2", IntPtg.class, IntPtg.class, PowerPtg.class,
+                IntPtg.class, PowerPtg.class);
+
+        // "abc" & 2 + 3 & "def"   evaluates to "abc5def"
+        confirmTokenClasses("\"abc\"&2+3&\"def\"", StringPtg.class, IntPtg.class, IntPtg.class,
+                AddPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class);
+
+        //  (1 / 2) - (3 * 4)
+        confirmTokenClasses("1/2-3*4", IntPtg.class, IntPtg.class, DividePtg.class,
+                IntPtg.class, IntPtg.class, MultiplyPtg.class, SubtractPtg.class);
+
+        // 2 * (2^2)
+        // NOT: (2 *2) ^ 2 -> int int multiply int power
+        confirmTokenClasses("2*2^2", IntPtg.class, IntPtg.class, IntPtg.class, PowerPtg.class, MultiplyPtg.class);
+
+        //  2^200% -> 2 not 1.6E58
+        confirmTokenClasses("2^200%", IntPtg.class, IntPtg.class, PercentPtg.class, PowerPtg.class);
+    }
+
+    /* package */ static Ptg[] confirmTokenClasses(String formula, Class<?>...expectedClasses) {
+        Ptg[] ptgs = parseFormula(formula);
+        confirmTokenClasses(ptgs, expectedClasses);
+        return ptgs;
+    }
+
+    private static void confirmTokenClasses(Ptg[] ptgs, Class<?>...expectedClasses) {
+        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() + ")");
+            }
+        }
+    }
+
+    @Test
+    public void testPower() {
+        confirmTokenClasses("2^5", IntPtg.class, IntPtg.class, PowerPtg.class);
+    }
+
+    private static Ptg parseSingleToken(String formula, Class<? extends Ptg> ptgClass) {
+        Ptg[] ptgs = parseFormula(formula);
+        assertEquals(1, ptgs.length);
+        Ptg result = ptgs[0];
+        assertEquals(ptgClass, result.getClass());
+        return result;
+    }
+
+    @Test
+    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() {
+
+        confirmTokenClasses("if(A1, ,C1)",
+                RefPtg.class,
+                AttrPtg.class, // tAttrIf
+                MissingArgPtg.class,
+                AttrPtg.class, // tAttrSkip
+                RefPtg.class,
+                AttrPtg.class, // tAttrSkip
+                FuncVarPtg.class
+        );
+
+        confirmTokenClasses("counta( , A1:B2, )", MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class,
+                FuncVarPtg.class);
+    }
+
+    @Test
+    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");
+        parseFormula("HLOOKUP(F7,#REF!,G7,#REF!)");
+    }
+
+    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());
+    }
+    
+    @Test
+    public void testParseStringLiterals_bug28754() throws IOException {
+
+        StringPtg sp;
+        try {
+            sp = (StringPtg) parseSingleToken("\"test\"\"ing\"", StringPtg.class);
+        } catch (RuntimeException e) {
+            if(e.getMessage().startsWith("Cannot Parse")) {
+                fail("Identified bug 28754a");
+            }
+            throw e;
+        }
+        assertEquals("test\"ing", sp.getValue());
+
+        HSSFWorkbook wb = new HSSFWorkbook();
+        try {
+            HSSFSheet sheet = wb.createSheet();
+            wb.setSheetName(0, "Sheet1");
+    
+            HSSFRow row = sheet.createRow(0);
+            HSSFCell cell = row.createCell(0);
+            cell.setCellFormula("right(\"test\"\"ing\", 3)");
+            String actualCellFormula = cell.getCellFormula();
+            if("RIGHT(\"test\"ing\",3)".equals(actualCellFormula)) {
+                fail("Identified bug 28754b");
+            }
+            assertEquals("RIGHT(\"test\"\"ing\",3)", actualCellFormula);
+        } finally {
+            wb.close();
+        }
+    }
+
+    @Test
+    public void testParseStringLiterals() {
+        confirmStringParse("goto considered harmful");
+
+        confirmStringParse("goto 'considered' harmful");
+
+        confirmStringParse("");
+        confirmStringParse("'");
+        confirmStringParse("''");
+        confirmStringParse("' '");
+        confirmStringParse(" ' ");
+    }
+
+    @Test
+    public void testParseSumIfSum() {
+        String formulaString;
+        Ptg[] ptgs;
+        ptgs = parseFormula("sum(5, 2, if(3>2, sum(A1:A2), 6))");
+        formulaString = toFormulaString(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 = toFormulaString(ptgs);
+        assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString);
+    }
+    
+    @Test
+    public void testParserErrors() {
+        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)");
+        
+        parseExpectedException("(");
+        parseExpectedException(")");
+        parseExpectedException("+");
+        parseExpectedException("42+");
+        
+        parseExpectedException("IF(");
+    }
+
+    private static void parseExpectedException(String formula) {
+        try {
+            parseFormula(formula);
+            fail("Expected FormulaParseException: " + formula);
+        } catch (FormulaParseException e) {
+            // expected during successful test
+            assertNotNull(e.getMessage());
+        }
+    }
+
+    @Test
+    public void testSetFormulaWithRowBeyond32768_Bug44539() throws IOException {
+
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet();
+        wb.setSheetName(0, "Sheet1");
+
+        HSSFRow row = sheet.createRow(0);
+        HSSFCell cell = row.createCell(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());
+        
+        wb.close();
+    }
+
+    @Test
+    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 = toFormulaString(ptgs);
+        } catch (IllegalStateException e) {
+            if(e.getMessage().equalsIgnoreCase("too much stuff left on the stack")) {
+                fail("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, AddPtg.instance, };
+        formulaString = toFormulaString(ptgs);
+        assertEquals("3+4", formulaString);
+    }
+
+    /**
+     * Checks some internal error detecting logic ('stack underflow error' in toFormulaString)
+     */
+    @Test
+    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),
+                DividePtg.instance,
+        };
+        try {
+            toFormulaString(ptgs);
+            fail("Expected exception was not thrown");
+        } catch (IllegalStateException e) {
+            // expected during successful test
+            assertTrue(e.getMessage().startsWith("Too few arguments supplied to operation"));
+        }
+    }
+    /**
+     * 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.
+     */
+    @Test
+    public void testFuncPtgSelection() {
+
+        Ptg[] ptgs = parseFormula("countif(A1:A2, 1)");
+        assertEquals(3, ptgs.length);
+        if(ptgs[2] instanceof FuncVarPtg) {
+            fail("Identified bug 44675");
+        }
+        confirmTokenClasses(ptgs, AreaPtg.class, IntPtg.class, FuncPtg.class);
+
+        confirmTokenClasses("sin(1)", IntPtg.class, FuncPtg.class);
+    }
+
+    @Test
+    public void testWrongNumberOfFunctionArgs() throws IOException {
+        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) throws IOException {
+        HSSFWorkbook book = new HSSFWorkbook();
+        try {
+            HSSFFormulaParser.parse(formula, book);
+            fail("Didn't get parse exception as expected");
+        } catch (FormulaParseException e) {
+            confirmParseException(e, expectedMessage);
+        }
+        book.close();
+    }
+
+    @Test
+    public void testParseErrorExpectedMsg() {
+
+        try {
+            parseFormula("round(3.14;2)");
+            fail("Didn't get parse exception as expected");
+        } catch (FormulaParseException e) {
+            confirmParseException(e,
+                    "Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'");
+        }
+
+        try {
+            parseFormula(" =2+2");
+            fail("Didn't get parse exception as expected");
+        } catch (FormulaParseException e) {
+            confirmParseException(e,
+                    "The specified formula ' =2+2' starts with an equals sign which is not allowed.");
+        }
+    }
+
+    /**
+     * this function name has a dot in it.
+     */
+    @Test
+    public void testParseErrorTypeFunction() {
+
+        Ptg[] ptgs;
+        try {
+            ptgs = parseFormula("error.type(A1)");
+        } catch (IllegalArgumentException e) {
+            if (e.getMessage().equals("Invalid Formula cell reference: 'error'")) {
+                fail("Identified bug 45334");
+            }
+            throw e;
+        }
+        confirmTokenClasses(ptgs, RefPtg.class, FuncPtg.class);
+        assertEquals("ERROR.TYPE", ((FuncPtg) ptgs[1]).getName());
+    }
+
+    @Test
+    public void testNamedRangeThatLooksLikeCell() throws IOException {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet("Sheet1");
+        HSSFName name = wb.createName();
+        name.setRefersToFormula("Sheet1!B1");
+        name.setNameName("pfy1");
+
+        Ptg[] ptgs;
+        try {
+            ptgs = HSSFFormulaParser.parse("count(pfy1)", wb);
+        } catch (IllegalArgumentException e) {
+            if (e.getMessage().equals("Specified colIx (1012) is out of range")) {
+                fail("Identified bug 45354");
+            }
+            wb.close();
+            throw e;
+        }
+        confirmTokenClasses(ptgs, NamePtg.class, FuncVarPtg.class);
+
+        HSSFCell cell = sheet.createRow(0).createCell(0);
+        cell.setCellFormula("count(pfy1)");
+        assertEquals("COUNT(pfy1)", cell.getCellFormula());
+        try {
+            cell.setCellFormula("count(pf1)");
+            fail("Expected formula parse execption");
+        } catch (FormulaParseException e) {
+            confirmParseException(e,
+                    "Specified named range 'pf1' does not exist in the current workbook.");
+        }
+        cell.setCellFormula("count(fp1)"); // plain cell ref, col is in range
+        wb.close();
+    }
+
+    @Test
+    public void testParseAreaRefHighRow_bug45358() throws IOException {
+        Ptg[] ptgs;
+        AreaI aptg;
+
+        HSSFWorkbook book = new HSSFWorkbook();
+        book.createSheet("Sheet1");
+
+        ptgs = HSSFFormulaParser.parse("Sheet1!A10:A40000", book);
+        aptg = (AreaI) ptgs[0];
+        if (aptg.getLastRow() == -25537) {
+            fail("Identified bug 45358");
+        }
+        assertEquals(39999, aptg.getLastRow());
+
+        ptgs = HSSFFormulaParser.parse("Sheet1!A10:A65536", book);
+        aptg = (AreaI) ptgs[0];
+        assertEquals(65535, aptg.getLastRow());
+
+        // plain area refs should be ok too
+        ptgs = parseFormula("A10:A65536");
+        aptg = (AreaI) ptgs[0];
+        assertEquals(65535, aptg.getLastRow());
+
+        book.close();
+    }
+    
+    @Test
+    public void testParseArray()  {
+        Ptg[] ptgs;
+        ptgs = parseFormula("mode({1,2,2,#REF!;FALSE,3,3,2})");
+        confirmTokenClasses(ptgs, ArrayPtg.class, FuncVarPtg.class);
+        assertEquals("{1,2,2,#REF!;FALSE,3,3,2}", ptgs[0].toFormulaString());
+
+        ArrayPtg aptg = (ArrayPtg) ptgs[0];
+        Object[][] values = aptg.getTokenArrayValues();
+        assertEquals(ErrorConstant.valueOf(FormulaError.REF.getCode()), values[0][3]);
+        assertEquals(Boolean.FALSE, values[1][0]);
+    }
+
+    @Test
+    public void testParseStringElementInArray() {
+        Ptg[] ptgs;
+        ptgs = parseFormula("MAX({\"5\"},3)");
+        confirmTokenClasses(ptgs, ArrayPtg.class, IntPtg.class, FuncVarPtg.class);
+        Object element = ((ArrayPtg)ptgs[0]).getTokenArrayValues()[0][0];
+        if (element instanceof UnicodeString) {
+            // this would cause ClassCastException below
+            fail("Wrong encoding of array element value");
+        }
+        assertEquals(String.class, element.getClass());
+
+        // make sure the formula encodes OK
+        int encSize = Ptg.getEncodedSize(ptgs);
+        byte[] data = new byte[encSize];
+        Ptg.serializePtgs(ptgs, data, 0);
+        byte[] expData = HexRead.readFromString(
+                "20 00 00 00 00 00 00 00 " // tArray
+                + "1E 03 00 "      // tInt(3)
+                + "42 02 07 00 "   // tFuncVar(MAX) 2-arg
+                + "00 00 00 "      // Array data: 1 col, 1 row
+                + "02 01 00 00 35" // elem (type=string, len=1, "5")
+        );
+        assertArrayEquals(expData, data);
+        int initSize = Ptg.getEncodedSizeWithoutArrayData(ptgs);
+        Ptg[] ptgs2 = Ptg.readTokens(initSize, new LittleEndianByteArrayInputStream(data));
+        confirmTokenClasses(ptgs2, ArrayPtg.class, IntPtg.class, FuncVarPtg.class);
+    }
+
+    @Test
+    public void testParseArrayNegativeElement() {
+        Ptg[] ptgs;
+        try {
+            ptgs = parseFormula("{-42}");
+        } catch (FormulaParseException e) {
+            if (e.getMessage().equals("Parse error near char 1 '-' in specified formula '{-42}'. Expected Integer")) {
+                fail("Identified bug - failed to parse negative array element.");
+            }
+            throw e;
+        }
+        confirmTokenClasses(ptgs, ArrayPtg.class);
+        Object element = ((ArrayPtg)ptgs[0]).getTokenArrayValues()[0][0];
+
+        assertEquals(-42.0, ((Double)element).doubleValue(), 0.0);
+
+        // Should be able to handle whitespace between unary minus and digits (Excel
+        // accepts this formula after presenting the user with a confirmation dialog).
+        ptgs = parseFormula("{- 5}");
+        element = ((ArrayPtg)ptgs[0]).getTokenArrayValues()[0][0];
+        assertEquals(-5.0, ((Double)element).doubleValue(), 0.0);
+    }
+
+    @Test
+    public void testRangeOperator() throws IOException {
+
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet();
+        HSSFCell cell = sheet.createRow(0).createCell(0);
+
+        wb.setSheetName(0, "Sheet1");
+        cell.setCellFormula("Sheet1!B$4:Sheet1!$C1"); // explicit range ':' operator
+        assertEquals("Sheet1!B$4:Sheet1!$C1", cell.getCellFormula());
+
+        cell.setCellFormula("Sheet1!B$4:$C1"); // plain area ref
+        assertEquals("Sheet1!B1:$C$4", cell.getCellFormula()); // note - area ref is normalised
+
+        cell.setCellFormula("Sheet1!$C1...B$4"); // different syntax for plain area ref
+        assertEquals("Sheet1!B1:$C$4", cell.getCellFormula());
+
+        // with funny sheet name
+        wb.setSheetName(0, "A1...A2");
+        cell.setCellFormula("A1...A2!B1");
+        assertEquals("A1...A2!B1", cell.getCellFormula());
+        
+        wb.close();
+    }
+
+    @Test
+    public void testBooleanNamedSheet() throws IOException {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet sheet = wb.createSheet("true");
+        HSSFCell cell = sheet.createRow(0).createCell(0);
+        cell.setCellFormula("'true'!B2");
+
+        assertEquals("'true'!B2", cell.getCellFormula());
+        
+        wb.close();
+    }
+
+    @Test
+    public void testParseExternalWorkbookReference() throws IOException {
+        HSSFWorkbook wbA = HSSFTestDataSamples.openSampleWorkbook("multibookFormulaA.xls");
+        HSSFCell cell = wbA.getSheetAt(0).getRow(0).getCell(0);
+
+        // make sure formula in sample is as expected
+        assertEquals("[multibookFormulaB.xls]BSheet1!B1", cell.getCellFormula());
+        Ptg[] expectedPtgs = FormulaExtractor.getPtgs(cell);
+        confirmSingle3DRef(expectedPtgs, 1);
+
+        // now try (re-)parsing the formula
+        Ptg[] actualPtgs = HSSFFormulaParser.parse("[multibookFormulaB.xls]BSheet1!B1", wbA);
+        confirmSingle3DRef(actualPtgs, 1); // externalSheetIndex 1 -> BSheet1
+
+        // try parsing a formula pointing to a different external sheet
+        Ptg[] otherPtgs = HSSFFormulaParser.parse("[multibookFormulaB.xls]AnotherSheet!B1", wbA);
+        confirmSingle3DRef(otherPtgs, 0); // externalSheetIndex 0 -> AnotherSheet
+
+        // try setting the same formula in a cell
+        cell.setCellFormula("[multibookFormulaB.xls]AnotherSheet!B1");
+        assertEquals("[multibookFormulaB.xls]AnotherSheet!B1", cell.getCellFormula());
+        
+        wbA.close();
+    }
+    
+    private static void confirmSingle3DRef(Ptg[] ptgs, int expectedExternSheetIndex) {
+        assertEquals(1, ptgs.length);
+        Ptg ptg0 = ptgs[0];
+        assertTrue(ptg0 instanceof Ref3DPtg);
+        assertEquals(expectedExternSheetIndex, ((Ref3DPtg)ptg0).getExternSheetIndex());
+    }
+
+    @Test
+    public void testUnion() throws IOException {
+        String formula = "Sheet1!$B$2:$C$3,OFFSET(Sheet1!$E$2:$E$4,1,Sheet1!$A$1),Sheet1!$D$6";
+        HSSFWorkbook wb = new HSSFWorkbook();
+        wb.createSheet("Sheet1");
+        Ptg[] ptgs = FormulaParser.parse(formula, HSSFEvaluationWorkbook.create(wb), FormulaType.CELL, -1,-1);
+
+        confirmTokenClasses(ptgs,
+                // TODO - AttrPtg.class, // Excel prepends this
+                MemFuncPtg.class,
+                Area3DPtg.class,
+                Area3DPtg.class,
+                IntPtg.class,
+                Ref3DPtg.class,
+                FuncVarPtg.class,
+                UnionPtg.class,
+                Ref3DPtg.class,
+                UnionPtg.class
+        );
+        MemFuncPtg mf = (MemFuncPtg)ptgs[0];
+        assertEquals(45, mf.getLenRefSubexpression());
 
         // We don't check the type of the operands.
         confirmTokenClasses("1,2", MemAreaPtg.class, IntPtg.class, IntPtg.class, UnionPtg.class);
         
         wb.close();
-       }
+    }
 
-       @Test
-       public void testIntersection() throws IOException {
+    @Test
+    public void testIntersection() throws IOException {
        String formula = "Sheet1!$B$2:$C$3 OFFSET(Sheet1!$E$2:$E$4, 1,Sheet1!$A$1) Sheet1!$D$6";
         HSSFWorkbook wb = new HSSFWorkbook();
         wb.createSheet("Sheet1");
@@ -1246,30 +1246,30 @@ public final class TestFormulaParser {
         confirmTokenClasses("1 2", MemAreaPtg.class, IntPtg.class, IntPtg.class, IntersectionPtg.class);
         
         wb.close();
-       }
-       
-       @Test
-       public void testComparisonInParen() {
-           confirmTokenClasses("(A1 > B2)", 
+    }
+    
+    @Test
+    public void testComparisonInParen() {
+        confirmTokenClasses("(A1 > B2)", 
             RefPtg.class, 
             RefPtg.class, 
             GreaterThanPtg.class, 
             ParenthesisPtg.class
         );
-       }
-       
-       @Test
-       public void testUnionInParen() {
-           confirmTokenClasses("(A1:B2,B2:C3)", 
+    }
+    
+    @Test
+    public void testUnionInParen() {
+        confirmTokenClasses("(A1:B2,B2:C3)", 
           MemAreaPtg.class, 
           AreaPtg.class, 
           AreaPtg.class, 
           UnionPtg.class, 
           ParenthesisPtg.class
         );
-       }
+    }
 
-       @Test
+    @Test
     public void testIntersectionInParen() {
         confirmTokenClasses("(A1:B2 B2:C3)", 
             MemAreaPtg.class, 
@@ -1280,316 +1280,316 @@ public final class TestFormulaParser {
         );
     }
     
-       @Test
-       public void testRange_bug46643() throws IOException {
-               String formula = "Sheet1!A1:Sheet1!B3";
-               HSSFWorkbook wb = new HSSFWorkbook();
-               wb.createSheet("Sheet1");
-               Ptg[] ptgs = FormulaParser.parse(formula, HSSFEvaluationWorkbook.create(wb), FormulaType.CELL, -1, -1);
-
-               if (ptgs.length == 3) {
-                       confirmTokenClasses(ptgs, Ref3DPtg.class, Ref3DPtg.class, RangePtg.class);
-                       fail("Identified bug 46643");
-               }
-
-               confirmTokenClasses(ptgs,
-                               MemFuncPtg.class,
-                               Ref3DPtg.class,
-                               Ref3DPtg.class,
-                               RangePtg.class
-               );
-               MemFuncPtg mf = (MemFuncPtg)ptgs[0];
-               assertEquals(15, mf.getLenRefSubexpression());
-               wb.close();
-       }
-
-       /** Named ranges with backslashes, e.g. 'POI\\2009' */
-       @Test
-       public void testBackSlashInNames() throws IOException {
-               HSSFWorkbook wb = new HSSFWorkbook();
-
-               HSSFName name = wb.createName();
-               name.setNameName("POI\\2009");
-               name.setRefersToFormula("Sheet1!$A$1");
-
-               HSSFSheet sheet = wb.createSheet();
-               HSSFRow row = sheet.createRow(0);
-
-               HSSFCell cell_C1 =  row.createCell(2);
-               cell_C1.setCellFormula("POI\\2009");
-               assertEquals("POI\\2009", cell_C1.getCellFormula());
-
-               HSSFCell cell_D1 = row.createCell(2);
-               cell_D1.setCellFormula("NOT(POI\\2009=\"3.5-final\")");
-               assertEquals("NOT(POI\\2009=\"3.5-final\")", cell_D1.getCellFormula());
-               
-               wb.close();
-       }
-
-       /**
-        * TODO - delete equiv test:
-        * {@link BaseTestBugzillaIssues#test42448()}
-        */
-       @Test
-       public void testParseAbnormalSheetNamesAndRanges_bug42448() throws IOException {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               wb.createSheet("A");
-               try {
-                       HSSFFormulaParser.parse("SUM(A!C7:A!C67)", wb);
-               } catch (StringIndexOutOfBoundsException e) {
-                       fail("Identified bug 42448");
-               }
-               // the exact example from the bugzilla description:
-               HSSFFormulaParser.parse("SUMPRODUCT(A!C7:A!C67, B8:B68) / B69", wb);
-               
-               wb.close();
-       }
-
-       @Test
-       public void testRangeFuncOperand_bug46951() throws IOException {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               Ptg[] ptgs;
-               try {
-                       ptgs = HSSFFormulaParser.parse("SUM(C1:OFFSET(C1,0,B1))", wb);
-               } catch (RuntimeException e) {
-                       if (e.getMessage().equals("Specified named range 'OFFSET' does not exist in the current workbook.")) {
-                               fail("Identified bug 46951");
-                       }
-                       wb.close();
-                       throw e;
-               }
-               confirmTokenClasses(ptgs,
-                       MemFuncPtg.class, // [len=23]
-                       RefPtg.class, // [C1]
-                       RefPtg.class, // [C1]
-                       IntPtg.class, // [0]
-                       RefPtg.class, // [B1]
-                       FuncVarPtg.class, // [OFFSET nArgs=3]
-                       RangePtg.class, //
-                       AttrPtg.class // [sum ]
-               );
-               wb.close();
-       }
-
-       @Test
-       public void testUnionOfFullCollFullRowRef() throws IOException {
-               Ptg[] ptgs;
-               ptgs = parseFormula("3:4");
-               ptgs = parseFormula("$Z:$AC");
-               confirmTokenClasses(ptgs, AreaPtg.class);
-               ptgs = parseFormula("B:B");
-
-               ptgs = parseFormula("$11:$13");
-               confirmTokenClasses(ptgs, AreaPtg.class);
-
-               ptgs = parseFormula("$A:$A,$1:$4");
-               confirmTokenClasses(ptgs, MemAreaPtg.class,
-                               AreaPtg.class,
-                               AreaPtg.class,
-                               UnionPtg.class
-               );
-
-               HSSFWorkbook wb = new HSSFWorkbook();
-               wb.createSheet("Sheet1");
-               ptgs = HSSFFormulaParser.parse("Sheet1!$A:$A,Sheet1!$1:$4", wb);
-               confirmTokenClasses(ptgs, MemFuncPtg.class,
-                               Area3DPtg.class,
-                               Area3DPtg.class,
-                               UnionPtg.class
-               );
-
-               ptgs = HSSFFormulaParser.parse("'Sheet1'!$A:$A,'Sheet1'!$1:$4", wb);
-               confirmTokenClasses(ptgs,
-                               MemFuncPtg.class,
-                               Area3DPtg.class,
-                               Area3DPtg.class,
-                               UnionPtg.class
-               );
-               
-               wb.close();
-       }
-
-       @Test
-       public void testExplicitRangeWithTwoSheetNames() throws IOException {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               wb.createSheet("Sheet1");
-               Ptg[] ptgs = HSSFFormulaParser.parse("Sheet1!F1:Sheet1!G2", wb);
-               confirmTokenClasses(ptgs,
-                               MemFuncPtg.class,
-                               Ref3DPtg.class,
-                               Ref3DPtg.class,
-                               RangePtg.class
-               );
-               MemFuncPtg mf;
-               mf = (MemFuncPtg)ptgs[0];
-               assertEquals(15, mf.getLenRefSubexpression());
-               wb.close();
-       }
-
-       /**
-        * Checks that the area-ref and explicit range operators get the right associativity
-        * and that the {@link MemFuncPtg} / {@link MemAreaPtg} is added correctly
-        */
-       @Test
-       public void testComplexExplicitRangeEncodings() {
-
-               Ptg[] ptgs;
-               ptgs = parseFormula("SUM(OFFSET(A1,0,0):B2:C3:D4:E5:OFFSET(F6,1,1):G7)");
-               confirmTokenClasses(ptgs,
-                       // AttrPtg.class, // [volatile ] // POI doesn't do this yet (Apr 2009)
-                       MemFuncPtg.class, // len 57
-                       RefPtg.class, // [A1]
-                       IntPtg.class, // [0]
-                       IntPtg.class, // [0]
-                       FuncVarPtg.class, // [OFFSET nArgs=3]
-                       AreaPtg.class, // [B2:C3]
-                       RangePtg.class,
-                       AreaPtg.class, // [D4:E5]
-                       RangePtg.class,
-                       RefPtg.class, // [F6]
-                       IntPtg.class, // [1]
-                       IntPtg.class, // [1]
-                       FuncVarPtg.class, // [OFFSET nArgs=3]
-                       RangePtg.class,
-                       RefPtg.class, // [G7]
-                       RangePtg.class,
-                       AttrPtg.class // [sum ]
-               );
-
-               MemFuncPtg mf = (MemFuncPtg)ptgs[0];
-               assertEquals(57, mf.getLenRefSubexpression());
-               assertEquals("D4:E5", ((AreaPtgBase)ptgs[7]).toFormulaString());
-               assertTrue(((AttrPtg)ptgs[16]).isSum());
-
-               ptgs = parseFormula("SUM(A1:B2:C3:D4)");
-               confirmTokenClasses(ptgs,
-                       // AttrPtg.class, // [volatile ] // POI doesn't do this yet (Apr 2009)
-                               MemAreaPtg.class, // len 19
-                               AreaPtg.class, // [A1:B2]
-                               AreaPtg.class, // [C3:D4]
-                               RangePtg.class,
-                               AttrPtg.class // [sum ]
-               );
-               MemAreaPtg ma = (MemAreaPtg)ptgs[0];
-               assertEquals(19, ma.getLenRefSubexpression());
-       }
-
-
-       /**
-        * Mostly confirming that erroneous conditions are detected.  Actual error message wording is not critical.
-        *
-        */
-       @Test
-       public void testEdgeCaseParserErrors() throws IOException {
-               HSSFWorkbook wb = new HSSFWorkbook();
-               wb.createSheet("Sheet1");
-
-               confirmParseError(wb, "A1:ROUND(B1,1)", "The RHS of the range operator ':' at position 3 is not a proper reference.");
+    @Test
+    public void testRange_bug46643() throws IOException {
+        String formula = "Sheet1!A1:Sheet1!B3";
+        HSSFWorkbook wb = new HSSFWorkbook();
+        wb.createSheet("Sheet1");
+        Ptg[] ptgs = FormulaParser.parse(formula, HSSFEvaluationWorkbook.create(wb), FormulaType.CELL, -1, -1);
+
+        if (ptgs.length == 3) {
+            confirmTokenClasses(ptgs, Ref3DPtg.class, Ref3DPtg.class, RangePtg.class);
+            fail("Identified bug 46643");
+        }
+
+        confirmTokenClasses(ptgs,
+                MemFuncPtg.class,
+                Ref3DPtg.class,
+                Ref3DPtg.class,
+                RangePtg.class
+        );
+        MemFuncPtg mf = (MemFuncPtg)ptgs[0];
+        assertEquals(15, mf.getLenRefSubexpression());
+        wb.close();
+    }
+
+    /** Named ranges with backslashes, e.g. 'POI\\2009' */
+    @Test
+    public void testBackSlashInNames() throws IOException {
+        HSSFWorkbook wb = new HSSFWorkbook();
+
+        HSSFName name = wb.createName();
+        name.setNameName("POI\\2009");
+        name.setRefersToFormula("Sheet1!$A$1");
+
+        HSSFSheet sheet = wb.createSheet();
+        HSSFRow row = sheet.createRow(0);
+
+        HSSFCell cell_C1 =  row.createCell(2);
+        cell_C1.setCellFormula("POI\\2009");
+        assertEquals("POI\\2009", cell_C1.getCellFormula());
+
+        HSSFCell cell_D1 = row.createCell(2);
+        cell_D1.setCellFormula("NOT(POI\\2009=\"3.5-final\")");
+        assertEquals("NOT(POI\\2009=\"3.5-final\")", cell_D1.getCellFormula());
+        
+        wb.close();
+    }
+
+    /**
+     * TODO - delete equiv test:
+     * {@link BaseTestBugzillaIssues#test42448()}
+     */
+    @Test
+    public void testParseAbnormalSheetNamesAndRanges_bug42448() throws IOException {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        wb.createSheet("A");
+        try {
+            HSSFFormulaParser.parse("SUM(A!C7:A!C67)", wb);
+        } catch (StringIndexOutOfBoundsException e) {
+            fail("Identified bug 42448");
+        }
+        // the exact example from the bugzilla description:
+        HSSFFormulaParser.parse("SUMPRODUCT(A!C7:A!C67, B8:B68) / B69", wb);
+        
+        wb.close();
+    }
+
+    @Test
+    public void testRangeFuncOperand_bug46951() throws IOException {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        Ptg[] ptgs;
+        try {
+            ptgs = HSSFFormulaParser.parse("SUM(C1:OFFSET(C1,0,B1))", wb);
+        } catch (RuntimeException e) {
+            if (e.getMessage().equals("Specified named range 'OFFSET' does not exist in the current workbook.")) {
+                fail("Identified bug 46951");
+            }
+            wb.close();
+            throw e;
+        }
+        confirmTokenClasses(ptgs,
+            MemFuncPtg.class, // [len=23]
+            RefPtg.class, // [C1]
+            RefPtg.class, // [C1]
+            IntPtg.class, // [0]
+            RefPtg.class, // [B1]
+            FuncVarPtg.class, // [OFFSET nArgs=3]
+            RangePtg.class, //
+            AttrPtg.class // [sum ]
+        );
+        wb.close();
+    }
+
+    @Test
+    public void testUnionOfFullCollFullRowRef() throws IOException {
+        Ptg[] ptgs;
+        ptgs = parseFormula("3:4");
+        ptgs = parseFormula("$Z:$AC");
+        confirmTokenClasses(ptgs, AreaPtg.class);
+        ptgs = parseFormula("B:B");
+
+        ptgs = parseFormula("$11:$13");
+        confirmTokenClasses(ptgs, AreaPtg.class);
+
+        ptgs = parseFormula("$A:$A,$1:$4");
+        confirmTokenClasses(ptgs, MemAreaPtg.class,
+                AreaPtg.class,
+                AreaPtg.class,
+                UnionPtg.class
+        );
+
+        HSSFWorkbook wb = new HSSFWorkbook();
+        wb.createSheet("Sheet1");
+        ptgs = HSSFFormulaParser.parse("Sheet1!$A:$A,Sheet1!$1:$4", wb);
+        confirmTokenClasses(ptgs, MemFuncPtg.class,
+                Area3DPtg.class,
+                Area3DPtg.class,
+                UnionPtg.class
+        );
+
+        ptgs = HSSFFormulaParser.parse("'Sheet1'!$A:$A,'Sheet1'!$1:$4", wb);
+        confirmTokenClasses(ptgs,
+                MemFuncPtg.class,
+                Area3DPtg.class,
+                Area3DPtg.class,
+                UnionPtg.class
+        );
+        
+        wb.close();
+    }
+
+    @Test
+    public void testExplicitRangeWithTwoSheetNames() throws IOException {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        wb.createSheet("Sheet1");
+        Ptg[] ptgs = HSSFFormulaParser.parse("Sheet1!F1:Sheet1!G2", wb);
+        confirmTokenClasses(ptgs,
+                MemFuncPtg.class,
+                Ref3DPtg.class,
+                Ref3DPtg.class,
+                RangePtg.class
+        );
+        MemFuncPtg mf;
+        mf = (MemFuncPtg)ptgs[0];
+        assertEquals(15, mf.getLenRefSubexpression());
+        wb.close();
+    }
+
+    /**
+     * Checks that the area-ref and explicit range operators get the right associativity
+     * and that the {@link MemFuncPtg} / {@link MemAreaPtg} is added correctly
+     */
+    @Test
+    public void testComplexExplicitRangeEncodings() {
+
+        Ptg[] ptgs;
+        ptgs = parseFormula("SUM(OFFSET(A1,0,0):B2:C3:D4:E5:OFFSET(F6,1,1):G7)");
+        confirmTokenClasses(ptgs,
+            // AttrPtg.class, // [volatile ] // POI doesn't do this yet (Apr 2009)
+            MemFuncPtg.class, // len 57
+            RefPtg.class, // [A1]
+            IntPtg.class, // [0]
+            IntPtg.class, // [0]
+            FuncVarPtg.class, // [OFFSET nArgs=3]
+            AreaPtg.class, // [B2:C3]
+            RangePtg.class,
+            AreaPtg.class, // [D4:E5]
+            RangePtg.class,
+            RefPtg.class, // [F6]
+            IntPtg.class, // [1]
+            IntPtg.class, // [1]
+            FuncVarPtg.class, // [OFFSET nArgs=3]
+            RangePtg.class,
+            RefPtg.class, // [G7]
+            RangePtg.class,
+            AttrPtg.class // [sum ]
+        );
+
+        MemFuncPtg mf = (MemFuncPtg)ptgs[0];
+        assertEquals(57, mf.getLenRefSubexpression());
+        assertEquals("D4:E5", ((AreaPtgBase)ptgs[7]).toFormulaString());
+        assertTrue(((AttrPtg)ptgs[16]).isSum());
+
+        ptgs = parseFormula("SUM(A1:B2:C3:D4)");
+        confirmTokenClasses(ptgs,
+            // AttrPtg.class, // [volatile ] // POI doesn't do this yet (Apr 2009)
+                MemAreaPtg.class, // len 19
+                AreaPtg.class, // [A1:B2]
+                AreaPtg.class, // [C3:D4]
+                RangePtg.class,
+                AttrPtg.class // [sum ]
+        );
+        MemAreaPtg ma = (MemAreaPtg)ptgs[0];
+        assertEquals(19, ma.getLenRefSubexpression());
+    }
+
+
+    /**
+     * Mostly confirming that erroneous conditions are detected.  Actual error message wording is not critical.
+     *
+     */
+    @Test
+    public void testEdgeCaseParserErrors() throws IOException {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        wb.createSheet("Sheet1");
+
+        confirmParseError(wb, "A1:ROUND(B1,1)", "The RHS of the range operator ':' at position 3 is not a proper reference.");
 
         confirmParseError(wb, "Sheet1!!!", "Parse error near char 7 '!' in specified formula 'Sheet1!!!'. Expected number, string, defined name, or table");
         confirmParseError(wb, "Sheet1!.Name", "Parse error near char 7 '.' in specified formula 'Sheet1!.Name'. Expected number, string, defined name, or table");
-               confirmParseError(wb, "Sheet1!Sheet1", "Specified name 'Sheet1' for sheet Sheet1 not found");
-               confirmParseError(wb, "Sheet1!F:Sheet1!G", "'Sheet1!F' is not a proper reference.");
-               confirmParseError(wb, "Sheet1!F..foobar", "Complete area reference expected after sheet name at index 11.");
-               confirmParseError(wb, "Sheet1!A .. B", "Dotted range (full row or column) expression 'A .. B' must not contain whitespace.");
-               confirmParseError(wb, "Sheet1!A...B", "Dotted range (full row or column) expression 'A...B' must have exactly 2 dots.");
-               confirmParseError(wb, "Sheet1!A foobar", "Second part of cell reference expected after sheet name at index 10.");
-
-               confirmParseError(wb, "foobar", "Specified named range 'foobar' does not exist in the current workbook.");
-               confirmParseError(wb, "A1:1", "The RHS of the range operator ':' at position 3 is not a proper reference.");
-               wb.close();
-       }
-
-       private static void confirmParseError(HSSFWorkbook wb, String formula, String expectedMessage) {
-
-               try {
-                       HSSFFormulaParser.parse(formula, wb);
-                       fail("Expected formula parse execption");
-               } catch (FormulaParseException e) {
-                       confirmParseException(e, expectedMessage);
-               }
-       }
-
-       /**
-        * In bug 47078, POI had trouble evaluating a defined name flagged as 'complex'.
-        * POI should also be able to parse such defined names.
-        */
-       @Test
-       public void testParseComplexName() throws IOException {
-
-               // Mock up a spreadsheet to match the critical details of the sample
-               HSSFWorkbook wb = new HSSFWorkbook();
-               wb.createSheet("Sheet1");
-               HSSFName definedName = wb.createName();
-               definedName.setNameName("foo");
-               definedName.setRefersToFormula("Sheet1!B2");
-
-               // Set the complex flag - POI doesn't usually manipulate this flag
-               NameRecord nameRec = TestHSSFName.getNameRecord(definedName);
-               nameRec.setOptionFlag((short)0x10); // 0x10 -> complex
-
-               Ptg[] result;
-               try {
-                       result = HSSFFormulaParser.parse("1+foo", wb);
-               } catch (FormulaParseException e) {
-                       if (e.getMessage().equals("Specified name 'foo' is not a range as expected.")) {
-                               fail("Identified bug 47078c");
-                       }
-                       wb.close();
-                       throw e;
-               }
-               confirmTokenClasses(result, IntPtg.class, NamePtg.class, AddPtg.class);
-               
-               wb.close();
-       }
-
-       /**
-        * Zero is not a valid row number so cell references like 'A0' are not valid.
-        * Actually, they should be treated like defined names.
-        * <br/>
-        * In addition, leading zeros (on the row component) should be removed from cell
-        * references during parsing.
-        */
-       @Test
-       public void testZeroRowRefs() throws IOException {
-               String badCellRef = "B0"; // bad because zero is not a valid row number
-               String leadingZeroCellRef = "B000001"; // this should get parsed as "B1"
-               HSSFWorkbook wb = new HSSFWorkbook();
-
-               try {
-                       HSSFFormulaParser.parse(badCellRef, wb);
-                       fail("Identified bug 47312b - Shouldn't be able to parse cell ref '"
-                                       + badCellRef + "'.");
-               } catch (FormulaParseException e) {
-                       // expected during successful test
-                       confirmParseException(e, "Specified named range '"
-                                       + badCellRef + "' does not exist in the current workbook.");
-               }
-
-               Ptg[] ptgs;
-               try {
-                       ptgs = HSSFFormulaParser.parse(leadingZeroCellRef, wb);
-                       assertEquals("B1", ((RefPtg) ptgs[0]).toFormulaString());
-               } catch (FormulaParseException e) {
-                       confirmParseException(e, "Specified named range '"
-                                       + leadingZeroCellRef + "' does not exist in the current workbook.");
-                       // close but no cigar
-                       fail("Identified bug 47312c - '" + leadingZeroCellRef + "' should parse as 'B1'.");
-               }
-
-               // create a defined name called 'B0' and try again
-               Name n = wb.createName();
-               n.setNameName("B0");
-               n.setRefersToFormula("1+1");
-               ptgs = HSSFFormulaParser.parse("B0", wb);
-               confirmTokenClasses(ptgs, NamePtg.class);
-               
-               wb.close();
-       }
-
-       private static void confirmParseException(FormulaParseException e, String expMsg) {
-               assertEquals(expMsg, e.getMessage());
-       }
+        confirmParseError(wb, "Sheet1!Sheet1", "Specified name 'Sheet1' for sheet Sheet1 not found");
+        confirmParseError(wb, "Sheet1!F:Sheet1!G", "'Sheet1!F' is not a proper reference.");
+        confirmParseError(wb, "Sheet1!F..foobar", "Complete area reference expected after sheet name at index 11.");
+        confirmParseError(wb, "Sheet1!A .. B", "Dotted range (full row or column) expression 'A .. B' must not contain whitespace.");
+        confirmParseError(wb, "Sheet1!A...B", "Dotted range (full row or column) expression 'A...B' must have exactly 2 dots.");
+        confirmParseError(wb, "Sheet1!A foobar", "Second part of cell reference expected after sheet name at index 10.");
+
+        confirmParseError(wb, "foobar", "Specified named range 'foobar' does not exist in the current workbook.");
+        confirmParseError(wb, "A1:1", "The RHS of the range operator ':' at position 3 is not a proper reference.");
+        wb.close();
+    }
+
+    private static void confirmParseError(HSSFWorkbook wb, String formula, String expectedMessage) {
+
+        try {
+            HSSFFormulaParser.parse(formula, wb);
+            fail("Expected formula parse execption");
+        } catch (FormulaParseException e) {
+            confirmParseException(e, expectedMessage);
+        }
+    }
+
+    /**
+     * In bug 47078, POI had trouble evaluating a defined name flagged as 'complex'.
+     * POI should also be able to parse such defined names.
+     */
+    @Test
+    public void testParseComplexName() throws IOException {
+
+        // Mock up a spreadsheet to match the critical details of the sample
+        HSSFWorkbook wb = new HSSFWorkbook();
+        wb.createSheet("Sheet1");
+        HSSFName definedName = wb.createName();
+        definedName.setNameName("foo");
+        definedName.setRefersToFormula("Sheet1!B2");
+
+        // Set the complex flag - POI doesn't usually manipulate this flag
+        NameRecord nameRec = TestHSSFName.getNameRecord(definedName);
+        nameRec.setOptionFlag((short)0x10); // 0x10 -> complex
+
+        Ptg[] result;
+        try {
+            result = HSSFFormulaParser.parse("1+foo", wb);
+        } catch (FormulaParseException e) {
+            if (e.getMessage().equals("Specified name 'foo' is not a range as expected.")) {
+                fail("Identified bug 47078c");
+            }
+            wb.close();
+            throw e;
+        }
+        confirmTokenClasses(result, IntPtg.class, NamePtg.class, AddPtg.class);
+        
+        wb.close();
+    }
+
+    /**
+     * Zero is not a valid row number so cell references like 'A0' are not valid.
+     * Actually, they should be treated like defined names.
+     * <br/>
+     * In addition, leading zeros (on the row component) should be removed from cell
+     * references during parsing.
+     */
+    @Test
+    public void testZeroRowRefs() throws IOException {
+        String badCellRef = "B0"; // bad because zero is not a valid row number
+        String leadingZeroCellRef = "B000001"; // this should get parsed as "B1"
+        HSSFWorkbook wb = new HSSFWorkbook();
+
+        try {
+            HSSFFormulaParser.parse(badCellRef, wb);
+            fail("Identified bug 47312b - Shouldn't be able to parse cell ref '"
+                    + badCellRef + "'.");
+        } catch (FormulaParseException e) {
+            // expected during successful test
+            confirmParseException(e, "Specified named range '"
+                    + badCellRef + "' does not exist in the current workbook.");
+        }
+
+        Ptg[] ptgs;
+        try {
+            ptgs = HSSFFormulaParser.parse(leadingZeroCellRef, wb);
+            assertEquals("B1", ((RefPtg) ptgs[0]).toFormulaString());
+        } catch (FormulaParseException e) {
+            confirmParseException(e, "Specified named range '"
+                    + leadingZeroCellRef + "' does not exist in the current workbook.");
+            // close but no cigar
+            fail("Identified bug 47312c - '" + leadingZeroCellRef + "' should parse as 'B1'.");
+        }
+
+        // create a defined name called 'B0' and try again
+        Name n = wb.createName();
+        n.setNameName("B0");
+        n.setRefersToFormula("1+1");
+        ptgs = HSSFFormulaParser.parse("B0", wb);
+        confirmTokenClasses(ptgs, NamePtg.class);
+        
+        wb.close();
+    }
+
+    private static void confirmParseException(FormulaParseException e, String expMsg) {
+        assertEquals(expMsg, e.getMessage());
+    }
 
     @Test
     public void test57196_Formula() throws IOException {