You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TestFormulaParser.java 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*
  2. * ====================================================================
  3. * Licensed to the Apache Software Foundation (ASF) under one or more
  4. * contributor license agreements. See the NOTICE file distributed with
  5. * this work for additional information regarding copyright ownership.
  6. * The ASF licenses this file to You under the Apache License, Version 2.0
  7. * (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. * ====================================================================
  18. */
  19. package org.apache.poi.ss.formula;
  20. import static org.junit.Assert.assertEquals;
  21. import static org.junit.Assert.assertNotNull;
  22. import static org.junit.Assert.assertTrue;
  23. import static org.junit.Assert.fail;
  24. import java.io.IOException;
  25. import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
  26. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  27. import org.apache.poi.ss.formula.ptg.AbstractFunctionPtg;
  28. import org.apache.poi.ss.formula.ptg.NameXPxg;
  29. import org.apache.poi.ss.formula.ptg.Ptg;
  30. import org.apache.poi.ss.formula.ptg.Ref3DPxg;
  31. import org.apache.poi.ss.formula.ptg.StringPtg;
  32. import org.apache.poi.util.IOUtils;
  33. import org.apache.poi.xssf.XSSFTestDataSamples;
  34. import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook;
  35. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  36. import org.junit.Test;
  37. /**
  38. * Test {@link FormulaParser}'s handling of row numbers at the edge of the
  39. * HSSF/XSSF ranges.
  40. *
  41. * @author David North
  42. */
  43. public class TestFormulaParser {
  44. @Test
  45. public void testHSSFFailsForOver65536() {
  46. FormulaParsingWorkbook workbook = HSSFEvaluationWorkbook.create(new HSSFWorkbook());
  47. try {
  48. FormulaParser.parse("Sheet1!1:65537", workbook, FormulaType.CELL, 0);
  49. fail("Expected exception");
  50. }
  51. catch (FormulaParseException expected) {
  52. // expected here
  53. }
  54. }
  55. private static void checkHSSFFormula(String formula) {
  56. HSSFWorkbook wb = new HSSFWorkbook();
  57. FormulaParsingWorkbook workbook = HSSFEvaluationWorkbook.create(wb);
  58. FormulaParser.parse(formula, workbook, FormulaType.CELL, 0);
  59. IOUtils.closeQuietly(wb);
  60. }
  61. private static void checkXSSFFormula(String formula) {
  62. XSSFWorkbook wb = new XSSFWorkbook();
  63. FormulaParsingWorkbook workbook = XSSFEvaluationWorkbook.create(wb);
  64. FormulaParser.parse(formula, workbook, FormulaType.CELL, 0);
  65. IOUtils.closeQuietly(wb);
  66. }
  67. private static void checkFormula(String formula) {
  68. checkHSSFFormula(formula);
  69. checkXSSFFormula(formula);
  70. }
  71. @Test
  72. public void testHSSFPassCase() {
  73. checkHSSFFormula("Sheet1!1:65536");
  74. }
  75. @Test
  76. public void testXSSFWorksForOver65536() {
  77. checkXSSFFormula("Sheet1!1:65537");
  78. }
  79. @Test
  80. public void testXSSFFailCase() {
  81. FormulaParsingWorkbook workbook = XSSFEvaluationWorkbook.create(new XSSFWorkbook());
  82. try {
  83. FormulaParser.parse("Sheet1!1:1048577", workbook, FormulaType.CELL, 0); // one more than max rows.
  84. fail("Expected exception");
  85. }
  86. catch (FormulaParseException expected) {
  87. // expected here
  88. }
  89. }
  90. // copied from org.apache.poi.hssf.model.TestFormulaParser
  91. @Test
  92. public void testMacroFunction() throws Exception {
  93. // testNames.xlsm contains a VB function called 'myFunc'
  94. final String testFile = "testNames.xlsm";
  95. XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook(testFile);
  96. try {
  97. XSSFEvaluationWorkbook workbook = XSSFEvaluationWorkbook.create(wb);
  98. //Expected ptg stack: [NamePtg(myFunc), StringPtg(arg), (additional operands would go here...), FunctionPtg(myFunc)]
  99. Ptg[] ptg = FormulaParser.parse("myFunc(\"arg\")", workbook, FormulaType.CELL, -1);
  100. assertEquals(3, ptg.length);
  101. // the name gets encoded as the first operand on the stack
  102. NameXPxg tname = (NameXPxg) ptg[0];
  103. assertEquals("myFunc", tname.toFormulaString());
  104. // the function's arguments are pushed onto the stack from left-to-right as OperandPtgs
  105. StringPtg arg = (StringPtg) ptg[1];
  106. assertEquals("arg", arg.getValue());
  107. // The external FunctionPtg is the last Ptg added to the stack
  108. // During formula evaluation, this Ptg pops off the the appropriate number of
  109. // arguments (getNumberOfOperands()) and pushes the result on the stack
  110. AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[2];
  111. assertTrue(tfunc.isExternalFunction());
  112. // confirm formula parsing is case-insensitive
  113. FormulaParser.parse("mYfUnC(\"arg\")", workbook, FormulaType.CELL, -1);
  114. // confirm formula parsing doesn't care about argument count or type
  115. // this should only throw an error when evaluating the formula.
  116. FormulaParser.parse("myFunc()", workbook, FormulaType.CELL, -1);
  117. FormulaParser.parse("myFunc(\"arg\", 0, TRUE)", workbook, FormulaType.CELL, -1);
  118. // A completely unknown formula name (not saved in workbook) should still be parseable and renderable
  119. // but will throw an NotImplementedFunctionException or return a #NAME? error value if evaluated.
  120. FormulaParser.parse("yourFunc(\"arg\")", workbook, FormulaType.CELL, -1);
  121. // Make sure workbook can be written and read
  122. XSSFTestDataSamples.writeOutAndReadBack(wb).close();
  123. // Manually check to make sure file isn't corrupted
  124. // TODO: develop a process for occasionally manually reviewing workbooks
  125. // to verify workbooks are not corrupted
  126. /*
  127. final File fileIn = XSSFTestDataSamples.getSampleFile(testFile);
  128. final File reSavedFile = new File(fileIn.getParentFile(), fileIn.getName().replace(".xlsm", "-saved.xlsm"));
  129. final FileOutputStream fos = new FileOutputStream(reSavedFile);
  130. wb.write(fos);
  131. fos.close();
  132. */
  133. } finally {
  134. wb.close();
  135. }
  136. }
  137. @Test
  138. public void testParserErrors() throws Exception {
  139. XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("testNames.xlsm");
  140. try {
  141. XSSFEvaluationWorkbook workbook = XSSFEvaluationWorkbook.create(wb);
  142. parseExpectedException("(");
  143. parseExpectedException(")");
  144. parseExpectedException("+");
  145. parseExpectedException("42+");
  146. parseExpectedException("IF()");
  147. parseExpectedException("IF("); //no closing paren
  148. parseExpectedException("myFunc(", workbook); //no closing paren
  149. } finally {
  150. wb.close();
  151. }
  152. }
  153. private static void parseExpectedException(String formula) {
  154. parseExpectedException(formula, null);
  155. }
  156. /** confirm formula has invalid syntax and parsing the formula results in FormulaParseException
  157. */
  158. private static void parseExpectedException(String formula, FormulaParsingWorkbook wb) {
  159. try {
  160. FormulaParser.parse(formula, wb, FormulaType.CELL, -1);
  161. fail("Expected FormulaParseException: " + formula);
  162. } catch (final FormulaParseException e) {
  163. // expected during successful test
  164. assertNotNull(e.getMessage());
  165. }
  166. }
  167. // trivial case for bug 60219: FormulaParser can't parse external references when sheet name is quoted
  168. @Test
  169. public void testParseExternalReferencesWithUnquotedSheetName() throws Exception {
  170. XSSFWorkbook wb = new XSSFWorkbook();
  171. XSSFEvaluationWorkbook fpwb = XSSFEvaluationWorkbook.create(wb);
  172. Ptg[] ptgs = FormulaParser.parse("[1]Sheet1!A1", fpwb, FormulaType.CELL, -1);
  173. // org.apache.poi.ss.formula.ptg.Ref3DPxg [ [workbook=1] sheet=Sheet 1 ! A1]
  174. assertEquals("Ptgs length", 1, ptgs.length);
  175. assertTrue("Ptg class", ptgs[0] instanceof Ref3DPxg);
  176. Ref3DPxg pxg = (Ref3DPxg) ptgs[0];
  177. assertEquals("External workbook number", 1, pxg.getExternalWorkbookNumber());
  178. assertEquals("Sheet name", "Sheet1", pxg.getSheetName());
  179. assertEquals("Row", 0, pxg.getRow());
  180. assertEquals("Column", 0, pxg.getColumn());
  181. wb.close();
  182. }
  183. // bug 60219: FormulaParser can't parse external references when sheet name is quoted
  184. @Test
  185. public void testParseExternalReferencesWithQuotedSheetName() throws Exception {
  186. XSSFWorkbook wb = new XSSFWorkbook();
  187. XSSFEvaluationWorkbook fpwb = XSSFEvaluationWorkbook.create(wb);
  188. Ptg[] ptgs = FormulaParser.parse("'[1]Sheet 1'!A1", fpwb, FormulaType.CELL, -1);
  189. // org.apache.poi.ss.formula.ptg.Ref3DPxg [ [workbook=1] sheet=Sheet 1 ! A1]
  190. assertEquals("Ptgs length", 1, ptgs.length);
  191. assertTrue("Ptg class", ptgs[0] instanceof Ref3DPxg);
  192. Ref3DPxg pxg = (Ref3DPxg) ptgs[0];
  193. assertEquals("External workbook number", 1, pxg.getExternalWorkbookNumber());
  194. assertEquals("Sheet name", "Sheet 1", pxg.getSheetName());
  195. assertEquals("Row", 0, pxg.getRow());
  196. assertEquals("Column", 0, pxg.getColumn());
  197. wb.close();
  198. }
  199. // bug 60260
  200. @Test
  201. public void testUnicodeSheetName() {
  202. checkFormula("'Sheet\u30FB1'!A1:A6");
  203. }
  204. }