diff options
Diffstat (limited to 'src/java/org')
4 files changed, 109 insertions, 2 deletions
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. + * <p> + * The purpose of this method is to validate the cell state prior to modification. + * </p> + * + * @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. |