From 7b44d1e54cf02613b421534d9815b2306c2d7aab Mon Sep 17 00:00:00 2001 From: Vladislav Galas Date: Mon, 7 Jan 2019 19:10:19 +0000 Subject: [PATCH] unified setCellType(null/_NONE) logic, setCellFormula(null) logic. updated javadoc and tests git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1850676 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/hssf/usermodel/HSSFCell.java | 97 ++++++------- .../apache/poi/hssf/usermodel/HSSFRow.java | 2 +- .../apache/poi/hssf/usermodel/HSSFSheet.java | 4 +- .../org/apache/poi/ss/usermodel/Cell.java | 50 +++++-- .../org/apache/poi/ss/usermodel/CellBase.java | 137 ++++++++++++++++++ .../apache/poi/xssf/streaming/SXSSFCell.java | 43 ++++-- .../apache/poi/xssf/usermodel/XSSFCell.java | 74 +++------- .../apache/poi/xssf/usermodel/XSSFRow.java | 2 +- .../poi/xssf/streaming/TestSXSSFCell.java | 32 ++++ .../apache/poi/ss/usermodel/BaseTestCell.java | 106 ++++++++++++++ 10 files changed, 411 insertions(+), 136 deletions(-) create mode 100644 src/java/org/apache/poi/ss/usermodel/CellBase.java diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java index 35c234a449..ee4d828d5e 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java @@ -43,6 +43,7 @@ import org.apache.poi.ss.formula.eval.ErrorEval; import org.apache.poi.ss.formula.ptg.ExpPtg; import org.apache.poi.ss.formula.ptg.Ptg; 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.Comment; @@ -68,7 +69,7 @@ import org.apache.poi.util.Removal; * cells that have values should be added. *

