diff --git a/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
index 07dd072ecb..a403d09e2d 100644
--- a/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
+++ b/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
@@ -230,7 +230,9 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
if (_sharedFormulaRecord != null) {
return false;
}
- return _formulaRecord.getFormula().getExpReference() != null;
+ CellReference expRef = _formulaRecord.getFormula().getExpReference();
+ ArrayRecord arec = expRef == null ? null : _sharedValueManager.getArrayRecord(expRef.getRow(), expRef.getCol());
+ return arec != null;
}
public CellRangeAddress getArrayFormulaRange() {
@@ -260,6 +262,8 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
*/
public CellRangeAddress removeArrayFormula(int rowIndex, int columnIndex) {
CellRangeAddress8Bit a = _sharedValueManager.removeArrayFormula(rowIndex, columnIndex);
- return new CellRangeAddress(a.getFirstRow(), a.getLastRow(), a.getFirstColumn(), a.getLastColumn());
+ // at this point FormulaRecordAggregate#isPartOfArrayFormula() should return false
+ _formulaRecord.setParsedExpression(null);
+ return new CellRangeAddress(a.getFirstRow(), a.getLastRow(), a.getFirstColumn(), a.getLastColumn());
}
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
index a543477136..071f1b4313 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
@@ -284,6 +284,9 @@ public class HSSFCell implements Cell {
*/
public void setCellType(int cellType) {
notifyFormulaChanging();
+ if(isPartOfArrayFormulaGroup()){
+ notifyArrayFormulaChanging();
+ }
int row=_record.getRow();
short col=_record.getColumn();
short styleIndex=_record.getXFIndex();
@@ -578,6 +581,10 @@ public class HSSFCell implements Cell {
}
public void setCellFormula(String formula) {
+ if(isPartOfArrayFormulaGroup()){
+ notifyArrayFormulaChanging();
+ }
+
int row=_record.getRow();
short col=_record.getColumn();
short styleIndex=_record.getXFIndex();
@@ -1191,4 +1198,40 @@ public class HSSFCell implements Cell {
}
return ((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 #setCellType(int)
+ * @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);
+ }
+
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
index 53938cf821..3d1c73497b 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
@@ -163,6 +163,10 @@ public final class HSSFRow implements Row {
if(column >= cells.length || cell != cells[column]) {
throw new RuntimeException("Specified cell is not from this row");
}
+ if(cell.isPartOfArrayFormulaGroup()){
+ cell.notifyArrayFormulaChanging();
+ }
+
cells[column]=null;
if(alsoRemoveRecords) {
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index 251b02c7fb..75e48cfa0f 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -238,6 +238,13 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
if (row.getSheet() != this) {
throw new IllegalArgumentException("Specified row does not belong to this sheet");
}
+ for(Cell cell : row) {
+ 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);
+ }
+ }
if (_rows.size() > 0) {
Integer key = Integer.valueOf(row.getRowNum());
@@ -571,12 +578,45 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
public int addMergedRegion(CellRangeAddress region)
{
region.validate(SpreadsheetVersion.EXCEL97);
+
+ // throw IllegalStateException if the argument CellRangeAddress intersects with
+ // a multi-cell array formula defined in this sheet
+ validateArrayFormulas(region);
+
return _sheet.addMergedRegion( region.getFirstRow(),
region.getFirstColumn(),
region.getLastRow(),
region.getLastColumn());
}
+ private void validateArrayFormulas(CellRangeAddress region){
+ int firstRow = region.getFirstRow();
+ int firstColumn = region.getFirstColumn();
+ int lastRow = region.getLastRow();
+ int lastColumn = region.getLastColumn();
+ for (int rowIn = firstRow; rowIn <= lastRow; rowIn++) {
+ for (int colIn = firstColumn; colIn <= lastColumn; colIn++) {
+ HSSFRow row = getRow(rowIn);
+ if (row == null) continue;
+
+ HSSFCell cell = row.getCell(colIn);
+ if(cell == null) continue;
+
+ if(cell.isPartOfArrayFormulaGroup()){
+ CellRangeAddress arrayRange = cell.getArrayFormulaRange();
+ if (arrayRange.getNumberOfCells() > 1 &&
+ ( arrayRange.isInRange(region.getFirstRow(), region.getFirstColumn()) ||
+ arrayRange.isInRange(region.getFirstRow(), region.getFirstColumn())) ){
+ String msg = "The range " + region.formatAsString() + " intersects with a multi-cell array formula. " +
+ "You cannot merge cells of an array.";
+ throw new IllegalStateException(msg);
+ }
+ }
+ }
+ }
+
+ }
+
/**
* Whether a record must be inserted or not at generation to indicate that
* formula must be recalculated when workbook is opened.
@@ -1214,6 +1254,11 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc ) {
HSSFRow row = getRow( rowNum );
+ // notify all cells in this row that we are going to shift them,
+ // it can throw IllegalStateException if the operation is not allowed, for example,
+ // if the row contains cells included in a multi-cell array formula
+ if(row != null) notifyRowShifting(row);
+
HSSFRow row2Replace = getRow( rowNum + n );
if ( row2Replace == null )
row2Replace = createRow( rowNum + n );
@@ -1301,6 +1346,17 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
_sheet.getRecords().addAll(window2Loc, records);
}
+ private void notifyRowShifting(HSSFRow row){
+ String msg = "Row[rownum="+row.getRowNum()+"] contains cell(s) included in a multi-cell array formula. " +
+ "You cannot change part of an array.";
+ for(Cell cell : row){
+ HSSFCell hcell = (HSSFCell)cell;
+ if(hcell.isPartOfArrayFormulaGroup()){
+ hcell.notifyArrayFormulaChanging(msg);
+ }
+ }
+ }
+
/**
* Creates a split (freezepane). Any existing freezepane or split pane is overwritten.
* @param colSplit Horizonatal position of split.
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 f2a4c497b0..9a6be02014 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java
@@ -385,8 +385,13 @@ public final class XSSFCell implements Cell {
* @param formula the formula to set, e.g. "SUM(C4:E4)"
.
* If the argument is null
then the current formula is removed.
* @throws org.apache.poi.ss.formula.FormulaParseException if the formula has incorrect syntax or is otherwise invalid
+ * @throws IllegalStateException if the operation is not allowed, for example,
+ * when the cell is a part of a multi-cell array formula
*/
public void setCellFormula(String formula) {
+ if(isPartOfArrayFormulaGroup()){
+ notifyArrayFormulaChanging();
+ }
setFormula(formula, FormulaType.CELL);
}
@@ -694,6 +699,10 @@ public final class XSSFCell implements Cell {
* @see #CELL_TYPE_ERROR
*/
public void setCellType(int cellType) {
+ if(isPartOfArrayFormulaGroup()){
+ notifyArrayFormulaChanging();
+ }
+
int prevType = getCellType();
switch (cellType) {
case CELL_TYPE_BLANK:
@@ -982,4 +991,41 @@ public final class XSSFCell implements Cell {
public boolean isPartOfArrayFormulaGroup() {
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);
+ }
+ }
+
+ /**
+ * Called when this cell is modified.
+ *
+ * The purpose of this method is to validate the cell state prior to modification.
+ *
+ *
+ * @see #setCellType(int)
+ * @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);
+ }
}
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 9e564023e9..61535c5cd3 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java
@@ -361,7 +361,15 @@ public class XSSFRow implements Row, Comparable {
* @param cell the cell to remove
*/
public void removeCell(Cell cell) {
- _cells.remove(cell.getColumnIndex());
+ if (cell.getRow() != this) {
+ throw new IllegalArgumentException("Specified cell does not belong to this row");
+ }
+
+ XSSFCell xcell = (XSSFCell)cell;
+ if(xcell.isPartOfArrayFormulaGroup()){
+ xcell.notifyArrayFormulaChanging();
+ }
+ _cells.remove(cell.getColumnIndex());
}
/**
@@ -409,8 +417,13 @@ public class XSSFRow implements Row, Comparable {
int rownum = getRowNum() + n;
CalculationChain calcChain = _sheet.getWorkbook().getCalculationChain();
int sheetId = (int)_sheet.sheet.getSheetId();
+ String msg = "Row[rownum="+getRowNum()+"] contains cell(s) included in a multi-cell array formula. " +
+ "You cannot change part of an array.";
for(Cell c : this){
XSSFCell cell = (XSSFCell)c;
+ if(cell.isPartOfArrayFormulaGroup()){
+ cell.notifyArrayFormulaChanging(msg);
+ }
//remove the reference in the calculation chain
if(calcChain != null) calcChain.removeItem(sheetId, cell.getReference());
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
index 61b7d3fe10..08e5c95ea1 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
@@ -244,18 +244,50 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
/**
* Adds a merged region of cells (hence those cells form one).
*
- * @param cra (rowfrom/colfrom-rowto/colto) to merge
+ * @param region (rowfrom/colfrom-rowto/colto) to merge
* @return index of this region
*/
- public int addMergedRegion(CellRangeAddress cra) {
- cra.validate(SpreadsheetVersion.EXCEL2007);
+ public int addMergedRegion(CellRangeAddress region) {
+ region.validate(SpreadsheetVersion.EXCEL2007);
+
+ // throw IllegalStateException if the argument CellRangeAddress intersects with
+ // a multi-cell array formula defined in this sheet
+ validateArrayFormulas(region);
CTMergeCells ctMergeCells = worksheet.isSetMergeCells() ? worksheet.getMergeCells() : worksheet.addNewMergeCells();
CTMergeCell ctMergeCell = ctMergeCells.addNewMergeCell();
- ctMergeCell.setRef(cra.formatAsString());
+ ctMergeCell.setRef(region.formatAsString());
return ctMergeCells.sizeOfMergeCellArray();
}
+ private void validateArrayFormulas(CellRangeAddress region){
+ int firstRow = region.getFirstRow();
+ int firstColumn = region.getFirstColumn();
+ int lastRow = region.getLastRow();
+ int lastColumn = region.getLastColumn();
+ for (int rowIn = firstRow; rowIn <= lastRow; rowIn++) {
+ for (int colIn = firstColumn; colIn <= lastColumn; colIn++) {
+ XSSFRow row = getRow(rowIn);
+ if (row == null) continue;
+
+ XSSFCell cell = row.getCell(colIn);
+ if(cell == null) continue;
+
+ if(cell.isPartOfArrayFormulaGroup()){
+ CellRangeAddress arrayRange = cell.getArrayFormulaRange();
+ if (arrayRange.getNumberOfCells() > 1 &&
+ ( arrayRange.isInRange(region.getFirstRow(), region.getFirstColumn()) ||
+ arrayRange.isInRange(region.getFirstRow(), region.getFirstColumn())) ){
+ String msg = "The range " + region.formatAsString() + " intersects with a multi-cell array formula. " +
+ "You cannot merge cells of an array.";
+ throw new IllegalStateException(msg);
+ }
+ }
+ }
+ }
+
+ }
+
/**
* Adjusts the column width to fit the contents.
*
@@ -1279,7 +1311,13 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
if (row.getSheet() != this) {
throw new IllegalArgumentException("Specified row does not belong to this sheet");
}
-
+ for(Cell cell : row) {
+ XSSFCell xcell = (XSSFCell)cell;
+ String msg = "Row[rownum="+row.getRowNum()+"] contains cell(s) included in a multi-cell array formula. You cannot change part of an array.";
+ if(xcell.isPartOfArrayFormulaGroup()){
+ xcell.notifyArrayFormulaChanging(msg);
+ }
+ }
rows.remove(row.getRowNum());
}
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheetUpdateArrayFormulas.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheetUpdateArrayFormulas.java
index b703c48cd0..fc55ffdebc 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheetUpdateArrayFormulas.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheetUpdateArrayFormulas.java
@@ -19,8 +19,7 @@ package org.apache.poi.xssf.usermodel;
import junit.framework.AssertionFailedError;
-import org.apache.poi.ss.usermodel.BaseTestSheetUpdateArrayFormulas;
-import org.apache.poi.ss.usermodel.CellRange;
+import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.XSSFITestDataProvider;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;
@@ -106,4 +105,5 @@ public final class TestXSSFSheetUpdateArrayFormulas extends BaseTestSheetUpdateA
assertEquals(STCellFormulaType.ARRAY, f.getT());
}
}
+
}
diff --git a/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java b/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java
index b5b9724c6b..be785b4fb9 100644
--- a/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java
+++ b/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java
@@ -100,5 +100,8 @@ public final class TestFormulaRecordAggregate extends TestCase {
Ptg[] ptg = agg.getFormulaTokens();
String fmlaSer = FormulaRenderer.toFormulaString(null, ptg);
assertEquals(formula, fmlaSer);
- }
+
+ agg.removeArrayFormula(rownum, colnum);
+ assertFalse(agg.isPartOfArrayFormula());
+ }
}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheetUpdateArrayFormulas.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheetUpdateArrayFormulas.java
index eff7157eb6..6f3cdad920 100644
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheetUpdateArrayFormulas.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheetUpdateArrayFormulas.java
@@ -27,8 +27,7 @@ import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.SharedValueManager;
import org.apache.poi.hssf.record.aggregates.TestSharedValueManager;
-import org.apache.poi.ss.usermodel.BaseTestSheetUpdateArrayFormulas;
-import org.apache.poi.ss.usermodel.CellRange;
+import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
/**
diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetUpdateArrayFormulas.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetUpdateArrayFormulas.java
index fdd11fec3a..41779ea9c4 100644
--- a/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetUpdateArrayFormulas.java
+++ b/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetUpdateArrayFormulas.java
@@ -233,4 +233,264 @@ public abstract class BaseTestSheetUpdateArrayFormulas extends TestCase {
assertTrue( cell1.isPartOfArrayFormulaGroup());
}
}
+
+ /**
+ * Test that we can set pre-calculated formula result for array formulas
+ */
+ public void testModifyArrayCells_setFormulaResult(){
+ Workbook workbook = _testDataProvider.createWorkbook();
+ Sheet sheet = workbook.createSheet();
+
+ //single-cell array formula
+ CellRange extends Cell> srange =
+ sheet.setArrayFormula("SUM(A4:A6,B4:B6)", CellRangeAddress.valueOf("B5"));
+ Cell scell = srange.getTopLeftCell();
+ assertEquals(Cell.CELL_TYPE_FORMULA, scell.getCellType());
+ assertEquals(0.0, scell.getNumericCellValue());
+ scell.setCellValue(1.1);
+ assertEquals(1.1, scell.getNumericCellValue());
+
+ //multi-cell array formula
+ CellRange extends Cell> mrange =
+ sheet.setArrayFormula("A1:A3*B1:B3", CellRangeAddress.valueOf("C1:C3"));
+ for(Cell mcell : mrange){
+ assertEquals(Cell.CELL_TYPE_FORMULA, mcell.getCellType());
+ assertEquals(0.0, mcell.getNumericCellValue());
+ double fmlaResult = 1.2;
+ mcell.setCellValue(fmlaResult);
+ assertEquals(fmlaResult, mcell.getNumericCellValue());
+ }
+ }
+
+ public void testModifyArrayCells_setCellType(){
+ Workbook workbook = _testDataProvider.createWorkbook();
+ Sheet sheet = workbook.createSheet();
+
+ // single-cell array formulas behave just like normal cells -
+ // changing cell type removes the array formula and associated cached result
+ CellRange extends Cell> srange =
+ sheet.setArrayFormula("SUM(A4:A6,B4:B6)", CellRangeAddress.valueOf("B5"));
+ Cell scell = srange.getTopLeftCell();
+ assertEquals(Cell.CELL_TYPE_FORMULA, scell.getCellType());
+ assertEquals(0.0, scell.getNumericCellValue());
+ scell.setCellType(Cell.CELL_TYPE_STRING);
+ assertEquals(Cell.CELL_TYPE_STRING, scell.getCellType());
+ scell.setCellValue("string cell");
+ assertEquals("string cell", scell.getStringCellValue());
+
+ //once you create a multi-cell array formula, you cannot change the type of its cells
+ CellRange extends Cell> mrange =
+ sheet.setArrayFormula("A1:A3*B1:B3", CellRangeAddress.valueOf("C1:C3"));
+ for(Cell mcell : mrange){
+ try {
+ assertEquals(Cell.CELL_TYPE_FORMULA, mcell.getCellType());
+ mcell.setCellType(Cell.CELL_TYPE_NUMERIC);
+ fail("expected exception");
+ } catch (IllegalStateException e){
+ CellReference ref = new CellReference(mcell);
+ String msg = "Cell "+ref.formatAsString()+" is part of a multi-cell array formula. You cannot change part of an array.";
+ assertEquals(msg, e.getMessage());
+ }
+ // a failed invocation of Cell.setCellType leaves the cell
+ // in the state that it was in prior to the invocation
+ assertEquals(Cell.CELL_TYPE_FORMULA, mcell.getCellType());
+ assertTrue(mcell.isPartOfArrayFormulaGroup());
+ }
+ }
+
+ public void testModifyArrayCells_setCellFormula(){
+ Workbook workbook = _testDataProvider.createWorkbook();
+ Sheet sheet = workbook.createSheet();
+
+ CellRange extends Cell> srange =
+ sheet.setArrayFormula("SUM(A4:A6,B4:B6)", CellRangeAddress.valueOf("B5"));
+ Cell scell = srange.getTopLeftCell();
+ assertEquals("SUM(A4:A6,B4:B6)", scell.getCellFormula());
+ assertEquals(Cell.CELL_TYPE_FORMULA, scell.getCellType());
+ assertTrue(scell.isPartOfArrayFormulaGroup());
+ scell.setCellFormula("SUM(A4,A6)");
+ //we are now a normal formula cell
+ assertEquals("SUM(A4,A6)", scell.getCellFormula());
+ assertFalse(scell.isPartOfArrayFormulaGroup());
+ assertEquals(Cell.CELL_TYPE_FORMULA, scell.getCellType());
+ //check that setting formula result works
+ assertEquals(0.0, scell.getNumericCellValue());
+ scell.setCellValue(33.0);
+ assertEquals(33.0, scell.getNumericCellValue());
+
+ //multi-cell array formula
+ CellRange extends Cell> mrange =
+ sheet.setArrayFormula("A1:A3*B1:B3", CellRangeAddress.valueOf("C1:C3"));
+ for(Cell mcell : mrange){
+ //we cannot set individual formulas for cells included in an array formula
+ try {
+ assertEquals("A1:A3*B1:B3", mcell.getCellFormula());
+ mcell.setCellFormula("A1+A2");
+ fail("expected exception");
+ } catch (IllegalStateException e){
+ CellReference ref = new CellReference(mcell);
+ String msg = "Cell "+ref.formatAsString()+" is part of a multi-cell array formula. You cannot change part of an array.";
+ assertEquals(msg, e.getMessage());
+ }
+ // a failed invocation of Cell.setCellFormula leaves the cell
+ // in the state that it was in prior to the invocation
+ assertEquals("A1:A3*B1:B3", mcell.getCellFormula());
+ assertTrue(mcell.isPartOfArrayFormulaGroup());
+ }
+ }
+
+ public void testModifyArrayCells_removeCell(){
+ Workbook workbook = _testDataProvider.createWorkbook();
+ Sheet sheet = workbook.createSheet();
+
+ //single-cell array formulas behave just like normal cells
+ CellRangeAddress cra = CellRangeAddress.valueOf("B5");
+ CellRange extends Cell> srange =
+ sheet.setArrayFormula("SUM(A4:A6,B4:B6)", cra);
+ Cell scell = srange.getTopLeftCell();
+
+ Row srow = sheet.getRow(cra.getFirstRow());
+ assertSame(srow, scell.getRow());
+ srow.removeCell(scell);
+ assertNull(srow.getCell(cra.getFirstColumn()));
+
+ //re-create the removed cell
+ scell = srow.createCell(cra.getFirstColumn());
+ assertEquals(Cell.CELL_TYPE_BLANK, scell.getCellType());
+ assertFalse(scell.isPartOfArrayFormulaGroup());
+
+ //we cannot remove cells included in a multi-cell array formula
+ CellRange extends Cell> mrange =
+ sheet.setArrayFormula("A1:A3*B1:B3", CellRangeAddress.valueOf("C1:C3"));
+ for(Cell mcell : mrange){
+ int columnIndex = mcell.getColumnIndex();
+ Row mrow = mcell.getRow();
+ try {
+ mrow.removeCell(mcell);
+ fail("expected exception");
+ } catch (IllegalStateException e){
+ CellReference ref = new CellReference(mcell);
+ String msg = "Cell "+ref.formatAsString()+" is part of a multi-cell array formula. You cannot change part of an array.";
+ assertEquals(msg, e.getMessage());
+ }
+ // a failed invocation of Row.removeCell leaves the row
+ // in the state that it was in prior to the invocation
+ assertSame(mcell, mrow.getCell(columnIndex));
+ assertTrue(mcell.isPartOfArrayFormulaGroup());
+ assertEquals(Cell.CELL_TYPE_FORMULA, mcell.getCellType());
+ }
+ }
+
+ public void testModifyArrayCells_removeRow(){
+ Workbook workbook = _testDataProvider.createWorkbook();
+ Sheet sheet = workbook.createSheet();
+
+ //single-cell array formulas behave just like normal cells
+ CellRangeAddress cra = CellRangeAddress.valueOf("B5");
+ CellRange extends Cell> srange =
+ sheet.setArrayFormula("SUM(A4:A6,B4:B6)", cra);
+ Cell scell = srange.getTopLeftCell();
+ assertEquals(Cell.CELL_TYPE_FORMULA, scell.getCellType());
+
+ Row srow = scell.getRow();
+ assertSame(srow, sheet.getRow(cra.getFirstRow()));
+ sheet.removeRow(srow);
+ assertNull(sheet.getRow(cra.getFirstRow()));
+
+ //re-create the removed row and cell
+ scell = sheet.createRow(cra.getFirstRow()).createCell(cra.getFirstColumn());
+ assertEquals(Cell.CELL_TYPE_BLANK, scell.getCellType());
+ assertFalse(scell.isPartOfArrayFormulaGroup());
+
+ //we cannot remove rows with cells included in a multi-cell array formula
+ CellRange extends Cell> mrange =
+ sheet.setArrayFormula("A1:A3*B1:B3", CellRangeAddress.valueOf("C1:C3"));
+ for(Cell mcell : mrange){
+ int columnIndex = mcell.getColumnIndex();
+ Row mrow = mcell.getRow();
+ try {
+ sheet.removeRow(mrow);
+ fail("expected exception");
+ } catch (IllegalStateException e){
+ String msg = "Row[rownum="+mrow.getRowNum()+"] contains cell(s) included in a multi-cell array formula. You cannot change part of an array.";
+ assertEquals(msg, e.getMessage());
+ }
+ // a failed invocation of Row.removeCell leaves the row
+ // in the state that it was in prior to the invocation
+ assertSame(mrow, sheet.getRow(mrow.getRowNum()));
+ assertSame(mcell, mrow.getCell(columnIndex));
+ assertTrue(mcell.isPartOfArrayFormulaGroup());
+ assertEquals(Cell.CELL_TYPE_FORMULA, mcell.getCellType());
+ }
+ }
+
+ public void testModifyArrayCells_mergeCells(){
+ Workbook workbook = _testDataProvider.createWorkbook();
+ Sheet sheet = workbook.createSheet();
+ assertEquals(0, sheet.getNumMergedRegions());
+
+ //single-cell array formulas behave just like normal cells
+ CellRange extends Cell> srange =
+ sheet.setArrayFormula("SUM(A4:A6,B4:B6)", CellRangeAddress.valueOf("B5"));
+ Cell scell = srange.getTopLeftCell();
+ sheet.addMergedRegion(CellRangeAddress.valueOf("B5:C6"));
+ //we are still an array formula
+ assertEquals(Cell.CELL_TYPE_FORMULA, scell.getCellType());
+ assertTrue(scell.isPartOfArrayFormulaGroup());
+ assertEquals(1, sheet.getNumMergedRegions());
+
+ //we cannot merge cells included in an array formula
+ CellRange extends Cell> mrange =
+ sheet.setArrayFormula("A1:A3*B1:B3", CellRangeAddress.valueOf("C1:C3"));
+ CellRangeAddress cra = CellRangeAddress.valueOf("C1:C3");
+ try {
+ sheet.addMergedRegion(cra);
+ fail("expected exception");
+ } catch (IllegalStateException e){
+ String msg = "The range "+cra.formatAsString()+" intersects with a multi-cell array formula. You cannot merge cells of an array.";
+ assertEquals(msg, e.getMessage());
+ }
+ //the number of merged regions remains the same
+ assertEquals(1, sheet.getNumMergedRegions());
+ }
+
+ public void testModifyArrayCells_shiftRows(){
+ Workbook workbook = _testDataProvider.createWorkbook();
+ Sheet sheet = workbook.createSheet();
+
+ //single-cell array formulas behave just like normal cells - we can change the cell type
+ CellRange extends Cell> srange =
+ sheet.setArrayFormula("SUM(A4:A6,B4:B6)", CellRangeAddress.valueOf("B5"));
+ Cell scell = srange.getTopLeftCell();
+ assertEquals("SUM(A4:A6,B4:B6)", scell.getCellFormula());
+ sheet.shiftRows(0, 0, 1);
+ sheet.shiftRows(0, 1, 1);
+
+ //we cannot set individual formulas for cells included in an array formula
+ CellRange extends Cell> mrange =
+ sheet.setArrayFormula("A1:A3*B1:B3", CellRangeAddress.valueOf("C1:C3"));
+
+ try {
+ sheet.shiftRows(0, 0, 1);
+ fail("expected exception");
+ } catch (IllegalStateException e){
+ String msg = "Row[rownum=0] contains cell(s) included in a multi-cell array formula. You cannot change part of an array.";
+ assertEquals(msg, e.getMessage());
+ }
+ /*
+ TODO: enable shifting the whole array
+
+ sheet.shiftRows(0, 2, 1);
+ //the array C1:C3 is now C2:C4
+ CellRangeAddress cra = CellRangeAddress.valueOf("C2:C4");
+ for(Cell mcell : mrange){
+ //TODO define equals and hashcode for CellRangeAddress
+ assertEquals(cra.formatAsString(), mcell.getArrayFormulaRange().formatAsString());
+ assertEquals("A2:A4*B2:B4", mcell.getCellFormula());
+ assertTrue(mcell.isPartOfArrayFormulaGroup());
+ assertEquals(Cell.CELL_TYPE_FORMULA, mcell.getCellType());
+ }
+
+ */
+ }
}