]> source.dussan.org Git - poi.git/commitdiff
setCellFormula: blank cell gets value 0, non-blank value is preserved
authorVladislav Galas <gallon@apache.org>
Sat, 26 Jan 2019 10:09:13 +0000 (10:09 +0000)
committerVladislav Galas <gallon@apache.org>
Sat, 26 Jan 2019 10:09:13 +0000 (10:09 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1852212 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
src/java/org/apache/poi/ss/usermodel/Cell.java
src/java/org/apache/poi/ss/usermodel/CellBase.java
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFCell.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java
src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFCell.java
src/testcases/org/apache/poi/ss/usermodel/BaseTestCell.java

index ee4d828d5ef2c3aafc0b3c0dfee3ecc0c9ffaef6..dbeaba8b48c6981ff936cb2186c5c6e27846687c 100644 (file)
@@ -46,8 +46,10 @@ import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.CellBase;
 import org.apache.poi.ss.usermodel.CellStyle;
 import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.CellValue;
 import org.apache.poi.ss.usermodel.Comment;
 import org.apache.poi.ss.usermodel.FormulaError;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
 import org.apache.poi.ss.usermodel.Hyperlink;
 import org.apache.poi.ss.usermodel.RichTextString;
 import org.apache.poi.ss.util.CellAddress;
@@ -290,8 +292,9 @@ public class HSSFCell extends CellBase {
                     frec.setRow(row);
                     frec.setColumn(col);
                 }
-                if (setValue)
-                {
+                if (getCellType() == CellType.BLANK) {
+                    frec.getFormulaRecord().setValue(0);
+                } else if (setValue) {
                     frec.getFormulaRecord().setValue(getNumericCellValue());
                 }
                 frec.setXFIndex(styleIndex);
@@ -596,6 +599,7 @@ public class HSSFCell extends CellBase {
         short col=_record.getColumn();
         short styleIndex=_record.getXFIndex();
 
+        final CellValue savedValue = readValue();
         int sheetIndex = _book.getSheetIndex(_sheet);
         Ptg[] ptgs = HSSFFormulaParser.parse(formula, _book, FormulaType.CELL, sheetIndex);
         setCellType(CellType.FORMULA, false, row, col, styleIndex);
@@ -603,13 +607,48 @@ public class HSSFCell extends CellBase {
         FormulaRecord frec = agg.getFormulaRecord();
         frec.setOptions((short) 2);
 
-        frec.setValue(0);
-
         //only set to default if there is no extended format index already set
         if (agg.getXFIndex() == (short)0) {
             agg.setXFIndex((short) 0x0f);
         }
         agg.setParsedExpression(ptgs);
+
+        restoreValue(savedValue);
+    }
+
+    private CellValue readValue() {
+        final CellType valueType = getCellType() == CellType.FORMULA ? getCachedFormulaResultType() : getCellType();
+        switch (valueType) {
+            case NUMERIC:
+                return new CellValue(getNumericCellValue());
+            case STRING:
+                return new CellValue(getStringCellValue());
+            case BOOLEAN:
+                return CellValue.valueOf(getBooleanCellValue());
+            case ERROR:
+                return CellValue.getError(getErrorCellValue());
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    private void restoreValue(CellValue value) {
+        switch (value.getCellType()) {
+            case NUMERIC:
+                setCellValue(value.getNumberValue());
+                break;
+            case STRING:
+                setCellValue(value.getStringValue());
+                break;
+            case BOOLEAN:
+                setCellValue(value.getBooleanValue());
+                break;
+            case ERROR:
+                setCellErrorValue(FormulaError.forInt(value.getErrorValue()));
+                break;
+            default:
+                throw new IllegalStateException();
+        }
     }
 
     @Override
index b255c03abe2a7baaf1a3ad0bb1c7992a6a1ccb67..6f960a30d735df6cbbb5932bf79344d984bb715d 100644 (file)
@@ -198,19 +198,18 @@ public interface Cell {
 
     /**
      * Sets formula for this cell.
+     * <p>If {@code formula} is not null, sets or updates the formula. If {@code formula} is null, removes the formula.
+     * Or use {@link #removeFormula()} to remove the formula.</p>
      *
-     * <p>
-     * Note, this method only sets the formula string and does not calculate the formula value.
-     * To set the precalculated value use {@link #setCellValue}
-     * </p>
+     * <p>Note, this method only sets the formula string and does not calculate the formula value.
+     * To set the precalculated value use {@link #setCellValue}.</p>
      *
-     * <p>
-     * If the cell was blank, sets value to 0. Otherwise, preserves the value as precalculated.
-     * </p>
+     * <p>If the cell was blank, sets value to 0. Otherwise, preserves the value as precalculated.</p>
      *
      * @param formula the formula to set, e.g. <code>"SUM(C4:E4)"</code>.
      * If the argument is <code>null</code> then the current formula is removed.
      *
+     * @see Cell#removeFormula
      * @throws IllegalStateException if this cell is a part of an array formula group containing other cells
      * @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid
      */
index 5a7e1f282a8e6123b0a65e4de8f8ba08bbe0d68e..7e3929b236a0b8ecb40f19f3b0f7b9d6bf8e103b 100644 (file)
@@ -82,29 +82,46 @@ public abstract class CellBase implements Cell {
      */
     @Override
     public final void setCellFormula(String formula) throws FormulaParseException, IllegalStateException {
+        // todo validate formula here, before changing the cell?
+        tryToDeleteArrayFormulaIfSet();
+
         if (formula == null) {
             removeFormula();
             return;
         }
 
-        CellType previousValueType = getCellType() == CellType.FORMULA ? getCachedFormulaResultType() : getCellType();
-
-        tryToDeleteArrayFormulaIfSet();
-
-        setCellFormulaImpl(formula);
-
-        if (previousValueType == CellType.BLANK) {
+        // formula cells always have a value. If the cell is blank (either initially or after removing an
+        // array formula), set value to 0
+        if (getValueType() == CellType.BLANK) {
             setCellValue(0);
         }
+
+        setCellFormulaImpl(formula);
     }
 
     /**
-     * Implementation-specific setting the formula.
+     * Implementation-specific setting the formula. Formula is not null.
      * Shall not change the value.
      * @param formula
      */
     protected abstract void setCellFormulaImpl(String formula);
 
+    /**
+     * Get value type of this cell. Can return BLANK, NUMERIC, STRING, BOOLEAN or ERROR.
+     * For current implementations where type is strongly coupled with formula, is equivalent to
+     * <code>getCellType() == CellType.FORMULA ? getCachedFormulaResultType() : getCellType()</code>
+     *
+     * <p>This is meant as a temporary helper method until the time when value type is decoupled from the formula.</p>
+     * @return value type
+     */
+    protected final CellType getValueType() {
+        CellType type = getCellType();
+        if (type != CellType.FORMULA) {
+            return type;
+        }
+        return getCachedFormulaResultType();
+    }
+
     /**
      * {@inheritDoc}
      */
index 29b4b2652b9c90eddadf9ad83319f2dbfaa579c5..3f67266f21adb877fef8e3a1dd826e1830546394 100644 (file)
@@ -55,6 +55,7 @@ public class SXSSFCell extends CellBase {
     public SXSSFCell(SXSSFRow row, CellType cellType)
     {
         _row=row;
+        _value = new BlankValue();
         setType(cellType);
     }
 
@@ -326,15 +327,37 @@ public class SXSSFCell extends CellBase {
      * @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid
      */
     @Override
-    public void setCellFormulaImpl(String formula) throws FormulaParseException
-    {
-        if(formula == null) {
-            setType(CellType.BLANK);
-            return;
+    public void setCellFormulaImpl(String formula) throws FormulaParseException {
+        assert formula != null;
+        if (getCellType() == CellType.FORMULA) {
+            ((FormulaValue)_value).setValue(formula);
+        } else {
+            switch (getCellType()) {
+                case NUMERIC:
+                    _value = new NumericFormulaValue(formula, getNumericCellValue());
+                    break;
+                case STRING:
+                    if (_value instanceof PlainStringValue) {
+                        _value = new StringFormulaValue(formula, getStringCellValue());
+                    } else {
+                        assert(_value instanceof RichTextValue);
+                        _value = new RichTextStringFormulaValue(formula, ((RichTextValue) _value).getValue());
+                    }
+                    break;
+                case BOOLEAN:
+                    _value = new BooleanFormulaValue(formula, getBooleanCellValue());
+                    break;
+                case ERROR:
+                    _value = new ErrorFormulaValue(formula, getErrorCellValue());
+                    break;
+                case _NONE:
+                    // fall-through
+                case FORMULA:
+                    // fall-through
+                case BLANK:
+                    throw new AssertionError();
+            }
         }
-
-        ensureFormulaType(computeTypeFromFormula(formula));
-        ((FormulaValue)_value).setValue(formula);
     }
 
     @Override
@@ -531,14 +554,9 @@ public class SXSSFCell extends CellBase {
     public void setCellErrorValue(byte value) {
         // for formulas, we want to keep the type and only have an ERROR as formula value
         if(_value.getType()==CellType.FORMULA) {
-            // ensure that the type is "ERROR"
-            setFormulaType(CellType.ERROR);
-
-            // populate the value
-            ((ErrorFormulaValue) _value).setPreEvaluatedValue(value);
+            _value = new ErrorFormulaValue(getCellFormula(), value);
         } else {
-            ensureTypeOrFormulaType(CellType.ERROR);
-            ((ErrorValue) _value).setValue(value);
+            _value = new ErrorValue(value);
         }
     }
 
@@ -870,8 +888,7 @@ public class SXSSFCell extends CellBase {
         // don't change cell type for formulas
         if(_value.getType() == CellType.FORMULA) {
             String formula = ((FormulaValue)_value).getValue();
-            _value = new RichTextStringFormulaValue();
-            ((RichTextStringFormulaValue) _value).setValue(formula);
+            _value = new RichTextStringFormulaValue(formula, new XSSFRichTextString(""));
         } else if(_value.getType()!=CellType.STRING ||
                 !((StringValue)_value).isRichText()) {
             _value = new RichTextValue();
@@ -882,12 +899,7 @@ public class SXSSFCell extends CellBase {
         if(_value.getType()!=type)
             setType(type);
     }
-    /*package*/ void ensureFormulaType(CellType type)
-    {
-        if(_value.getType()!=CellType.FORMULA
-           ||((FormulaValue)_value).getFormulaType()!=type)
-            setFormulaType(type);
-    }
+
     /*
      * Sets the cell type to type if it is different
      */
@@ -903,7 +915,22 @@ public class SXSSFCell extends CellBase {
         {
             if(((FormulaValue)_value).getFormulaType()==type)
                 return;
-            setFormulaType(type); // once a formula, always a formula
+            switch (type) {
+                case BOOLEAN:
+                    _value = new BooleanFormulaValue(getCellFormula(), false);
+                    break;
+                case NUMERIC:
+                    _value = new NumericFormulaValue(getCellFormula(), 0);
+                    break;
+                case STRING:
+                    _value = new StringFormulaValue(getCellFormula(), "");
+                    break;
+                case ERROR:
+                    _value = new ErrorFormulaValue(getCellFormula(), FormulaError._NO_ERROR.getCode());
+                    break;
+                default:
+                    throw new AssertionError();
+            }
             return;
         }
         setType(type);
@@ -937,7 +964,9 @@ public class SXSSFCell extends CellBase {
             }
             case FORMULA:
             {
-                _value = new NumericFormulaValue();
+                if (getCellType() == CellType.BLANK) {
+                    _value = new NumericFormulaValue("", 0);
+                }
                 break;
             }
             case BLANK:
@@ -967,49 +996,6 @@ public class SXSSFCell extends CellBase {
             }
         }
     }
-    /*package*/ void setFormulaType(CellType type)
-    {
-        Value prevValue = _value;
-        switch(type)
-        {
-            case NUMERIC:
-            {
-                _value = new NumericFormulaValue();
-                break;
-            }
-            case STRING:
-            {
-                _value = new StringFormulaValue();
-                break;
-            }
-            case BOOLEAN:
-            {
-                _value = new BooleanFormulaValue();
-                break;
-            }
-            case ERROR:
-            {
-                _value = new ErrorFormulaValue();
-                break;
-            }
-            default:
-            {
-                throw new IllegalArgumentException("Illegal type " + type);
-            }
-        }
-
-        // if we had a Formula before, we should copy over the _value of the formula
-        if(prevValue instanceof FormulaValue) {
-            ((FormulaValue)_value)._value = ((FormulaValue)prevValue)._value;
-        }
-    }
-
-//TODO: implement this correctly
-    @NotImplemented
-    /*package*/ CellType computeTypeFromFormula(String formula)
-    {
-        return CellType.NUMERIC;
-    }
 //COPIED FROM https://svn.apache.org/repos/asf/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java since the functions are declared private there
     /**
      * Used to help format error messages
@@ -1123,9 +1109,17 @@ public class SXSSFCell extends CellBase {
     {
         CellType getType();
     }
-    static class NumericValue implements Value
-    {
+    static class NumericValue implements Value {
         double _value;
+
+        public NumericValue() {
+            _value = 0;
+        }
+
+        public NumericValue(double _value) {
+            this._value = _value;
+        }
+
         public CellType getType()
         {
             return CellType.NUMERIC;
@@ -1190,6 +1184,11 @@ public class SXSSFCell extends CellBase {
     static abstract class FormulaValue implements Value
     {
         String _value;
+
+        public FormulaValue(String _value) {
+            this._value = _value;
+        }
+
         public CellType getType()
         {
             return CellType.FORMULA;
@@ -1204,9 +1203,14 @@ public class SXSSFCell extends CellBase {
         }
         abstract CellType getFormulaType();
     }
-    static class NumericFormulaValue extends FormulaValue
-    {
+    static class NumericFormulaValue extends FormulaValue {
         double _preEvaluatedValue;
+
+        public NumericFormulaValue(String formula, double _preEvaluatedValue) {
+            super(formula);
+            this._preEvaluatedValue = _preEvaluatedValue;
+        }
+
         @Override
         CellType getFormulaType()
         {
@@ -1221,9 +1225,14 @@ public class SXSSFCell extends CellBase {
             return _preEvaluatedValue;
         }
     }
-    static class StringFormulaValue extends FormulaValue
-    {
+    static class StringFormulaValue extends FormulaValue {
         String _preEvaluatedValue;
+
+        public StringFormulaValue(String formula, String value) {
+            super(formula);
+            _preEvaluatedValue = value;
+        }
+
         @Override
         CellType getFormulaType()
         {
@@ -1238,9 +1247,15 @@ public class SXSSFCell extends CellBase {
             return _preEvaluatedValue;
         }
     }
-    static class RichTextStringFormulaValue extends FormulaValue
-    {
+
+    static class RichTextStringFormulaValue extends FormulaValue {
         RichTextString _preEvaluatedValue;
+
+        public RichTextStringFormulaValue(String formula, RichTextString value) {
+            super(formula);
+            _preEvaluatedValue = value;
+        }
+
         @Override
         CellType getFormulaType()
         {
@@ -1255,9 +1270,15 @@ public class SXSSFCell extends CellBase {
             return _preEvaluatedValue;
         }
     }
-    static class BooleanFormulaValue extends FormulaValue
-    {
+
+    static class BooleanFormulaValue extends FormulaValue {
         boolean _preEvaluatedValue;
+
+        public BooleanFormulaValue(String formula, boolean value) {
+            super(formula);
+            _preEvaluatedValue = value;
+        }
+
         @Override
         CellType getFormulaType()
         {
@@ -1272,9 +1293,15 @@ public class SXSSFCell extends CellBase {
             return _preEvaluatedValue;
         }
     }
-    static class ErrorFormulaValue extends FormulaValue
-    {
+
+    static class ErrorFormulaValue extends FormulaValue {
         byte _preEvaluatedValue;
+
+        public ErrorFormulaValue(String formula, byte value) {
+            super(formula);
+            _preEvaluatedValue = value;
+        }
+
         @Override
         CellType getFormulaType()
         {
@@ -1289,16 +1316,25 @@ public class SXSSFCell extends CellBase {
             return _preEvaluatedValue;
         }
     }
-    static class BlankValue implements Value
-    {
+
+    static class BlankValue implements Value {
         public CellType getType()
         {
             return CellType.BLANK;
         }
     }
-    static class BooleanValue implements Value
-    {
+
+    static class BooleanValue implements Value {
         boolean _value;
+
+        public BooleanValue() {
+            _value = false;
+        }
+
+        public BooleanValue(boolean _value) {
+            this._value = _value;
+        }
+
         public CellType getType()
         {
             return CellType.BOOLEAN;
@@ -1315,6 +1351,15 @@ public class SXSSFCell extends CellBase {
     static class ErrorValue implements Value
     {
         byte _value;
+
+        public ErrorValue() {
+            _value = FormulaError._NO_ERROR.getCode();
+        }
+
+        public ErrorValue(byte _value) {
+            this._value = _value;
+        }
+
         public CellType getType()
         {
             return CellType.ERROR;
index 47ccb646bccaa175a19dca0bb2163a4773c65178..9eadae5b26b84ac55fb7680af35bb139981b3086 100644 (file)
@@ -551,6 +551,7 @@ public final class XSSFCell extends CellBase {
      */
     @Override
     protected void setCellFormulaImpl(String formula) {
+        assert formula != null;
         setFormula(formula, FormulaType.CELL);
     }
 
@@ -590,9 +591,6 @@ public final class XSSFCell extends CellBase {
             f.setStringValue(formula);
             _cell.setF(f);
         }
-        if(_cell.isSetV()) {
-            _cell.unsetV();
-        }
     }
 
     @Override
index 7a030defd9dcc47f4107b149eefabbc3c6a65131..8ecbce0544da3397f0209be22af69d98caf535c5 100644 (file)
@@ -175,7 +175,16 @@ public class TestSXSSFCell extends BaseTestXCell {
 
     @Override
     @Ignore
-    @Test
     public void removeFormula_turnsCellToBlank_whenFormulaWasASingleCellArrayFormula() {
     }
+
+    @Override
+    @Ignore
+    public void setCellFormula_onASingleCellArrayFormulaCell_preservesTheValue() {
+    }
+
+    @Test
+    @Ignore
+    public void setCellFormula_isExceptionSafe_onBlankCell() {
+    }
 }
index 242b4abeec4cf18ff5ea25933d4292a6b64d6895..9afa79f9569d7ba448414befb3b7a4c3454f8ce6 100644 (file)
@@ -32,11 +32,13 @@ import java.util.Date;
 import java.util.GregorianCalendar;
 import java.util.Locale;
 import java.util.TimeZone;
+import java.util.function.Consumer;
 
 import org.apache.poi.common.usermodel.HyperlinkType;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.ss.ITestDataProvider;
 import org.apache.poi.ss.SpreadsheetVersion;
+import org.apache.poi.ss.formula.FormulaParseException;
 import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.util.LocaleUtil;
 import org.junit.Test;
@@ -1303,6 +1305,58 @@ public abstract class BaseTestCell {
         assertEquals(CellType.BLANK, cell.getCellType());
     }
 
+    @Test
+    public void setCellFormula_onABlankCell_setsValueToZero() {
+        Cell cell = getInstance();
+        cell.setCellFormula("\"foo\"");
+        assertEquals(CellType.FORMULA, cell.getCellType());
+        assertEquals(CellType.NUMERIC, cell.getCachedFormulaResultType());
+        assertEquals(0, cell.getNumericCellValue(), 0);
+    }
+
+
+    @Test
+    public void setCellFormula_onANonBlankCell_preservesTheValue() {
+        Cell cell = getInstance();
+        cell.setCellValue(true);
+        cell.setCellFormula("\"foo\"");
+        assertEquals(CellType.FORMULA, cell.getCellType());
+        assertEquals(CellType.BOOLEAN, cell.getCachedFormulaResultType());
+        assertTrue(cell.getBooleanCellValue());
+    }
+
+    @Test
+    public void setCellFormula_onAFormulaCell_changeFormula_preservesTheValue() {
+        Cell cell = getInstance();
+        cell.setCellFormula("\"foo\"");
+        cell.setCellValue(true);
+        assertEquals(CellType.FORMULA, cell.getCellType());
+        assertEquals(CellType.BOOLEAN, cell.getCachedFormulaResultType());
+        assertTrue(cell.getBooleanCellValue());
+
+        cell.setCellFormula("\"bar\"");
+        assertEquals(CellType.FORMULA, cell.getCellType());
+        assertEquals(CellType.BOOLEAN, cell.getCachedFormulaResultType());
+        assertTrue(cell.getBooleanCellValue());
+    }
+
+    @Test
+    public void setCellFormula_onASingleCellArrayFormulaCell_preservesTheValue() {
+        Cell cell = getInstance();
+        cell.getSheet().setArrayFormula("\"foo\"", CellRangeAddress.valueOf("A1"));
+        cell.setCellValue(true);
+
+        assertTrue(cell.isPartOfArrayFormulaGroup());
+        assertEquals(CellType.FORMULA, cell.getCellType());
+        assertEquals(CellType.BOOLEAN, cell.getCachedFormulaResultType());
+        assertTrue(cell.getBooleanCellValue());
+
+        cell.getSheet().setArrayFormula("\"bar\"", CellRangeAddress.valueOf("A1"));
+        assertEquals(CellType.FORMULA, cell.getCellType());
+        assertEquals(CellType.BOOLEAN, cell.getCachedFormulaResultType());
+        assertTrue(cell.getBooleanCellValue());
+    }
+
     private Cell getInstance() {
         return _testDataProvider.createWorkbook().createSheet().createRow(0).createCell(0);
     }