*/ -public class HSSFCell implements Cell { +public class HSSFCell extends CellBase { private static final String FILE_FORMAT_NAME = "BIFF8"; /** * The maximum number of columns in BIFF8 @@ -255,17 +256,10 @@ public class HSSFCell implements Cell { return new CellAddress(this); } - /** - * Set the cells type (numeric, formula or string). - * If the cell currently contains a value, the value will - * be converted to match the new type, if possible. - */ @Override - public void setCellType(CellType cellType) { + protected void setCellTypeImpl(CellType cellType) { notifyFormulaChanging(); - if(isPartOfArrayFormulaGroup()){ - notifyArrayFormulaChanging(); - } + int row=_record.getRow(); short col=_record.getColumn(); short styleIndex=_record.getXFIndex(); @@ -594,26 +588,21 @@ public class HSSFCell implements Cell { _stringValue.setUnicodeString(_book.getWorkbook().getSSTString(index)); } - public void setCellFormula(String formula) { - if(isPartOfArrayFormulaGroup()){ - notifyArrayFormulaChanging(); - } + @Override + protected void setCellFormulaImpl(String formula) { + assert formula != null; int row=_record.getRow(); short col=_record.getColumn(); short styleIndex=_record.getXFIndex(); - if (formula==null) { - notifyFormulaChanging(); - setCellType(CellType.BLANK, false, row, col, styleIndex); - return; - } int sheetIndex = _book.getSheetIndex(_sheet); Ptg[] ptgs = HSSFFormulaParser.parse(formula, _book, FormulaType.CELL, sheetIndex); setCellType(CellType.FORMULA, false, row, col, styleIndex); FormulaRecordAggregate agg = (FormulaRecordAggregate) _record; FormulaRecord frec = agg.getFormulaRecord(); frec.setOptions((short) 2); + frec.setValue(0); //only set to default if there is no extended format index already set @@ -622,6 +611,42 @@ public class HSSFCell implements Cell { } agg.setParsedExpression(ptgs); } + + @Override + protected void removeFormulaImpl() { + assert getCellType() == CellType.FORMULA; + + notifyFormulaChanging(); + + switch (getCachedFormulaResultType()) { + case NUMERIC: + double numericValue = ((FormulaRecordAggregate)_record).getFormulaRecord().getValue(); + _record = new NumberRecord(); + ((NumberRecord)_record).setValue(numericValue); + _cellType = CellType.NUMERIC; + break; + case STRING: + _record = new NumberRecord(); + ((NumberRecord)_record).setValue(0); + _cellType = CellType.STRING; + break; + case BOOLEAN: + boolean booleanValue = ((FormulaRecordAggregate)_record).getFormulaRecord().getCachedBooleanValue(); + _record = new BoolErrRecord(); + ((BoolErrRecord)_record).setValue(booleanValue); + _cellType = CellType.BOOLEAN; + break; + case ERROR: + byte errorValue = (byte) ((FormulaRecordAggregate)_record).getFormulaRecord().getCachedErrorValue(); + _record = new BoolErrRecord(); + ((BoolErrRecord)_record).setValue(errorValue); + _cellType = CellType.ERROR; + break; + default: + throw new AssertionError(); + } + } + /** * Should be called any time that a formula could potentially be deleted. * Does nothing if this cell currently does not hold a formula @@ -1192,40 +1217,6 @@ public class HSSFCell implements Cell { return _cellType == CellType.FORMULA && ((FormulaRecordAggregate) _record).isPartOfArrayFormula(); } - /** - * The purpose of this method is to validate the cell state prior to modification - * - * @see #notifyArrayFormulaChanging() - */ - void notifyArrayFormulaChanging(String msg){ - CellRangeAddress cra = getArrayFormulaRange(); - if(cra.getNumberOfCells() > 1) { - throw new IllegalStateException(msg); - } - //un-register the single-cell array formula from the parent XSSFSheet - getRow().getSheet().removeArrayFormula(this); - } - - /** - * Called when this cell is modified. - *

- * The purpose of this method is to validate the cell state prior to modification. - *

- * - * @see #setCellFormula(String) - * @see HSSFRow#removeCell(org.apache.poi.ss.usermodel.Cell) - * @see org.apache.poi.hssf.usermodel.HSSFSheet#removeRow(org.apache.poi.ss.usermodel.Row) - * @see org.apache.poi.hssf.usermodel.HSSFSheet#shiftRows(int, int, int) - * @see org.apache.poi.hssf.usermodel.HSSFSheet#addMergedRegion(org.apache.poi.ss.util.CellRangeAddress) - * @throws IllegalStateException if modification is not allowed - */ - void notifyArrayFormulaChanging(){ - CellReference ref = new CellReference(this); - String msg = "Cell "+ref.formatAsString()+" is part of a multi-cell array formula. " + - "You cannot change part of an array."; - notifyArrayFormulaChanging(msg); - } - /** * Applying a user-defined style (UDS) is special. Excel does not directly reference user-defined styles, but * instead create a 'proxy' ExtendedFormatRecord referencing the UDS as parent. diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java index 845b58a009..9f5461cff3 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java @@ -164,7 +164,7 @@ public final class HSSFRow implements Row, Comparable { throw new RuntimeException("Specified cell is not from this row"); } if(cell.isPartOfArrayFormulaGroup()){ - cell.notifyArrayFormulaChanging(); + cell.tryToDeleteArrayFormula(null); } cells[column]=null; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index de3b05c1e5..48a9095907 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -299,7 +299,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { HSSFCell xcell = (HSSFCell) cell; if (xcell.isPartOfArrayFormulaGroup()) { String msg = "Row[rownum=" + row.getRowNum() + "] contains cell(s) included in a multi-cell array formula. You cannot change part of an array."; - xcell.notifyArrayFormulaChanging(msg); + xcell.tryToDeleteArrayFormula(msg); } } @@ -1779,7 +1779,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { for (Cell cell : row) { HSSFCell hcell = (HSSFCell) cell; if (hcell.isPartOfArrayFormulaGroup()) { - hcell.notifyArrayFormulaChanging(msg); + hcell.tryToDeleteArrayFormula(msg); } } } diff --git a/src/java/org/apache/poi/ss/usermodel/Cell.java b/src/java/org/apache/poi/ss/usermodel/Cell.java index 13ddfd6518..b255c03abe 100644 --- a/src/java/org/apache/poi/ss/usermodel/Cell.java +++ b/src/java/org/apache/poi/ss/usermodel/Cell.java @@ -70,17 +70,22 @@ public interface Cell { Row getRow(); /** - * Set the cells type (numeric, formula or string). + * Set the cells type (blank, numeric, boolean, error or string). *

If the cell currently contains a value, the value will * be converted to match the new type, if possible. Formatting * is generally lost in the process however.

+ *

Conversion rules:

+ *

to NUMERIC: numeric value is left as is. True converts to 1.0, false converts to 0. otherwise, the + * value is set to 0. Formula is removed.

*

If what you want to do is get a String value for your * numeric cell, stop!. This is not the way to do it. * Instead, for fetching the string value of a numeric or boolean - * or date cell, use {@link DataFormatter} instead.

- * - * @throws IllegalArgumentException if the specified cell type is invalid - * @throws IllegalStateException if the current value cannot be converted to the new type + * or date cell, use {@link DataFormatter} instead.

+ *

If cell is a member of an array formula group containing more than 1 cell, an {@link IllegalStateException} + * is thrown. If the array formula group contains only this cell, it is removed

+ * @throws IllegalArgumentException if the specified cell type is invalid (null or _NONE) + * @throws IllegalStateException if the current value cannot be converted to the new type or + * if the cell is a part of an array formula group containing other cells */ void setCellType(CellType cellType); @@ -90,7 +95,7 @@ public interface Cell { * @return the cell type */ CellType getCellType(); - + /** * Return the cell type. * @@ -101,13 +106,13 @@ public interface Cell { @Deprecated @Removal(version="4.2") CellType getCellTypeEnum(); - + /** * Only valid for formula cells - * + * * Will return {@link CellType} in a future version of POI. * For forwards compatibility, do not hard-code cell type literals in your code. - * + * * @return one of ({@link CellType#NUMERIC}, {@link CellType#STRING}, * {@link CellType#BOOLEAN}, {@link CellType#ERROR}) depending * on the cached value of the formula @@ -138,7 +143,7 @@ public interface Cell { /** *

Converts the supplied date to its equivalent Excel numeric value and sets * that into the cell.

- * + * *

Note - There is actually no 'DATE' cell type in Excel. In many * cases (when entering date values), Excel automatically adjusts the * cell style to some date format, creating the illusion that the cell @@ -193,16 +198,33 @@ public interface Cell { /** * Sets formula for this cell. + * *

* Note, this method only sets the formula string and does not calculate the formula value. - * To set the precalculated value use {@link #setCellValue(double)} or {@link #setCellValue(String)} + * To set the precalculated value use {@link #setCellValue} + *

+ * + *

+ * If the cell was blank, sets value to 0. Otherwise, preserves the value as precalculated. *

* * @param formula the formula to set, e.g. "SUM(C4:E4)". - * If the argument is null then the current formula is removed. + * If the argument is null then the current formula is removed. + * + * @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 */ - void setCellFormula(String formula) throws FormulaParseException; + void setCellFormula(String formula) throws FormulaParseException, IllegalStateException; + + /** + * Removes formula, if any. + * + * If cell was blank, leaves it as is. + * If it is a part of an array formula group, blanks the cell. + * If has a regular formula, removes the formula preserving the "cached" value. + * @throws IllegalStateException if cell is a part of an array formula group containing other cells + */ + void removeFormula() throws IllegalStateException; /** * Return a formula for the cell, for example, SUM(C4:E4) @@ -304,7 +326,7 @@ public interface Cell { /** *

Set the style for the cell. The style should be an CellStyle created/retrieved from * the Workbook.

- * + * *

To change the style of a cell without affecting other cells that use the same style, * use {@link org.apache.poi.ss.util.CellUtil#setCellStyleProperties(Cell, Map)}

* diff --git a/src/java/org/apache/poi/ss/usermodel/CellBase.java b/src/java/org/apache/poi/ss/usermodel/CellBase.java new file mode 100644 index 0000000000..5a7e1f282a --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/CellBase.java @@ -0,0 +1,137 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.ss.usermodel; + +import org.apache.poi.ss.formula.FormulaParseException; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellReference; + +/** + * Common implementation-independent logic shared by all implementations of {@link Cell}. + * @author Vladislav "gallon" Galas gallon at apache dot org + */ +public abstract class CellBase implements Cell { + /** + * {@inheritDoc} + */ + @Override + public final void setCellType(CellType cellType) { + if (cellType == null || cellType == CellType._NONE) { + throw new IllegalArgumentException("cellType shall not be null nor _NONE"); + } + + tryToDeleteArrayFormulaIfSet(); + + setCellTypeImpl(cellType); + } + + /** + * Implementation-specific logic + * @param cellType new cell type. Guaranteed non-null, not _NONE. + */ + protected abstract void setCellTypeImpl(CellType cellType); + + /** + * Called when this an array formula in this cell is deleted. + *

The purpose of this method is to validate the cell state prior to modification.

+ * + * @param message a customized exception message for the case if deletion of the cell is impossible. If null, a + * default message will be generated + * @see #setCellType(CellType) + * @see #setCellFormula(String) + * @see Row#removeCell(org.apache.poi.ss.usermodel.Cell) + * @see org.apache.poi.ss.usermodel.Sheet#removeRow(org.apache.poi.ss.usermodel.Row) + * @see org.apache.poi.ss.usermodel.Sheet#shiftRows(int, int, int) + * @see org.apache.poi.ss.usermodel.Sheet#addMergedRegion(org.apache.poi.ss.util.CellRangeAddress) + * @throws IllegalStateException if modification is not allowed + * + * Note. Exposing this to public is ugly. Needed for methods like Sheet#shiftRows. + */ + public final void tryToDeleteArrayFormula(String message) { + assert isPartOfArrayFormulaGroup(); + + CellRangeAddress arrayFormulaRange = getArrayFormulaRange(); + if(arrayFormulaRange.getNumberOfCells() > 1) { + if (message == null) { + message = "Cell " + new CellReference(this).formatAsString() + " is part of a multi-cell array formula. " + + "You cannot change part of an array."; + } + throw new IllegalStateException(message); + } + //un-register the single-cell array formula from the parent sheet through public interface + getRow().getSheet().removeArrayFormula(this); + } + + /** + * {@inheritDoc} + */ + @Override + public final void setCellFormula(String formula) throws FormulaParseException, IllegalStateException { + if (formula == null) { + removeFormula(); + return; + } + + CellType previousValueType = getCellType() == CellType.FORMULA ? getCachedFormulaResultType() : getCellType(); + + tryToDeleteArrayFormulaIfSet(); + + setCellFormulaImpl(formula); + + if (previousValueType == CellType.BLANK) { + setCellValue(0); + } + } + + /** + * Implementation-specific setting the formula. + * Shall not change the value. + * @param formula + */ + protected abstract void setCellFormulaImpl(String formula); + + /** + * {@inheritDoc} + */ + @Override + public final void removeFormula() { + if (getCellType() == CellType.BLANK) { + return; + } + + if (isPartOfArrayFormulaGroup()) { + tryToDeleteArrayFormula(null); + return; + } + + removeFormulaImpl(); + } + + /** + * Implementation-specific removal of the formula. + * The cell is guaranteed to have a regular formula set. + * Shall preserve the "cached" value. + */ + protected abstract void removeFormulaImpl(); + + private void tryToDeleteArrayFormulaIfSet() { + if (isPartOfArrayFormulaGroup()) { + tryToDeleteArrayFormula(null); + } + } +} diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFCell.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFCell.java index 8917e60105..29b4b2652b 100644 --- a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFCell.java +++ b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFCell.java @@ -27,6 +27,7 @@ import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.FormulaParseException; import org.apache.poi.ss.formula.eval.ErrorEval; 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.Comment; @@ -45,7 +46,7 @@ import org.apache.poi.xssf.usermodel.XSSFRichTextString; /** * Streaming version of XSSFCell implementing the "BigGridDemo" strategy. */ -public class SXSSFCell implements Cell { +public class SXSSFCell extends CellBase { private final SXSSFRow _row; private Value _value; private CellStyle _style; @@ -111,14 +112,8 @@ public class SXSSFCell implements Cell { return _row; } - /** - * Set the cells type (numeric, formula or string) - * - * @throws IllegalArgumentException if the specified cell type is invalid - */ @Override - public void setCellType(CellType cellType) - { + protected void setCellTypeImpl(CellType cellType) { ensureType(cellType); } @@ -331,7 +326,7 @@ public class SXSSFCell implements Cell { * @throws FormulaParseException if the formula has incorrect syntax or is otherwise invalid */ @Override - public void setCellFormula(String formula) throws FormulaParseException + public void setCellFormulaImpl(String formula) throws FormulaParseException { if(formula == null) { setType(CellType.BLANK); @@ -341,6 +336,36 @@ public class SXSSFCell implements Cell { ensureFormulaType(computeTypeFromFormula(formula)); ((FormulaValue)_value).setValue(formula); } + + @Override + protected void removeFormulaImpl() { + assert getCellType() == CellType.FORMULA; + switch (getCachedFormulaResultType()) { + case NUMERIC: + double numericValue = ((NumericFormulaValue)_value).getPreEvaluatedValue(); + _value = new NumericValue(); + ((NumericValue) _value).setValue(numericValue); + break; + case STRING: + String stringValue = ((StringFormulaValue)_value).getPreEvaluatedValue(); + _value = new PlainStringValue(); + ((PlainStringValue) _value).setValue(stringValue); + break; + case BOOLEAN: + boolean booleanValue = ((BooleanFormulaValue)_value).getPreEvaluatedValue(); + _value = new BooleanValue(); + ((BooleanValue) _value).setValue(booleanValue); + break; + case ERROR: + byte errorValue = ((ErrorFormulaValue)_value).getPreEvaluatedValue(); + _value = new ErrorValue(); + ((ErrorValue) _value).setValue(errorValue); + break; + default: + throw new AssertionError(); + } + } + /** * Return a formula for the cell, for example, SUM(C4:E4) * diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java index 864b031729..e336ae458b 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -30,6 +30,7 @@ import org.apache.poi.ss.formula.SharedFormula; import org.apache.poi.ss.formula.eval.ErrorEval; import org.apache.poi.ss.formula.ptg.Ptg; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellBase; import org.apache.poi.ss.usermodel.CellCopyPolicy; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellType; @@ -70,7 +71,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellType; * cells that have values should be added. *

*/ -public final class XSSFCell implements Cell { +public final class XSSFCell extends CellBase { private static final String FALSE_AS_STRING = "0"; private static final String TRUE_AS_STRING = "1"; @@ -549,10 +550,7 @@ public final class XSSFCell implements Cell { * when the cell is a part of a multi-cell array formula */ @Override - public void setCellFormula(String formula) { - if(isPartOfArrayFormulaGroup()){ - notifyArrayFormulaChanging(); - } + protected void setCellFormulaImpl(String formula) { setFormula(formula, FormulaType.CELL); } @@ -565,7 +563,7 @@ public final class XSSFCell implements Cell { private void setFormula(String formula, FormulaType formulaType) { XSSFWorkbook wb = _row.getSheet().getWorkbook(); - if (formula == null) { + if (formulaType == FormulaType.ARRAY && formula == null) { wb.onDeleteFormula(this); if (_cell.isSetF()) { _row.getSheet().onDeleteFormula(this, null); @@ -597,6 +595,15 @@ public final class XSSFCell implements Cell { } } + @Override + protected void removeFormulaImpl() { + _row.getSheet().getWorkbook().onDeleteFormula(this); + if (_cell.isSetF()) { + _row.getSheet().onDeleteFormula(this, null); + _cell.unsetF(); + } + } + /** * Returns column index of this cell * @@ -960,13 +967,8 @@ public final class XSSFCell implements Cell { _cell.setR(ref); } - /** - * Set the cells type (numeric, formula or string) - * - * @throws IllegalArgumentException if the specified cell type is invalid - */ @Override - public void setCellType(CellType cellType) { + protected void setCellTypeImpl(CellType cellType) { setCellType(cellType, null); } @@ -978,10 +980,6 @@ public final class XSSFCell implements Cell { */ protected void setCellType(CellType cellType, BaseXSSFEvaluationWorkbook evalWb) { CellType prevType = getCellType(); - - if(isPartOfArrayFormulaGroup()){ - notifyArrayFormulaChanging(); - } if(prevType == CellType.FORMULA && cellType != CellType.FORMULA) { if (_cell.isSetF()) { _row.getSheet().onDeleteFormula(this, evalWb); @@ -1307,48 +1305,12 @@ public final class XSSFCell implements Cell { return getSheet().isCellInArrayFormulaContext(this); } - /** - * The purpose of this method is to validate the cell state prior to modification - * - * @see #notifyArrayFormulaChanging() - */ - void notifyArrayFormulaChanging(String msg){ - if(isPartOfArrayFormulaGroup()){ - CellRangeAddress cra = getArrayFormulaRange(); - if(cra.getNumberOfCells() > 1) { - throw new IllegalStateException(msg); - } - //un-register the single-cell array formula from the parent XSSFSheet - getRow().getSheet().removeArrayFormula(this); + //Moved from XSSFRow.shift(). Not sure what is purpose. + public void updateCellReferencesForShifting(String msg){ + if(isPartOfArrayFormulaGroup()) { + tryToDeleteArrayFormula(msg); } - } - /** - * Called when this cell is modified. - *

- * The purpose of this method is to validate the cell state prior to modification. - *

- * - * @see #setCellType(CellType) - * @see #setCellFormula(String) - * @see XSSFRow#removeCell(org.apache.poi.ss.usermodel.Cell) - * @see org.apache.poi.xssf.usermodel.XSSFSheet#removeRow(org.apache.poi.ss.usermodel.Row) - * @see org.apache.poi.xssf.usermodel.XSSFSheet#shiftRows(int, int, int) - * @see org.apache.poi.xssf.usermodel.XSSFSheet#addMergedRegion(org.apache.poi.ss.util.CellRangeAddress) - * @throws IllegalStateException if modification is not allowed - */ - void notifyArrayFormulaChanging(){ - CellReference ref = new CellReference(this); - String msg = "Cell "+ref.formatAsString()+" is part of a multi-cell array formula. " + - "You cannot change part of an array."; - notifyArrayFormulaChanging(msg); - } - - - //Moved from XSSFRow.shift(). Not sure what is purpose. - public void updateCellReferencesForShifting(String msg){ - if(isPartOfArrayFormulaGroup()) - notifyArrayFormulaChanging(msg); CalculationChain calcChain = getSheet().getWorkbook().getCalculationChain(); int sheetId = (int)getSheet().sheet.getSheetId(); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java index 66307c206d..363fc5bd9b 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java @@ -480,7 +480,7 @@ public class XSSFRow implements Row, Comparable { XSSFCell xcell = (XSSFCell)cell; if(xcell.isPartOfArrayFormulaGroup()) { - xcell.notifyArrayFormulaChanging(); + xcell.setCellFormula(null); // to remove the array formula } if(cell.getCellType() == CellType.FORMULA) { _sheet.getWorkbook().onDeleteFormula(xcell); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFCell.java b/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFCell.java index d5f4b243d1..7a030defd9 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFCell.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFCell.java @@ -41,6 +41,7 @@ import org.apache.poi.xssf.usermodel.XSSFRichTextString; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.xmlbeans.XmlCursor; import org.junit.AfterClass; +import org.junit.Ignore; import org.junit.Test; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst; @@ -146,4 +147,35 @@ public class TestSXSSFCell extends BaseTestXCell { byte result = cell.getErrorCellValue(); assertEquals(0, result); } + + /** + * For now, {@link SXSSFCell} doesn't support array formulas. + * However, this test should be enabled if array formulas are implemented for SXSSF. + */ + @Override + @Ignore + public void setCellType_BLANK_removesArrayFormula_ifCellIsPartOfAnArrayFormulaGroupContainingOnlyThisCell() { + } + + /** + * For now, {@link SXSSFCell} doesn't support array formulas. + * However, this test should be enabled if array formulas are implemented for SXSSF. + */ + @Override + @Ignore + @Test // <- annotation is necessary to override expected exception + public void setCellType_BLANK_throwsISE_ifCellIsPartOfAnArrayFormulaGroupContainingOtherCells() { + } + + @Override + @Ignore + @Test + public void setCellFormula_throwsISE_ifCellIsPartOfAnArrayFormulaGroupContainingOtherCells() { + } + + @Override + @Ignore + @Test + public void removeFormula_turnsCellToBlank_whenFormulaWasASingleCellArrayFormula() { + } } diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestCell.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestCell.java index 8705ea5094..242b4abeec 100644 --- a/src/testcases/org/apache/poi/ss/usermodel/BaseTestCell.java +++ b/src/testcases/org/apache/poi/ss/usermodel/BaseTestCell.java @@ -37,6 +37,7 @@ 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.util.CellRangeAddress; import org.apache.poi.util.LocaleUtil; import org.junit.Test; @@ -1200,4 +1201,109 @@ public abstract class BaseTestCell { assertEquals(CellType.NUMERIC, cell.getCellType()); assertEquals(value, cell.getNumericCellValue(), 0); } + + @Test(expected = IllegalArgumentException.class) + public void setCellType_null_throwsIAE() { + Cell cell = getInstance(); + cell.setCellType(null); + } + + @Test(expected = IllegalArgumentException.class) + public void setCellType_NONE_throwsIAE() { + Cell cell = getInstance(); + cell.setCellType(CellType._NONE); + } + + + @Test + public void setCellType_BLANK_removesArrayFormula_ifCellIsPartOfAnArrayFormulaGroupContainingOnlyThisCell() { + Cell cell = getInstance(); + + cell.getSheet().setArrayFormula("1", CellRangeAddress.valueOf("A1")); + cell.setCellValue("foo"); + assertTrue(cell.isPartOfArrayFormulaGroup()); + assertEquals("1", cell.getCellFormula()); + + cell.setCellType(CellType.BLANK); + + assertEquals(CellType.BLANK, cell.getCellType()); + assertFalse(cell.isPartOfArrayFormulaGroup()); + } + + @Test(expected = IllegalStateException.class) + public void setCellType_BLANK_throwsISE_ifCellIsPartOfAnArrayFormulaGroupContainingOtherCells() { + Cell cell = getInstance(); + cell.getSheet().setArrayFormula("1", CellRangeAddress.valueOf("A1:B1")); + cell.setCellValue("foo"); + cell.setCellType(CellType.BLANK); + } + + @Test(expected = IllegalStateException.class) + public void setCellFormula_throwsISE_ifCellIsPartOfAnArrayFormulaGroupContainingOtherCells() { + Cell cell = getInstance(); + + cell.getSheet().setArrayFormula("1", CellRangeAddress.valueOf("A1:B1")); + assertTrue(cell.isPartOfArrayFormulaGroup()); + assertEquals(CellType.FORMULA, cell.getCellType()); + + cell.setCellFormula("1"); + } + + @Test + public void removeFormula_preservesValue() { + Cell cell = getInstance(); + + cell.setCellFormula("#DIV/0!"); + cell.setCellValue(true); + cell.removeFormula(); + assertEquals(CellType.BOOLEAN, cell.getCellType()); + assertTrue(cell.getBooleanCellValue()); + + cell.setCellFormula("#DIV/0!"); + cell.setCellValue(2); + cell.removeFormula(); + assertEquals(CellType.NUMERIC, cell.getCellType()); + assertEquals(2, cell.getNumericCellValue(), 0); + + cell.setCellFormula("#DIV/0!"); + cell.setCellValue("foo"); + cell.removeFormula(); + assertEquals(CellType.STRING, cell.getCellType()); + assertEquals("foo", cell.getStringCellValue()); + + cell.setCellFormula("#DIV/0!"); + cell.setCellErrorValue(FormulaError.NUM.getCode()); + cell.removeFormula(); + assertEquals(CellType.ERROR, cell.getCellType()); + assertEquals(FormulaError.NUM.getCode(), cell.getErrorCellValue()); + } + + @Test + public void removeFormula_turnsCellToBlank_whenFormulaWasASingleCellArrayFormula() { + Cell cell = getInstance(); + + cell.getSheet().setArrayFormula("#DIV/0!", CellRangeAddress.valueOf("A1")); + cell.setCellValue(true); + cell.removeFormula(); + assertEquals(CellType.BLANK, cell.getCellType()); + + cell.getSheet().setArrayFormula("#DIV/0!", CellRangeAddress.valueOf("A1")); + cell.setCellValue(2); + cell.removeFormula(); + assertEquals(CellType.BLANK, cell.getCellType()); + + cell.getSheet().setArrayFormula("#DIV/0!", CellRangeAddress.valueOf("A1")); + cell.setCellValue(true); + cell.removeFormula(); + assertEquals(CellType.BLANK, cell.getCellType()); + + cell.getSheet().setArrayFormula("#DIV/0!", CellRangeAddress.valueOf("A1")); + cell.setCellErrorValue(FormulaError.NUM.getCode()); + cell.removeFormula(); + assertEquals(CellType.BLANK, cell.getCellType()); + } + + private Cell getInstance() { + return _testDataProvider.createWorkbook().createSheet().createRow(0).createCell(0); + } } -- 2.39.5