diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 86ab48719..0f17e7cc3 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -36,6 +36,8 @@ + 43410 - [PATCH] - Improved Formula Parser support for numbers and ranges + When writing HSLF files out, optionally preserve all OLE2 nodes (default is just the HSLF related nodes) 43323 - [PATCH] - Support for adding Pictures to ShapeGroups in HSLF. 43222 - [PATCH] - Support for getting OLE object data from HSSFWorkbook. 43247 - [PATCH] - Support for getting OLE object data from slideshows. diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index a5ab0905b..9a3a8d6fa 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -33,6 +33,8 @@ + 43410 - [PATCH] - Improved Formula Parser support for numbers and ranges + When writing HSLF files out, optionally preserve all OLE2 nodes (default is just the HSLF related nodes) 43323 - [PATCH] - Support for adding Pictures to ShapeGroups in HSLF. 43222 - [PATCH] - Support for getting OLE object data from HSSFWorkbook. 43247 - [PATCH] - Support for getting OLE object data from slideshows. diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java index ffecb6daf..af3382f15 100644 --- a/src/java/org/apache/poi/hssf/model/FormulaParser.java +++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java @@ -45,6 +45,7 @@ import org.apache.poi.hssf.record.formula.*; * @author Eric Ladner (eladner at goldinc dot com) * @author Cameron Riley (criley at ekmail.com) * @author Peter M. Murray (pete at quantrix dot com) + * @author Pavel Krupets (pkrupets at palmtreebusiness dot com) */ public class FormulaParser { @@ -230,36 +231,21 @@ public class FormulaParser { } - /** Get the exponent for numbers of form 1.3E21 */ - private String GetExponent() { - StringBuffer retval = new StringBuffer(); - String sign = ""; - GetChar(); - if ('-' == look) { - sign = "-"; - GetChar(); - } - while (IsDigit(look)) { - retval.append(look); - GetChar(); - } - if (retval.length() > 0) { - retval.insert(0, sign); - } - return retval.toString(); - } - /** Get a Number */ private String GetNum() { StringBuffer value = new StringBuffer(); - if (!IsDigit(look)) Expected("Integer"); - while (IsDigit(look)){ - value.append(look); + + while (IsDigit(this.look)){ + value.append(this.look); GetChar(); } + SkipWhite(); - return value.toString(); + + return value.length() == 0 ? null : value.toString(); } + + /** Output a String with Tab */ private void Emit(String s){ System.out.print(TAB+s); @@ -278,9 +264,14 @@ public class FormulaParser { if (look == '('){ //This is a function function(name); - } else if (look == ':') { // this is a AreaReference + } else if (look == ':' || look == '.') { // this is a AreaReference + GetChar(); + + while (look == '.') { // formulas can have . or .. or ... instead of : + GetChar(); + } + String first = name; - Match(':'); String second = GetName(); tokens.add(new AreaPtg(first+":"+second)); } else if (look == '!') { @@ -508,43 +499,81 @@ public class FormulaParser { } else if (look == ')' || look == ',') { tokens.add(new MissingArgPtg()); } else { - - String number = GetNum(); - if (look=='.') { - Match('.'); - String decimalPart = null; - if (IsDigit(look)) number = number +"."+ GetNum(); //this also takes care of someone entering "1234." - if ('E' == look) { - String exponent = GetExponent(); - number += 'E' + exponent; + String number2 = null; + String exponent = null; + String number1 = GetNum(); + + if (look == '.') { + GetChar(); + number2 = GetNum(); + } + + if (look == 'E') { + GetChar(); + + String sign = ""; + if (look == '+') { + GetChar(); + } else if (look == '-') { + GetChar(); + sign = "-"; } - tokens.add(new NumberPtg(number)); + + String number = GetNum(); + if (number == null) { + Expected("Integer"); + } + exponent = sign + number; } - else if ('E' == look) { - String exponent = GetExponent(); - number += 'E'+exponent; - tokens.add(new NumberPtg(number)); - } - else { - tokens.add(getNumberPtgFromString(number)); //TODO:what if the number is too big to be a short? ..add factory to return Int or Number! + + if (number1 == null && number2 == null) { + Expected("Integer"); } + + tokens.add(getNumberPtgFromString(number1, number2, exponent)); } } - /** Get a PTG for an integer from its string representation. - * return Int or Number Ptg based on size of input - * @param number + /** + * Get a PTG for an integer from its string representation. + * return Int or Number Ptg based on size of input */ - private Ptg getNumberPtgFromString(String number) { - try { - return new IntPtg(number); - } catch (NumberFormatException e) { - return new NumberPtg(number); - } - + private Ptg getNumberPtgFromString(String number1, String number2, String exponent) { + StringBuffer number = new StringBuffer(); + + if (number2 == null) { + number.append(number1); + + if (exponent != null) { + number.append('E'); + number.append(exponent); + } + + String numberStr = number.toString(); + + try { + return new IntPtg(numberStr); + } catch (NumberFormatException e) { + return new NumberPtg(numberStr); + } + } else { + if (number1 != null) { + number.append(number1); + } + + number.append('.'); + number.append(number2); + + if (exponent != null) { + number.append('E'); + number.append(exponent); + } + + return new NumberPtg(number.toString()); + } } - - + + private void StringLiteral() { // Can't use match here 'cuz it consumes whitespace diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java index a5be67359..2d1a25c06 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java @@ -507,4 +507,66 @@ public class TestFormulaParser extends TestCase { System.out.println("Testing org.apache.poi.hssf.record.formula.FormulaParser"); junit.textui.TestRunner.run(TestFormulaParser.class); } + + public void testNumbers() { + HSSFWorkbook wb = new HSSFWorkbook(); + + wb.createSheet("Cash_Flow");; + + HSSFSheet sheet = wb.createSheet("Test"); + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell((short)0); + String formula = null; + + // starts from decimal point + + cell.setCellFormula(".1"); + formula = cell.getCellFormula(); + assertEquals("0.1", formula); + + cell.setCellFormula("+.1"); + formula = cell.getCellFormula(); + assertEquals("+0.1", formula); + + cell.setCellFormula("-.1"); + formula = cell.getCellFormula(); + assertEquals("-0.1", formula); + + // has exponent + + cell.setCellFormula("10E1"); + formula = cell.getCellFormula(); + assertEquals("100.0", formula); + + cell.setCellFormula("10E+1"); + formula = cell.getCellFormula(); + assertEquals("100.0", formula); + + cell.setCellFormula("10E-1"); + formula = cell.getCellFormula(); + assertEquals("1.0", formula); + } + + public void testRanges() { + HSSFWorkbook wb = new HSSFWorkbook(); + + wb.createSheet("Cash_Flow");; + + HSSFSheet sheet = wb.createSheet("Test"); + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell((short)0); + String formula = null; + + cell.setCellFormula("A1.A2"); + formula = cell.getCellFormula(); + assertEquals("A1:A2", formula); + + cell.setCellFormula("A1..A2"); + formula = cell.getCellFormula(); + assertEquals("A1:A2", formula); + + cell.setCellFormula("A1...A2"); + formula = cell.getCellFormula(); + assertEquals("A1:A2", formula); + } }