]> source.dussan.org Git - poi.git/commitdiff
Added CellRange return type for Sheet array formula methods. Renamed new test classe...
authorJosh Micich <josh@apache.org>
Fri, 25 Dec 2009 23:04:04 +0000 (23:04 +0000)
committerJosh Micich <josh@apache.org>
Fri, 25 Dec 2009 23:04:04 +0000 (23:04 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@893897 13f79535-47bb-0310-9956-ffa450edef68

25 files changed:
src/java/org/apache/poi/hssf/dev/BiffViewer.java
src/java/org/apache/poi/hssf/record/ArrayRecord.java
src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java
src/java/org/apache/poi/hssf/record/aggregates/SharedValueManager.java
src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
src/java/org/apache/poi/ss/formula/OperandClassTransformer.java
src/java/org/apache/poi/ss/usermodel/Cell.java
src/java/org/apache/poi/ss/usermodel/CellRange.java [new file with mode: 0644]
src/java/org/apache/poi/ss/usermodel/Sheet.java
src/java/org/apache/poi/ss/util/CellRangeAddress.java
src/java/org/apache/poi/ss/util/CellReference.java
src/java/org/apache/poi/ss/util/SSCellRange.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/AllXSSFUsermodelTests.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFArrayFormulas.java [deleted file]
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheetUpdateArrayFormulas.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java
src/testcases/org/apache/poi/hssf/record/aggregates/TestSharedValueManager.java
src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFArrayFormulas.java [deleted file]
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheetUpdateArrayFormulas.java [new file with mode: 0644]
src/testcases/org/apache/poi/ss/usermodel/BaseTestArrayFormulas.java [deleted file]
src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetUpdateArrayFormulas.java [new file with mode: 0644]

index 3b0c91da4be3e817265572993f153c0738eaa661..66223b227497cbbc0f14c9ba93639a7b26a4c681 100644 (file)
@@ -307,8 +307,8 @@ public final class BiffViewer {
                                                System.setProperty("poi.deserialize.escher", "true");
                                        } else if ("--rawhex".equals(arg)) {
                                                rawhex = true;
-                    } else if ("--noheader".equals(arg)) {
-                        noheader = true;
+                                       } else if ("--noheader".equals(arg)) {
+                                               noheader = true;
                                        } else {
                                                throw new CommandParseException("Unexpected option '" + arg + "'");
                                        }
index 540926c60e606675ba41441bca11e93fa0248bea..2f65d8a946aaa0ca1a583af0a6bd111dc140a488 100644 (file)
@@ -17,9 +17,7 @@
 
 package org.apache.poi.hssf.record;
 
-import org.apache.poi.hssf.record.formula.AreaPtgBase;
 import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.RefPtgBase;
 import org.apache.poi.hssf.util.CellRangeAddress8Bit;
 import org.apache.poi.ss.formula.Formula;
 import org.apache.poi.util.HexDump;
@@ -31,7 +29,6 @@ import org.apache.poi.util.LittleEndianOutput;
  * Treated in a similar way to SharedFormulaRecord
  *
  * @author Josh Micich
- * @author Vladimirs Abramovs(Vladimirs.Abramovs at exigenservices.com) - Array Formula support
  */
 public final class ArrayRecord extends SharedValueRecordBase {
 
@@ -52,7 +49,7 @@ public final class ArrayRecord extends SharedValueRecordBase {
                _formula = Formula.read(formulaTokenLen, in, totalFormulaLen);
        }
 
-       public ArrayRecord(Formula formula, CellRangeAddress8Bit range ) {
+       public ArrayRecord(Formula formula, CellRangeAddress8Bit range) {
                super(range);
                _options = 0; //YK: Excel 2007 leaves this field unset
                _field3notUsed = 0;
@@ -66,13 +63,12 @@ public final class ArrayRecord extends SharedValueRecordBase {
                return (_options & OPT_CALCULATE_ON_OPEN) != 0;
        }
 
-    public void setOptions(int val){
-        _options = val;
-    }
+       public Ptg[] getFormulaTokens() {
+               return _formula.getTokens();
+       }
 
-    protected int getExtraDataSize() {
-               return 2 + 4
-                       + _formula.getEncodedSize();
+       protected int getExtraDataSize() {
+               return 2 + 4 + _formula.getEncodedSize();
        }
        protected void serializeExtraData(LittleEndianOutput out) {
                out.writeShort(_options);
@@ -99,42 +95,4 @@ public final class ArrayRecord extends SharedValueRecordBase {
                sb.append("]");
                return sb.toString();
        }
-
-       /**
-        * @return the equivalent {@link Ptg} array that the formula would have,
-        *         were it not shared.
-        */
-    public Ptg[] getFormulaTokens() {
-        return _formula.getTokens();
-        /*
-        YK: I don't understand all t
-
-        int formulaRow = this.getFirstRow();
-        int formulaColumn = this.getLastColumn();
-
-        // Use SharedFormulaRecord static method to convert formula
-
-        Ptg[] ptgs = _formula.getTokens();
-
-        // Convert from relative addressing to absolute
-        // because all formulas in array need to be referenced to the same
-        // ref/range
-        for (int i = 0; i < ptgs.length; i++) {
-            Ptg ptg = ptgs[i];
-            if (ptg instanceof AreaPtgBase) {
-                AreaPtgBase aptg = (AreaPtgBase) ptg;
-                aptg.setFirstRowRelative(false);
-                aptg.setLastRowRelative(false);
-                aptg.setFirstColRelative(false);
-                aptg.setLastColRelative(false);
-
-            } else if (ptg instanceof RefPtgBase) {
-                RefPtgBase rptg = (RefPtgBase) ptg;
-                rptg.setRowRelative(false);
-                rptg.setColRelative(false);
-            }
-        }
-        return SharedFormulaRecord.convertSharedFormulas(ptgs, formulaRow, formulaColumn);
-        */
-    }
 }
index 8d822ee5234baed3584f18cd6c260a0962c2995a..07dd072ecbf38632086516bf8c30a5ae3cd0b2e3 100644 (file)
@@ -186,9 +186,9 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
        }
 
        public Ptg[] getFormulaTokens() {
-        if (_sharedFormulaRecord != null) {
-            return _sharedFormulaRecord.getFormulaTokens(_formulaRecord);
-        }
+               if (_sharedFormulaRecord != null) {
+                       return _sharedFormulaRecord.getFormulaTokens(_formulaRecord);
+               }
                CellReference expRef = _formulaRecord.getFormula().getExpReference();
                if (expRef != null) {
                        ArrayRecord arec = _sharedValueManager.getArrayRecord(expRef.getRow(), expRef.getCol());
@@ -226,15 +226,14 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
                        _sharedValueManager.unlink(_sharedFormulaRecord);
                }
        }
+       public boolean isPartOfArrayFormula() {
+               if (_sharedFormulaRecord != null) {
+                       return false;
+               }
+               return _formulaRecord.getFormula().getExpReference() != null;
+       }
 
-    public boolean isPartOfArrayFormula() {
-        if (_sharedFormulaRecord != null) {
-            return false;
-        }
-        return _formulaRecord.getFormula().getExpReference() != null;
-    }
-
-    public CellRangeAddress getArrayFormulaRange() {
+       public CellRangeAddress getArrayFormulaRange() {
                if (_sharedFormulaRecord != null) {
                        throw new IllegalStateException("not an array formula cell.");
                }
@@ -243,14 +242,14 @@ public final class FormulaRecordAggregate extends RecordAggregate implements Cel
                        throw new IllegalStateException("not an array formula cell.");
                }
                ArrayRecord arec = _sharedValueManager.getArrayRecord(expRef.getRow(), expRef.getCol());
-        if (arec == null) {
-            throw new IllegalStateException("ArrayRecord was not found for the locator " + expRef.formatAsString());
-        }
+               if (arec == null) {
+                       throw new IllegalStateException("ArrayRecord was not found for the locator " + expRef.formatAsString());
+               }
                CellRangeAddress8Bit a = arec.getRange();
                return new CellRangeAddress(a.getFirstRow(), a.getLastRow(), a.getFirstColumn(),a.getLastColumn());
        }
-    
-    public void setArrayFormula(CellRangeAddress r, Ptg[] ptgs) {
+
+       public void setArrayFormula(CellRangeAddress r, Ptg[] ptgs) {
 
                ArrayRecord arr = new ArrayRecord(Formula.create(ptgs), new CellRangeAddress8Bit(r.getFirstRow(), r.getLastRow(), r.getFirstColumn(), r.getLastColumn()));
                _sharedValueManager.addArrayRecord(arr);
index ff7d4c0f2471dac4b22f4fe7bcd095599ea718cd..91c3e3b15c54cbcef3e037978ffb7992d5fb4305 100644 (file)
@@ -120,7 +120,7 @@ public final class SharedValueManager {
                return new SharedValueManager(
                        new SharedFormulaRecord[0], new CellReference[0], new ArrayRecord[0], new TableRecord[0]);
        }
-    private final List<ArrayRecord> _arrayRecords;
+       private final List<ArrayRecord> _arrayRecords;
        private final TableRecord[] _tableRecords;
        private final Map<SharedFormulaRecord, SharedFormulaGroup> _groupsBySharedFormulaRecord;
        /** cached for optimization purposes */
@@ -132,8 +132,7 @@ public final class SharedValueManager {
                if (nShF != firstCells.length) {
                        throw new IllegalArgumentException("array sizes don't match: " + nShF + "!=" + firstCells.length + ".");
                }
-               _arrayRecords = new ArrayList<ArrayRecord>();
-        _arrayRecords.addAll(Arrays.asList(arrayRecords));
+               _arrayRecords = toList(arrayRecords);
                _tableRecords = tableRecords;
                Map<SharedFormulaRecord, SharedFormulaGroup> m = new HashMap<SharedFormulaRecord, SharedFormulaGroup>(nShF * 3 / 2);
                for (int i = 0; i < nShF; i++) {
@@ -143,6 +142,25 @@ public final class SharedValueManager {
                _groupsBySharedFormulaRecord = m;
        }
 
+       /**
+        * @return a modifiable list, independent of the supplied array
+        */
+       private static <Z> List<Z> toList(Z[] zz) {
+               List<Z> result = new ArrayList<Z>(zz.length);
+               for (int i = 0; i < zz.length; i++) {
+                       result.add(zz[i]);
+               }
+               return result;
+       }
+
+       /**
+        * @param firstCells
+        * @param recs list of sheet records (possibly contains records for other parts of the Excel file)
+        * @param startIx index of first row/cell record for current sheet
+        * @param endIx one past index of last row/cell record for current sheet.  It is important
+        * that this code does not inadvertently collect <tt>SharedFormulaRecord</tt>s from any other
+        * sheet (which could happen if endIx is chosen poorly).  (see bug 44449)
+        */
        public static SharedValueManager create(SharedFormulaRecord[] sharedFormulaRecords,
                        CellReference[] firstCells, ArrayRecord[] arrayRecords, TableRecord[] tableRecords) {
                if (sharedFormulaRecords.length + firstCells.length + arrayRecords.length + tableRecords.length < 1) {
@@ -250,8 +268,7 @@ public final class SharedValueManager {
                // The first cell will be the top left in the range.  So we can match the
                // ARRAY/TABLE record directly.
 
-               for (int i = 0; i < _tableRecords.length; i++) {
-                       TableRecord tr = _tableRecords[i];
+               for (TableRecord tr : _tableRecords) {
                        if (tr.isFirstCell(row, column)) {
                                return tr;
                        }
@@ -270,46 +287,47 @@ public final class SharedValueManager {
         */
        public void unlink(SharedFormulaRecord sharedFormulaRecord) {
                SharedFormulaGroup svg = _groupsBySharedFormulaRecord.remove(sharedFormulaRecord);
-               _groups = null; // be sure to reset cached value
                if (svg == null) {
                        throw new IllegalStateException("Failed to find formulas for shared formula");
                }
+               _groups = null; // be sure to reset cached value
                svg.unlinkSharedFormulas();
        }
 
-    /**
-     * Add specified Array Record.
-     */
-    public void addArrayRecord(ArrayRecord ar) {
-        // could do a check here to make sure none of the ranges overlap
-        _arrayRecords.add(ar);
-    }
-
-    /**
-     * Removes the {@link ArrayRecord} for the cell group containing the specified cell.
-     * The caller should clear (set blank) all cells in the returned range.
-     * @return the range of the array formula which was just removed. Never <code>null</code>.
-     */
-    public CellRangeAddress8Bit removeArrayFormula(int rowIndex, int columnIndex) {
-        for (ArrayRecord ar : _arrayRecords) {
-            if (ar.isInRange(rowIndex, columnIndex)) {
-                _arrayRecords.remove(ar);
-                return ar.getRange();
-            }
-        }
-        throw new IllegalArgumentException("Specified cell is not part of an array formula.");
-    }
-
-    /**
-     * @return the shared ArrayRecord identified by (firstRow, firstColumn). never <code>null</code>.
-     */
-    public ArrayRecord getArrayRecord(int firstRow, int firstColumn) {
-        for(ArrayRecord ar : _arrayRecords) {
-            if(ar.isFirstCell(firstRow, firstColumn)) {
-                return ar;
-            }
-        }
-        return null;
-    }
-    
+       /**
+        * Add specified Array Record.
+        */
+       public void addArrayRecord(ArrayRecord ar) {
+               // could do a check here to make sure none of the ranges overlap
+               _arrayRecords.add(ar);
+       }
+
+       /**
+        * Removes the {@link ArrayRecord} for the cell group containing the specified cell.
+        * The caller should clear (set blank) all cells in the returned range.
+        * @return the range of the array formula which was just removed. Never <code>null</code>.
+        */
+       public CellRangeAddress8Bit removeArrayFormula(int rowIndex, int columnIndex) {
+               for (ArrayRecord ar : _arrayRecords) {
+                       if (ar.isInRange(rowIndex, columnIndex)) {
+                               _arrayRecords.remove(ar);
+                               return ar.getRange();
+                       }
+               }
+               String ref = new CellReference(rowIndex, columnIndex, false, false).formatAsString();
+               throw new IllegalArgumentException("Specified cell " + ref
+                               + " is not part of an array formula.");
+       }
+
+       /**
+        * @return the shared ArrayRecord identified by (firstRow, firstColumn). never <code>null</code>.
+        */
+       public ArrayRecord getArrayRecord(int firstRow, int firstColumn) {
+               for(ArrayRecord ar : _arrayRecords) {
+                       if(ar.isFirstCell(firstRow, firstColumn)) {
+                               return ar;
+                       }
+               }
+               return null;
+       }
 }
index c9462b3e1402f72542a383a979d079160666cbf3..a543477136da51638ed755884e5fa52da3712958 100644 (file)
@@ -52,8 +52,8 @@ import org.apache.poi.ss.usermodel.Comment;
 import org.apache.poi.ss.usermodel.Hyperlink;
 import org.apache.poi.ss.usermodel.RichTextString;
 import org.apache.poi.ss.util.CellRangeAddress;
-import org.apache.poi.ss.util.NumberToTextConverter;
 import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.ss.util.NumberToTextConverter;
 import org.apache.poi.ss.formula.FormulaType;
 import org.apache.poi.ss.SpreadsheetVersion;
 import org.apache.poi.util.POILogger;
@@ -1179,7 +1179,8 @@ public class HSSFCell implements Cell {
     public CellRangeAddress getArrayFormulaRange() {
         if (_cellType != CELL_TYPE_FORMULA) {
             String ref = new CellReference(this).formatAsString();
-            throw new IllegalStateException("Cell "+ref+" is not part of an array formula");
+            throw new IllegalStateException("Cell " + ref
+                    + " is not part of an array formula.");
         }
         return ((FormulaRecordAggregate)_record).getArrayFormulaRange();
     }
index e3fc7c20f0ee0e9bd441088f7debf092b0e28b38..251b02c7fb831d17db7e89cfb74159b3b1720d68 100644 (file)
@@ -53,10 +53,12 @@ import org.apache.poi.hssf.util.PaneInformation;
 import org.apache.poi.hssf.util.Region;
 import org.apache.poi.ss.formula.FormulaType;
 import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellRange;
 import org.apache.poi.ss.usermodel.CellStyle;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.ss.util.SSCellRange;
 import org.apache.poi.ss.SpreadsheetVersion;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
@@ -69,6 +71,8 @@ import org.apache.poi.util.POILogger;
  * @author  Shawn Laubach (slaubach at apache dot org) (Just a little)
  * @author  Jean-Pierre Paris (jean-pierre.paris at m4x dot org) (Just a little, too)
  * @author  Yegor Kozlov (yegor at apache.org) (Autosizing columns)
+ * @author  Josh Micich
+ * @author  Petr Udalau(Petr.Udalau at exigenservices.com) - set/remove array formulas
  */
 public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
     private static final POILogger log = POILogFactory.getLogger(HSSFSheet.class);
@@ -646,7 +650,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
      */
     public void setRightToLeft(boolean value)
     {
-           _sheet.getWindowTwo().setArabic(value);
+        _sheet.getWindowTwo().setArabic(value);
     }
 
     /**
@@ -656,7 +660,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
      */
     public boolean isRightToLeft()
     {
-           return _sheet.getWindowTwo().getArabic();
+        return _sheet.getWindowTwo().getArabic();
     }
 
     /**
@@ -1875,17 +1879,19 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
         return wb.getSheetName(idx);
     }
 
-    public HSSFCell[] setArrayFormula(String formula, CellRangeAddress range) {
-        HSSFCell[] cells = new HSSFCell[range.getNumberOfCells()];
-        int k = 0;
-
-        // make sure the formula parses OK first
-        int sheetIndex = _workbook.getSheetIndex(this);
-        Ptg[] ptgs = HSSFFormulaParser.parse(formula, _workbook, FormulaType.ARRAY, sheetIndex);
+    /**
+     * Also creates cells if they don't exist
+     */
+    private CellRange<HSSFCell> getCellRange(CellRangeAddress range) {
         int firstRow = range.getFirstRow();
         int firstColumn = range.getFirstColumn();
-        for (int rowIn = firstRow; rowIn <= range.getLastRow(); rowIn++) {
-            for (int colIn = firstColumn; colIn <= range.getLastColumn(); colIn++) {
+        int lastRow = range.getLastRow();
+        int lastColumn = range.getLastColumn();
+        int height = lastRow - firstRow + 1;
+        int width = lastColumn - firstColumn + 1;
+        List<HSSFCell> temp = new ArrayList<HSSFCell>(height*width);
+        for (int rowIn = firstRow; rowIn <= lastRow; rowIn++) {
+            for (int colIn = firstColumn; colIn <= lastColumn; colIn++) {
                 HSSFRow row = getRow(rowIn);
                 if (row == null) {
                     row = createRow(rowIn);
@@ -1894,38 +1900,45 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
                 if (cell == null) {
                     cell = row.createCell(colIn);
                 }
-                cell.setCellArrayFormula(range);
-                cells[k++] = cell;
+                temp.add(cell);
             }
         }
-        HSSFCell mainArrayFormulaCell = getRow(firstRow).getCell(firstColumn);
+        return SSCellRange.create(firstRow, firstColumn, height, width, temp, HSSFCell.class);
+    }
+
+    public CellRange<HSSFCell> setArrayFormula(String formula, CellRangeAddress range) {
+        // make sure the formula parses OK first
+        int sheetIndex = _workbook.getSheetIndex(this);
+        Ptg[] ptgs = HSSFFormulaParser.parse(formula, _workbook, FormulaType.ARRAY, sheetIndex);
+        CellRange<HSSFCell> cells = getCellRange(range);
+
+        for (HSSFCell c : cells) {
+            c.setCellArrayFormula(range);
+        }
+        HSSFCell mainArrayFormulaCell = cells.getTopLeftCell();
         FormulaRecordAggregate agg = (FormulaRecordAggregate)mainArrayFormulaCell.getCellValueRecord();
         agg.setArrayFormula(range, ptgs);
         return cells;
     }
 
 
-    public HSSFCell[] removeArrayFormula(Cell cell) {
-        ArrayList<HSSFCell> lst = new ArrayList<HSSFCell>();
+    public CellRange<HSSFCell> removeArrayFormula(Cell cell) {
+        if (cell.getSheet() != this) {
+            throw new IllegalArgumentException("Specified cell does not belong to this sheet.");
+        }
         CellValueRecordInterface rec = ((HSSFCell) cell).getCellValueRecord();
         if (!(rec instanceof FormulaRecordAggregate)) {
             String ref = new CellReference(cell).formatAsString();
-            throw new IllegalArgumentException("Cell " + ref + " is not part of an array formula");
+            throw new IllegalArgumentException("Cell " + ref + " is not part of an array formula.");
         }
         FormulaRecordAggregate fra = (FormulaRecordAggregate) rec;
         CellRangeAddress range = fra.removeArrayFormula(cell.getRowIndex(), cell.getColumnIndex());
-        if (range == null) {
-            String ref = new CellReference(cell).formatAsString();
-            throw new IllegalArgumentException("Cell " + ref + " is not part of an array formula");
-        }
+
+        CellRange<HSSFCell> result = getCellRange(range);
         // clear all cells in the range
-        for (int rowIn = range.getFirstRow(); rowIn <= range.getLastRow(); rowIn++) {
-            for (int colIn = range.getFirstColumn(); colIn <= range.getLastColumn(); colIn++) {
-                HSSFCell rCell = getRow(rowIn).getCell(colIn);
-                rCell.setCellType(Cell.CELL_TYPE_BLANK);
-                lst.add(rCell);
-             }
+        for (Cell c : result) {
+            c.setCellType(Cell.CELL_TYPE_BLANK);
         }
-        return lst.toArray(new HSSFCell[lst.size()]);
+        return result;
     }
 }
index 4d601fb640f2ba8ee01e60428cae747bd5b298c1..46f5c71f0c46f9c8753692b495379d4563aaada6 100644 (file)
@@ -72,10 +72,10 @@ final class OperandClassTransformer {
                        case FormulaType.CELL:
                                rootNodeOperandClass = Ptg.CLASS_VALUE;
                                break;
-            case FormulaType.ARRAY:
-                rootNodeOperandClass = Ptg.CLASS_ARRAY;
-                break;
-            case FormulaType.NAMEDRANGE:
+                       case FormulaType.ARRAY:
+                               rootNodeOperandClass = Ptg.CLASS_ARRAY;
+                               break;
+                       case FormulaType.NAMEDRANGE:
                        case FormulaType.DATAVALIDATION_LIST:
                                rootNodeOperandClass = Ptg.CLASS_REF;
                                break;
index 5adbde3babf8e390ec06cc4acdb806e0ffaa965a..66c9d38b3ab0e8ff53aadd7adb4685f22eb75614 100644 (file)
@@ -361,20 +361,17 @@ public interface Cell {
     void removeCellComment();
 
     /**
-     * Returns hyperlink associated with this cell
-     *
      * @return hyperlink associated with this cell or <code>null</code> if not found
      */
     Hyperlink getHyperlink();
 
     /**
-     * Assign a hypelrink to this cell
+     * Assign a hyperlink to this cell
      *
-     * @param link hypelrink associated with this cell
+     * @param link hyperlink associated with this cell
      */
     void setHyperlink(Hyperlink link);
 
-
     /**
      * Only valid for array formula cells
      *
@@ -386,5 +383,4 @@ public interface Cell {
      * @return <code>true</code> if this cell is part of group of cells having a common array formula.
      */
     boolean isPartOfArrayFormulaGroup();
-
 }
diff --git a/src/java/org/apache/poi/ss/usermodel/CellRange.java b/src/java/org/apache/poi/ss/usermodel/CellRange.java
new file mode 100644 (file)
index 0000000..9bdd4ba
--- /dev/null
@@ -0,0 +1,66 @@
+/* ====================================================================
+   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 java.util.Iterator;
+
+
+/**
+ * Represents a rectangular region of a {@link Sheet}
+ *
+ * @author Josh Micich
+ */
+public interface CellRange<C extends Cell> extends Iterable<C> {
+
+       int getWidth();
+       int getHeight();
+       /**
+        * Gets the number of cells in this range.
+        * @return <tt>height * width </tt>
+        */
+       int size();
+       String getReferenceText();
+
+       /**
+        * @return the cell at relative coordinates (0,0).  Never <code>null</code>.
+        */
+       C getTopLeftCell();
+
+       /**
+        * @param relativeRowIndex must be between <tt>0</tt> and <tt>height-1</tt>
+        * @param relativeColumnIndex must be between <tt>0</tt> and <tt>width-1</tt>
+        * @return the cell at the specified coordinates.  Never <code>null</code>.
+        */
+       C getCell(int relativeRowIndex, int relativeColumnIndex);
+       /**
+        * @return a flattened array of all the cells in this {@link CellRange}
+        */
+       C[] getFlattenedCells();
+       /**
+        * @return a 2-D array of all the cells in this {@link CellRange}.  The first
+        * array dimension is the row index (values <tt>0...height-1</tt>)
+        * and the second dimension is the column index (values <tt>0...width-1</tt>)
+        */
+       C[][] getCells();
+
+       /**
+        * @return an {@link Iterator} over all cells in this range.  Iteration starts
+        * with all cells in the first row followed by all cells in the next row, etc.
+        */
+       Iterator<C> iterator();
+}
index f8f8223da4ac2a758cdd26034f75b109cffafdf6..084155943810b410e8ef7091c092cdde75b54e40 100644 (file)
@@ -785,16 +785,17 @@ public interface Sheet extends Iterable<Row> {
     /**
      * Sets array formula to specified region for result.
      *
-     * @param formula Formula
-     * @param range   Region of array formula for result.
+     * @param formula text representation of the formula
+     * @param range Region of array formula for result.
+     * @return the {@link CellRange} of cells affected by this change
      */
-    Cell[] setArrayFormula(String formula, CellRangeAddress range);
+    CellRange<? extends Cell> setArrayFormula(String formula, CellRangeAddress range);
 
     /**
      * Remove a Array Formula from this sheet.  All cells contained in the Array Formula range are removed as well
      *
-     * @param cell any cell within Array Formula range
+     * @param cell   any cell within Array Formula range
+     * @return the {@link CellRange} of cells affected by this change
      */
-    Cell[] removeArrayFormula(Cell cell);
-
+    CellRange<? extends Cell> removeArrayFormula(Cell cell);
 }
index 65689cb4a0efa43d54a72cb2ff025fd3b0fa96ae..0b0f3a97299d2de7eeee0ff912fa80478dbfe1b1 100644 (file)
@@ -24,7 +24,7 @@ import org.apache.poi.util.LittleEndianOutput;
 
 /**
  * See OOO documentation: excelfileformat.pdf sec 2.5.14 - 'Cell Range Address'<p/>
- * 
+ *
  * Note - {@link SelectionRecord} uses the BIFF5 version of this structure
  * @author Dragos Buleandra (dragos.buleandra@trade2b.ro)
  */
@@ -51,7 +51,7 @@ public class CellRangeAddress extends CellRangeAddressBase {
                out.writeShort(getFirstColumn());
                out.writeShort(getLastColumn());
        }
-       
+
        public CellRangeAddress(RecordInputStream in) {
                super(readUShortAndCheck(in), in.readUShort(), in.readUShort(), in.readUShort());
        }
@@ -72,6 +72,10 @@ public class CellRangeAddress extends CellRangeAddressBase {
                return numberOfItems * ENCODED_SIZE;
        }
 
+    /**
+     * @return the text format of this range.  Single cell ranges are formatted
+     *         like single cell references (e.g. 'A1' instead of 'A1:A1').
+     */
     public String formatAsString() {
         StringBuffer sb = new StringBuffer();
         CellReference cellRefFrom = new CellReference(getFirstRow(), getFirstColumn());
index e542962cbae4580ad44df2f076b809765a4a3d93..5acebcef16a4046d8c29396dd915baaf990f8097 100644 (file)
@@ -114,10 +114,10 @@ public class CellReference {
        public CellReference(int pRow, short pCol) {
                this(pRow, pCol & 0xFFFF, false, false);
        }
-    
-    public CellReference(Cell cell) {
-        this(cell.getRowIndex(), cell.getColumnIndex(), false, false);
-    }
+
+       public CellReference(Cell cell) {
+               this(cell.getRowIndex(), cell.getColumnIndex(), false, false);
+       }
 
        public CellReference(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
                this(null, pRow, pCol, pAbsRow, pAbsCol);
@@ -489,21 +489,22 @@ public class CellReference {
                sb.append(_rowIndex+1);
        }
 
-    /**
-     * Checks whether this cell reference is equal to another object.
-     * <p>
-     *  Two cells references are assumed to be equal if their string representations
-     *  ({@link #formatAsString()}  are equal.
-     * </p>
-     */
-    @Override
-    public boolean equals(Object o){
-        if(o == null || !(o instanceof CellReference)) {
-            return false;
-        }
-
-        String me = formatAsString();
-        String anotherRef = ((CellReference)o).formatAsString();
-        return me.equals(anotherRef);
-    }
+       /**
+        * Checks whether this cell reference is equal to another object.
+        * <p>
+        *  Two cells references are assumed to be equal if their string representations
+        *  ({@link #formatAsString()}  are equal.
+        * </p>
+        */
+       @Override
+       public boolean equals(Object o){
+               if(!(o instanceof CellReference)) {
+                       return false;
+               }
+               CellReference cr = (CellReference) o;
+               return _rowIndex == cr._rowIndex
+                       && _colIndex == cr._colIndex
+                       && _isRowAbs == cr._isColAbs
+                       && _isColAbs == cr._isColAbs;
+       }
 }
diff --git a/src/java/org/apache/poi/ss/util/SSCellRange.java b/src/java/org/apache/poi/ss/util/SSCellRange.java
new file mode 100644 (file)
index 0000000..c1973b4
--- /dev/null
@@ -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.util;
+
+import java.lang.reflect.Array;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellRange;
+import org.apache.poi.util.Internal;
+
+/**
+ * For POI internal use only
+ *
+ * @author Josh Micich
+ */
+@Internal
+public final class SSCellRange<K extends Cell> implements CellRange<K> {
+
+       private final int _height;
+       private final int _width;
+       private final K[] _flattenedArray;
+       private final int _firstRow;
+       private final int _firstColumn;
+
+       private SSCellRange(int firstRow, int firstColumn, int height, int width, K[] flattenedArray) {
+               _firstRow = firstRow;
+               _firstColumn = firstColumn;
+               _height = height;
+               _width = width;
+               _flattenedArray = flattenedArray;
+       }
+
+       public static <B extends Cell> SSCellRange<B> create(int firstRow, int firstColumn, int height, int width, List<B> flattenedList, Class<B> cellClass) {
+               int nItems = flattenedList.size();
+               if (height * width != nItems) {
+                       throw new IllegalArgumentException("Array size mismatch.");
+               }
+
+               @SuppressWarnings("unchecked")
+               B[] flattenedArray = (B[]) Array.newInstance(cellClass, nItems);
+               flattenedList.toArray(flattenedArray);
+               return new SSCellRange<B>(firstRow, firstColumn, height, width, flattenedArray);
+       }
+
+       public int getHeight() {
+               return _height;
+       }
+       public int getWidth() {
+               return _width;
+       }
+       public int size() {
+               return _height*_width;
+       }
+
+       public String getReferenceText() {
+               CellRangeAddress cra = new CellRangeAddress(_firstRow, _firstRow+_height-1, _firstColumn, _firstColumn+_width-1);
+               return cra.formatAsString();
+       }
+
+       public K getTopLeftCell() {
+               return _flattenedArray[0];
+       }
+
+       public K getCell(int relativeRowIndex, int relativeColumnIndex) {
+               if (relativeRowIndex < 0 || relativeRowIndex >= _height) {
+                       throw new ArrayIndexOutOfBoundsException("Specified row " + relativeRowIndex
+                                       + " is outside the allowable range (0.." + (_height-1) + ").");
+               }
+               if (relativeColumnIndex < 0 || relativeColumnIndex >= _width) {
+                       throw new ArrayIndexOutOfBoundsException("Specified colummn " + relativeColumnIndex
+                                       + " is outside the allowable range (0.." + (_width-1) + ").");
+               }
+               int flatIndex = _width * relativeRowIndex + relativeColumnIndex;
+               return _flattenedArray[flatIndex];
+       }
+       public K[] getFlattenedCells() {
+               return _flattenedArray.clone();
+       }
+
+       public K[][] getCells() {
+               Class<?> itemCls = _flattenedArray.getClass();
+               @SuppressWarnings("unchecked")
+               K[][] result = (K[][]) Array.newInstance(itemCls, _height);
+               itemCls = itemCls.getComponentType();
+               for (int r=_height-1; r>=0; r--) {
+                       @SuppressWarnings("unchecked")
+                       K[] row = (K[]) Array.newInstance(itemCls, _width);
+                       int flatIndex = _width * r;
+                       System.arraycopy(_flattenedArray, flatIndex, row, 0, _width);
+               }
+               return result;
+       }
+       public Iterator<K> iterator() {
+               return new ArrayIterator<K>(_flattenedArray);
+       }
+       private static final class ArrayIterator<D> implements Iterator<D> {
+
+               private final D[] _array;
+               private int _index;
+
+               public ArrayIterator(D[] array) {
+                       _array = array;
+                       _index = 0;
+               }
+               public boolean hasNext() {
+                       return _index < _array.length;
+               }
+               public D next() {
+                       if (_index >= _array.length) {
+                               throw new NoSuchElementException(String.valueOf(_index));
+                       }
+                       return _array[_index++];
+               }
+
+               public void remove() {
+                       throw new UnsupportedOperationException("Cannot remove cells from this CellRange.");
+               }
+       }
+}
index 6a5bcd48022e4cf5957f7359d05cebd34374506f..f2a4c497b09c9c4be729a7f54101a794b97e2b93 100644 (file)
@@ -969,29 +969,16 @@ public final class XSSFCell implements Cell {
         throw new IllegalStateException("Unexpected formula result type (" + cellType + ")");
     }
 
-    /**
-     * If this cell is part of an array formula, returns a CellRangeAddress object
-     * that represents the entire array. 
-     *
-     * @return the range of the array formula group that this cell belongs to.
-     * @throws IllegalStateException if this cell is not part of an array formula
-     * @see #isPartOfArrayFormulaGroup()
-     */
     public CellRangeAddress getArrayFormulaRange() {
         XSSFCell cell = getSheet().getFirstCellInArrayFormula(this);
         if (cell == null) {
-            throw new IllegalStateException("Cell " + _cell.getR() + " is not part of an array formula");
+            throw new IllegalStateException("Cell " + _cell.getR()
+                    + " is not part of an array formula.");
         }
         String formulaRef = cell._cell.getF().getRef();
         return CellRangeAddress.valueOf(formulaRef);
     }
 
-    /**
-     * Test if this cell is included in an array formula
-     *
-     * @return true if this cell is part of an array formula
-     * @see #getArrayFormulaRange()
-     */
     public boolean isPartOfArrayFormulaGroup() {
         return getSheet().isCellInArrayFormulaContext(this);
     }
index 0574a000b224dff6b7dafdec28c9792463d04726..61b7d3fe10dc6ec10efcc68d1994d37f5b76738e 100644 (file)
@@ -40,6 +40,7 @@ import org.apache.poi.openxml4j.opc.PackageRelationship;
 import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
 import org.apache.poi.ss.SpreadsheetVersion;
 import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellRange;
 import org.apache.poi.ss.usermodel.CellStyle;
 import org.apache.poi.ss.usermodel.Footer;
 import org.apache.poi.ss.usermodel.Header;
@@ -47,9 +48,10 @@ import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.ss.util.SSCellRange;
+import org.apache.poi.util.Internal;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
-import org.apache.poi.util.Internal;
 import org.apache.poi.xssf.model.CommentsTable;
 import org.apache.poi.xssf.usermodel.helpers.ColumnHelper;
 import org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter;
@@ -2323,7 +2325,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
         }
         if (f != null && f.getT() == STCellFormulaType.ARRAY && f.getRef() != null) {
             arrayFormulas.add(CellRangeAddress.valueOf(f.getRef()));
-    }
+        }
     }
 
     @Override
@@ -2701,85 +2703,57 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
     }
 
     /**
-     * Sets array formula to the specified range of cells.
-     * <p>
-     *  Note, that this method silently creates cells in the
-     *  specified range if they don't exist.
-     * </p>
-     * Example:
-     * <blockquote><pre>
-     *  Workbook workbook = new XSSFWorkbook();
-     *  Sheet sheet = workbook.createSheet();
-     *  CellRangeAddress range = CellRangeAddress.valueOf("C1:C3");
-     *  Cell[] cells = sheet.setArrayFormula("A1:A3*B1:B3", range);
-     * </pre></blockquote>
-     *  Three cells in the C1:C3 range are created and returned.
-     *
-     * @param formula the formula to set
-     * @param range Region of array formula for result.
-     * @return the array of cells that represent the entire formula array
-     * @throws org.apache.poi.ss.formula.FormulaParseException if
-     *   the formula has incorrect syntax or is otherwise invalid
-     */
-     public XSSFCell[] setArrayFormula(String formula, CellRangeAddress range) {
-        XSSFRow row = getRow(range.getFirstRow());
-        if (row == null) {
-            row = createRow(range.getFirstRow());
-        }
-        XSSFCell mainArrayFormulaCell = row.getCell(range.getFirstColumn());
-        if (mainArrayFormulaCell == null) {
-            mainArrayFormulaCell = row.createCell(range.getFirstColumn());
-        }
-        mainArrayFormulaCell.setCellArrayFormula(formula, range);
-        arrayFormulas.add(range);
-
-        XSSFCell[] cells = new XSSFCell[range.getNumberOfCells()];
-        int k = 0;
-        for (int rowIndex = range.getFirstRow(); rowIndex <= range.getLastRow(); rowIndex++) {
-            row = getRow(rowIndex);
-            if (row == null) {
-                row = createRow(rowIndex);
-            }
-            for (int columnIndex = range.getFirstColumn(); columnIndex <= range.getLastColumn(); columnIndex++) {
-                XSSFCell arrayFormulaCell = row.getCell(columnIndex);
-                if (arrayFormulaCell == null) {
-                    arrayFormulaCell = row.createCell(columnIndex);
+     * Also creates cells if they don't exist
+     */
+    private CellRange<XSSFCell> getCellRange(CellRangeAddress range) {
+        int firstRow = range.getFirstRow();
+        int firstColumn = range.getFirstColumn();
+        int lastRow = range.getLastRow();
+        int lastColumn = range.getLastColumn();
+        int height = lastRow - firstRow + 1;
+        int width = lastColumn - firstColumn + 1;
+        List<XSSFCell> temp = new ArrayList<XSSFCell>(height*width);
+        for (int rowIn = firstRow; rowIn <= lastRow; rowIn++) {
+            for (int colIn = firstColumn; colIn <= lastColumn; colIn++) {
+                XSSFRow row = getRow(rowIn);
+                if (row == null) {
+                    row = createRow(rowIn);
+                }
+                XSSFCell cell = row.getCell(colIn);
+                if (cell == null) {
+                    cell = row.createCell(colIn);
                 }
-                cells[k++] = arrayFormulaCell;
+                temp.add(cell);
             }
         }
-        return cells;
+        return SSCellRange.create(firstRow, firstColumn, height, width, temp, XSSFCell.class);
     }
 
-    /**
-     * Remove an Array Formula from this sheet.
-     * <p>
-     * All cells contained in the Array Formula range are removed as well
-     * </p>
-     *
-     * @param cell   any cell within Array Formula range
-     * @return the array of affected cells.
-     * @throws IllegalArgumentException if the specified cell is not part of an array formula
-     */
-    public XSSFCell[] removeArrayFormula(Cell cell) {
-        ArrayList<XSSFCell> lst = new ArrayList<XSSFCell>();
+    public CellRange<XSSFCell> setArrayFormula(String formula, CellRangeAddress range) {
+
+        CellRange<XSSFCell> cr = getCellRange(range);
+
+        XSSFCell mainArrayFormulaCell = cr.getTopLeftCell();
+        mainArrayFormulaCell.setCellArrayFormula(formula, range);
+        arrayFormulas.add(range);
+        return cr;
+    }
+
+    public CellRange<XSSFCell> removeArrayFormula(Cell cell) {
+        if (cell.getSheet() != this) {
+            throw new IllegalArgumentException("Specified cell does not belong to this sheet.");
+        }
         for (CellRangeAddress range : arrayFormulas) {
             if (range.isInRange(cell.getRowIndex(), cell.getColumnIndex())) {
                 arrayFormulas.remove(range);
-                for (int rowIndex = range.getFirstRow(); rowIndex <= range.getLastRow(); rowIndex++) {
-                    XSSFRow row = getRow(rowIndex);
-                    for (int columnIndex = range.getFirstColumn(); columnIndex <= range.getLastColumn(); columnIndex++) {
-                        XSSFCell arrayFormulaCell = row.getCell(columnIndex);
-                        if (arrayFormulaCell != null) {
-                            arrayFormulaCell.setCellType(Cell.CELL_TYPE_BLANK);
-                            lst.add(arrayFormulaCell);
-                        }
-                    }
+                CellRange<XSSFCell> cr = getCellRange(range);
+                for (XSSFCell c : cr) {
+                    c.setCellType(Cell.CELL_TYPE_BLANK);
                 }
-                return lst.toArray(new XSSFCell[lst.size()]);
+                return cr;
             }
         }
         String ref = ((XSSFCell)cell).getCTCell().getR();
-        throw new IllegalArgumentException("Cell " + ref + " is not part of an array formula");
+        throw new IllegalArgumentException("Cell " + ref + " is not part of an array formula.");
     }
 }
index 1f5b31a4861d04b8c7bb9a5351f9718e16dc21bf..5c5f69e6e5c4e9bee46312ed1a8785cbb0a6b333 100644 (file)
@@ -28,7 +28,7 @@ import org.apache.poi.xssf.usermodel.helpers.TestHeaderFooterHelper;
 
 /**
  * Collects all tests for <tt>org.apache.poi.xssf.usermodel</tt> and sub-packages.
- * 
+ *
  * @author Josh Micich
  */
 public final class AllXSSFUsermodelTests {
@@ -54,12 +54,13 @@ public final class AllXSSFUsermodelTests {
                result.addTestSuite(TestXSSFRichTextString.class);
                result.addTestSuite(TestXSSFRow.class);
                result.addTestSuite(TestXSSFSheet.class);
+               result.addTestSuite(TestXSSFSheetUpdateArrayFormulas.class);
                result.addTestSuite(TestXSSFWorkbook.class);
 
                result.addTestSuite(TestXSSFBorder.class);
                result.addTestSuite(TestXSSFCellFill.class);
                result.addTestSuite(TestXSSFSheetComments.class);
-               
+
                result.addTestSuite(TestColumnHelper.class);
                result.addTestSuite(TestHeaderFooterHelper.class);
 
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFArrayFormulas.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFArrayFormulas.java
deleted file mode 100644 (file)
index b00665c..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/* ====================================================================
-   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.xssf.usermodel;
-
-import org.apache.poi.ss.usermodel.BaseTestArrayFormulas;
-import org.apache.poi.ss.util.CellRangeAddress;
-import org.apache.poi.xssf.XSSFITestDataProvider;
-import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula;
-import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType;
-
-/**
- * Test array formulas in XSSF
- *
- * @author Yegor Kozlov
- */
-public final class TestXSSFArrayFormulas extends BaseTestArrayFormulas {
-
-    @Override
-    protected XSSFITestDataProvider getTestDataProvider(){
-        return XSSFITestDataProvider.getInstance();
-    }
-
-    public void testXSSFSetArrayFormula_singleCell() {
-        XSSFWorkbook workbook = getTestDataProvider().createWorkbook();
-        XSSFSheet sheet = workbook.createSheet();
-
-        // row 3 does not yet exist
-        assertNull(sheet.getRow(2));
-        CellRangeAddress range = new CellRangeAddress(2, 2, 2, 2);
-        XSSFCell[] cells = sheet.setArrayFormula("SUM(C11:C12*D11:D12)", range);
-        assertEquals(1, cells.length);
-
-        // sheet.setArrayFormula creates rows and cells for the designated range
-        assertNotNull(sheet.getRow(2));
-        XSSFCell cell = sheet.getRow(2).getCell(2);
-        assertNotNull(cell);
-
-        assertTrue(cell.isPartOfArrayFormulaGroup());
-        assertSame(cells[0], sheet.getFirstCellInArrayFormula(cells[0]));
-        //retrieve the range and check it is the same
-        assertEquals(range.formatAsString(), cell.getArrayFormulaRange().formatAsString());
-
-        //check the CTCellFormula bean
-        CTCellFormula f = cell.getCTCell().getF();
-        assertEquals("SUM(C11:C12*D11:D12)", f.getStringValue());
-        assertEquals("C3", f.getRef());
-        assertEquals(STCellFormulaType.ARRAY, f.getT());
-
-    }
-
-    public void testXSSFSetArrayFormula_multiCell() {
-        XSSFCell[] cells;
-
-        XSSFWorkbook workbook = getTestDataProvider().createWorkbook();
-        XSSFSheet sheet = workbook.createSheet();
-        
-        CellRangeAddress range = new CellRangeAddress(3, 5, 2, 2);
-        assertEquals("C4:C6", range.formatAsString());
-        cells = sheet.setArrayFormula("SUM(A1:A3*B1:B3)", range);
-        assertEquals(3, cells.length);
-
-        // sheet.setArrayFormula creates rows and cells for the designated range
-        assertEquals("C4", cells[0].getCTCell().getR());
-        assertEquals("C5", cells[1].getCTCell().getR());
-        assertEquals("C6", cells[2].getCTCell().getR());
-        assertSame(cells[0], sheet.getFirstCellInArrayFormula(cells[0]));
-
-        /*
-         * From the spec:
-         * For a multi-cell formula, the c elements for all cells except the top-left
-         * cell in that range shall not have an f element;
-         */
-
-        //the first cell has an f element
-        CTCellFormula f = cells[0].getCTCell().getF();
-        assertEquals("SUM(A1:A3*B1:B3)", f.getStringValue());
-        assertEquals("C4:C6", f.getRef());
-        assertEquals(STCellFormulaType.ARRAY, f.getT());
-        //the other two cells don't have an f element
-        assertNull(cells[1].getCTCell().getF());
-        assertNull(cells[2].getCTCell().getF());
-    }
-}
\ No newline at end of file
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheetUpdateArrayFormulas.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheetUpdateArrayFormulas.java
new file mode 100644 (file)
index 0000000..c8e5357
--- /dev/null
@@ -0,0 +1,109 @@
+/* ====================================================================
+   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.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.util.CellRangeAddress;
+import org.apache.poi.xssf.XSSFITestDataProvider;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType;
+/**
+ * Test array formulas in XSSF
+ *
+ * @author Yegor Kozlov
+ * @author Josh Micich
+ */
+public final class TestXSSFSheetUpdateArrayFormulas extends BaseTestSheetUpdateArrayFormulas {
+
+    public TestXSSFSheetUpdateArrayFormulas() {
+        super(XSSFITestDataProvider.getInstance());
+    }
+
+    // Test methods common with HSSF are in superclass
+    // Local methods here test XSSF-specific details of updating array formulas
+
+    public void testXSSFSetArrayFormula_singleCell() {
+        CellRange<XSSFCell> cells;
+
+        XSSFWorkbook workbook = new XSSFWorkbook();
+        XSSFSheet sheet = workbook.createSheet();
+
+        // 1. single-cell array formula
+        String formula1 = "123";
+        CellRangeAddress range = CellRangeAddress.valueOf("C3:C3");
+        cells = sheet.setArrayFormula(formula1, range);
+        assertEquals(1, cells.size());
+
+        // check getFirstCell...
+        XSSFCell firstCell = cells.getTopLeftCell();
+        assertSame(firstCell, sheet.getFirstCellInArrayFormula(firstCell));
+        //retrieve the range and check it is the same
+        assertEquals(range.formatAsString(), firstCell.getArrayFormulaRange().formatAsString());
+        confirmArrayFormulaCell(firstCell, "C3", formula1, "C3");
+    }
+
+    public void testXSSFSetArrayFormula_multiCell() {
+        CellRange<XSSFCell> cells;
+
+        String formula2 = "456";
+        XSSFWorkbook workbook = new XSSFWorkbook();
+        XSSFSheet sheet = workbook.createSheet();
+
+        CellRangeAddress range = CellRangeAddress.valueOf("C4:C6");
+        cells = sheet.setArrayFormula(formula2, range);
+        assertEquals(3, cells.size());
+
+        // sheet.setArrayFormula creates rows and cells for the designated range
+        /*
+         * From the spec:
+         * For a multi-cell formula, the c elements for all cells except the top-left
+         * cell in that range shall not have an f element;
+         */
+        // Check that each cell exists and that the formula text is set correctly on the first cell
+        XSSFCell firstCell = cells.getTopLeftCell();
+        confirmArrayFormulaCell(firstCell, "C4", formula2, "C4:C6");
+        confirmArrayFormulaCell(cells.getCell(1, 0), "C5");
+        confirmArrayFormulaCell(cells.getCell(2, 0), "C6");
+
+        assertSame(firstCell, sheet.getFirstCellInArrayFormula(firstCell));
+    }
+
+    private static void confirmArrayFormulaCell(XSSFCell c, String cellRef) {
+        confirmArrayFormulaCell(c, cellRef, null, null);
+    }
+    private static void confirmArrayFormulaCell(XSSFCell c, String cellRef, String formulaText, String arrayRangeRef) {
+        if (c == null) {
+            throw new AssertionFailedError("Cell should not be null.");
+        }
+        CTCell ctCell = c.getCTCell();
+        assertEquals(cellRef, ctCell.getR());
+        if (formulaText == null) {
+            assertFalse(ctCell.isSetF());
+            assertNull(ctCell.getF());
+        } else {
+            CTCellFormula f = ctCell.getF();
+            assertEquals(arrayRangeRef, f.getRef());
+            assertEquals(formulaText, f.getStringValue());
+            assertEquals(STCellFormulaType.ARRAY, f.getT());
+        }
+    }
+}
index 867bfc35f6d5bb4db60ef973a8c219171989abc1..b5b9724c6b3f7c95b37ddaf69e3bdffe4c50662c 100644 (file)
@@ -45,8 +45,8 @@ public final class TestFormulaRecordAggregate extends TestCase {
                s.setString("abc");
                FormulaRecordAggregate fagg = new FormulaRecordAggregate(f, s, SharedValueManager.createEmpty());
                assertEquals("abc", fagg.getStringValue());
-        assertFalse(fagg.isPartOfArrayFormula());
-    }
+               assertFalse(fagg.isPartOfArrayFormula());
+       }
 
        /**
         * Sometimes a {@link StringRecord} appears after a {@link FormulaRecord} even though the
@@ -79,26 +79,26 @@ public final class TestFormulaRecordAggregate extends TestCase {
                assertEquals(fr, vraRecs[0]);
        }
 
-    public void testArrayFormulas() {
-        int rownum = 4;
-        int colnum = 4;
+       public void testArrayFormulas() {
+               int rownum = 4;
+               int colnum = 4;
 
-        FormulaRecord fr = new FormulaRecord();
-        fr.setRow(rownum);
-        fr.setColumn((short)colnum);
+               FormulaRecord fr = new FormulaRecord();
+               fr.setRow(rownum);
+               fr.setColumn((short)colnum);
 
-        FormulaRecordAggregate agg = new FormulaRecordAggregate(fr, null, SharedValueManager.createEmpty());
-        Ptg[] ptgsForCell = {new ExpPtg(rownum, colnum)};
-        agg.setParsedExpression(ptgsForCell);
+               FormulaRecordAggregate agg = new FormulaRecordAggregate(fr, null, SharedValueManager.createEmpty());
+               Ptg[] ptgsForCell = {new ExpPtg(rownum, colnum)};
+               agg.setParsedExpression(ptgsForCell);
 
-        String formula = "SUM(A1:A3*B1:B3)";
-        Ptg[] ptgs = HSSFFormulaParser.parse(formula, null, FormulaType.ARRAY, 0);
-        agg.setArrayFormula(new CellRangeAddress(rownum, rownum, colnum, colnum), ptgs);
+               String formula = "SUM(A1:A3*B1:B3)";
+               Ptg[] ptgs = HSSFFormulaParser.parse(formula, null, FormulaType.ARRAY, 0);
+               agg.setArrayFormula(new CellRangeAddress(rownum, rownum, colnum, colnum), ptgs);
 
-        assertTrue(agg.isPartOfArrayFormula());
-        assertEquals("E5", agg.getArrayFormulaRange().formatAsString());
-        Ptg[] ptg = agg.getFormulaTokens();
-        String fmlaSer = FormulaRenderer.toFormulaString(null, ptg);
-        assertEquals(formula, fmlaSer);
-    }
+               assertTrue(agg.isPartOfArrayFormula());
+               assertEquals("E5", agg.getArrayFormulaRange().formatAsString());
+               Ptg[] ptg = agg.getFormulaTokens();
+               String fmlaSer = FormulaRenderer.toFormulaString(null, ptg);
+               assertEquals(formula, fmlaSer);
+       }
 }
index 66e1c84f0349e15b78bba19b0216a1ae5546e8f1..7e359a3fb1d1b5dfb069516d800368b0fb025ba3 100644 (file)
@@ -17,6 +17,7 @@
 
 package org.apache.poi.hssf.record.aggregates;
 
+import java.lang.reflect.Field;
 import java.util.Collection;
 import java.util.HashMap;
 
@@ -169,4 +170,26 @@ public final class TestSharedValueManager extends TestCase {
                }
                assertEquals("$AF24*A$7", formulaText);
        }
+
+       /**
+        * Convenience test method for digging the {@link SharedValueManager} out of a
+        * {@link RowRecordsAggregate}.
+        */
+       public static SharedValueManager extractFromRRA(RowRecordsAggregate rra) {
+               Field f;
+               try {
+                       f = RowRecordsAggregate.class.getDeclaredField("_sharedValueManager");
+               } catch (NoSuchFieldException e) {
+                       throw new RuntimeException(e);
+               }
+
+               f.setAccessible(true);
+               try {
+                       return (SharedValueManager) f.get(rra);
+               } catch (IllegalArgumentException e) {
+                       throw new RuntimeException(e);
+               } catch (IllegalAccessException e) {
+                       throw new RuntimeException(e);
+               }
+       }
 }
index eec05641afc2c2cc18a762b3468196e2ffb741f7..08d35bf6aff8d2a8bff737defa2fc97b252ae7bc 100644 (file)
@@ -22,14 +22,14 @@ import junit.framework.TestSuite;
 
 /**
  * Collects all tests for the <tt>org.apache.poi.hssf.usermodel</tt> package.
- * 
+ *
  * @author Josh Micich
  */
 public class AllUserModelTests {
-       
+
        public static Test suite() {
                TestSuite result = new TestSuite(AllUserModelTests.class.getName());
-               
+
                result.addTestSuite(TestBugs.class);
                result.addTestSuite(TestCellStyle.class);
                result.addTestSuite(TestCloneSheet.class);
@@ -57,6 +57,7 @@ public class AllUserModelTests {
                result.addTestSuite(TestHSSFRichTextString.class);
                result.addTestSuite(TestHSSFRow.class);
                result.addTestSuite(TestHSSFSheet.class);
+               result.addTestSuite(TestHSSFSheetUpdateArrayFormulas.class);
                result.addTestSuite(TestHSSFTextbox.class);
                result.addTestSuite(TestHSSFWorkbook.class);
                result.addTestSuite(TestHSSFName.class);
@@ -71,8 +72,8 @@ public class AllUserModelTests {
                }
                result.addTestSuite(TestUnicodeWorkbook.class);
                result.addTestSuite(TestUppercaseWorkbook.class);
-               result.addTestSuite(TestWorkbook.class);                
-               
+               result.addTestSuite(TestWorkbook.class);
+
                return result;
        }
 }
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFArrayFormulas.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFArrayFormulas.java
deleted file mode 100644 (file)
index fb86b0a..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/* ====================================================================
-   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.hssf.usermodel;
-
-import org.apache.poi.ss.usermodel.BaseTestArrayFormulas;
-import org.apache.poi.ss.util.CellRangeAddress;
-import org.apache.poi.hssf.HSSFITestDataProvider;
-import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
-
-/**
- * Test array formulas in HSSF
- *
- * @author Yegor Kozlov
- */
-public final class TestHSSFArrayFormulas extends BaseTestArrayFormulas {
-
-    @Override
-    protected HSSFITestDataProvider getTestDataProvider(){
-        return HSSFITestDataProvider.getInstance();
-    }
-
-    public void testHSSFSetArrayFormula_singleCell() {
-        HSSFWorkbook workbook = getTestDataProvider().createWorkbook();
-        HSSFSheet sheet = workbook.createSheet();
-
-        CellRangeAddress range = new CellRangeAddress(2, 2, 2, 2);
-        HSSFCell[] cells = sheet.setArrayFormula("SUM(C11:C12*D11:D12)", range);
-        assertEquals(1, cells.length);
-
-        // sheet.setArrayFormula creates rows and cells for the designated range
-        assertNotNull(sheet.getRow(2));
-        HSSFCell cell = sheet.getRow(2).getCell(2);
-        assertNotNull(cell);
-
-        assertTrue(cell.isPartOfArrayFormulaGroup());
-        //retrieve the range and check it is the same
-        assertEquals(range.formatAsString(), cell.getArrayFormulaRange().formatAsString());
-
-        FormulaRecordAggregate agg = (FormulaRecordAggregate)cell.getCellValueRecord();
-        assertEquals(range.formatAsString(), agg.getArrayFormulaRange().formatAsString());
-        assertTrue(agg.isPartOfArrayFormula());
-
-    }
-
-}
\ No newline at end of file
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheetUpdateArrayFormulas.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheetUpdateArrayFormulas.java
new file mode 100644 (file)
index 0000000..625ffdb
--- /dev/null
@@ -0,0 +1,122 @@
+/* ====================================================================
+   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.hssf.usermodel;
+
+import junit.framework.AssertionFailedError;
+
+import org.apache.poi.hssf.HSSFITestDataProvider;
+import org.apache.poi.hssf.record.ArrayRecord;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.Record;
+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.util.CellRangeAddress;
+
+/**
+ * Test array formulas in HSSF
+ *
+ * @author Yegor Kozlov
+ * @author Josh Micich
+ */
+public final class TestHSSFSheetUpdateArrayFormulas extends BaseTestSheetUpdateArrayFormulas {
+
+    public TestHSSFSheetUpdateArrayFormulas() {
+        super(HSSFITestDataProvider.getInstance());
+    }
+
+    // Test methods common with XSSF are in superclass
+    // Local methods here test HSSF-specific details of updating array formulas
+
+    public void testHSSFSetArrayFormula_singleCell() {
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFSheet sheet = workbook.createSheet("Sheet1");
+
+        CellRangeAddress range = new CellRangeAddress(2, 2, 2, 2);
+        HSSFCell[] cells = sheet.setArrayFormula("SUM(C11:C12*D11:D12)", range).getFlattenedCells();
+        assertEquals(1, cells.length);
+
+        // sheet.setArrayFormula creates rows and cells for the designated range
+        assertNotNull(sheet.getRow(2));
+        HSSFCell cell = sheet.getRow(2).getCell(2);
+        assertNotNull(cell);
+
+        assertTrue(cell.isPartOfArrayFormulaGroup());
+        //retrieve the range and check it is the same
+        assertEquals(range.formatAsString(), cell.getArrayFormulaRange().formatAsString());
+
+        FormulaRecordAggregate agg = (FormulaRecordAggregate)cell.getCellValueRecord();
+        assertEquals(range.formatAsString(), agg.getArrayFormulaRange().formatAsString());
+        assertTrue(agg.isPartOfArrayFormula());
+    }
+
+    /**
+     * Makes sure the internal state of HSSFSheet is consistent after removing array formulas
+     */
+    public void testAddRemoveArrayFormulas_recordUpdates() {
+        HSSFWorkbook wb = new HSSFWorkbook();
+        HSSFSheet s = wb.createSheet("Sheet1");
+
+        CellRange<HSSFCell> cr = s.setArrayFormula("123", CellRangeAddress.valueOf("B5:C6"));
+        Record[] recs;
+        int ix;
+        recs = RecordInspector.getRecords(s, 0);
+        ix = findRecordOfType(recs, ArrayRecord.class, 0);
+        confirmRecordClass(recs, ix-1, FormulaRecord.class);
+        confirmRecordClass(recs, ix+1, FormulaRecord.class);
+        confirmRecordClass(recs, ix+2, FormulaRecord.class);
+        confirmRecordClass(recs, ix+3, FormulaRecord.class);
+        // just one array record
+        assertTrue(findRecordOfType(recs, ArrayRecord.class, ix+1) < 0);
+
+        s.removeArrayFormula(cr.getTopLeftCell());
+
+        // Make sure the array formula has been removed properly
+
+        recs = RecordInspector.getRecords(s, 0);
+        assertTrue(findRecordOfType(recs, ArrayRecord.class, 0) < 0);
+        assertTrue(findRecordOfType(recs, FormulaRecord.class, 0) < 0);
+        RowRecordsAggregate rra = s.getSheet().getRowsAggregate();
+        SharedValueManager svm = TestSharedValueManager.extractFromRRA(rra);
+        if (svm.getArrayRecord(4, 1) != null) {
+            throw new AssertionFailedError("Array record was not cleaned up properly.");
+        }
+    }
+
+    private static void confirmRecordClass(Record[] recs, int index, Class<? extends Record> cls) {
+        if (recs.length <= index) {
+            throw new AssertionFailedError("Expected (" + cls.getName() + ") at index "
+                    + index + " but array length is " + recs.length + ".");
+        }
+        assertEquals(cls, recs[index].getClass());
+    }
+    /**
+     * @return <tt>-1<tt> if not found
+     */
+    private static int findRecordOfType(Record[] recs, Class<?> type, int fromIndex) {
+        for (int i=fromIndex; i<recs.length; i++) {
+            if (type.isInstance(recs[i])) {
+                return i;
+            }
+        }
+        return -1;
+    }
+}
diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestArrayFormulas.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestArrayFormulas.java
deleted file mode 100644 (file)
index d07e13b..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-/* ====================================================================
-   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 junit.framework.TestCase;
-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.ss.util.CellReference;
-
-import java.util.Iterator;
-import java.util.Arrays;
-
-/**
- * Common superclass for testing usermodel API for array formulas
- *
- * @author Yegor Kozlov
- */
-public abstract class BaseTestArrayFormulas extends TestCase {
-
-    /**
-     * @return an object that provides test data in HSSF / XSSF specific way
-     */
-    protected abstract ITestDataProvider getTestDataProvider();
-
-
-    /**
-     *  Set single-cell array formula
-     */
-    public void testSetArrayFormula_singleCell() {
-        Workbook workbook = getTestDataProvider().createWorkbook();
-        Sheet sheet = workbook.createSheet();
-
-        // row 3 does not yet exist
-        assertNull(sheet.getRow(2));
-        CellRangeAddress range = new CellRangeAddress(2, 2, 2, 2);
-        Cell[] cells = sheet.setArrayFormula("SUM(C11:C12*D11:D12)", range);
-        assertEquals(1, cells.length);
-        // sheet.setArrayFormula creates rows and cells for the designated range
-        assertNotNull(sheet.getRow(2));
-        Cell cell = sheet.getRow(2).getCell(2);
-        assertNotNull(cell);
-
-        assertTrue(cell.isPartOfArrayFormulaGroup());
-        //retrieve the range and check it is the same
-        assertEquals(range.formatAsString(), cell.getArrayFormulaRange().formatAsString());
-        //check the formula
-        assertEquals("SUM(C11:C12*D11:D12)", cell.getCellFormula());
-    }
-
-    /**
-     * Set multi-cell array formula
-     */
-    public void testSetArrayFormula_multiCell() {
-        Workbook workbook = getTestDataProvider().createWorkbook();
-        Sheet sheet = workbook.createSheet();
-
-        // multi-cell formula
-        // rows 3-5 don't exist yet
-        assertNull(sheet.getRow(3));
-        assertNull(sheet.getRow(4));
-        assertNull(sheet.getRow(5));
-
-        CellRangeAddress range = new CellRangeAddress(3, 5, 2, 2);
-        assertEquals("C4:C6", range.formatAsString());
-        Cell[] cells = sheet.setArrayFormula("SUM(A1:A3*B1:B3)", range);
-        assertEquals(3, cells.length);
-
-        // sheet.setArrayFormula creates rows and cells for the designated range
-        assertSame(cells[0], sheet.getRow(3).getCell(2));
-        assertSame(cells[1], sheet.getRow(4).getCell(2));
-        assertSame(cells[2], sheet.getRow(5).getCell(2));
-
-        for(Cell acell : cells){
-            assertTrue(acell.isPartOfArrayFormulaGroup());
-            assertEquals(Cell.CELL_TYPE_FORMULA, acell.getCellType());
-            assertEquals("SUM(A1:A3*B1:B3)", acell.getCellFormula());
-            //retrieve the range and check it is the same
-            assertEquals(range.formatAsString(), acell.getArrayFormulaRange().formatAsString());
-        }
-    }
-
-    /**
-     * Passing an incorrect formula to sheet.setArrayFormula
-     *  should throw FormulaParseException
-     */
-    public void testSetArrayFormula_incorrectFormula() {
-        Workbook workbook = getTestDataProvider().createWorkbook();
-        Sheet sheet = workbook.createSheet();
-
-        try {
-            sheet.setArrayFormula("incorrect-formula(C11_C12*D11_D12)",
-                    new CellRangeAddress(10, 10, 10, 10));
-            fail("expected exception");
-        } catch (FormulaParseException e){
-            //expected exception
-        }
-    }
-
-    /**
-     * Calls of cell.getArrayFormulaRange and sheet.removeArrayFormula
-     * on a not-array-formula cell throw IllegalStateException
-     */
-    public void testArrayFormulas_illegalCalls() {
-        Workbook workbook = getTestDataProvider().createWorkbook();
-        Sheet sheet = workbook.createSheet();
-
-        Cell cell = sheet.createRow(0).createCell(0);
-        assertFalse(cell.isPartOfArrayFormulaGroup());
-        try {
-            CellRangeAddress range = cell.getArrayFormulaRange();
-            fail("expected exception");
-        } catch (IllegalStateException e){
-            assertEquals("Cell A1 is not part of an array formula", e.getMessage());
-        }
-
-        try {
-            sheet.removeArrayFormula(cell);
-            fail("expected exception");
-        } catch (IllegalArgumentException e){
-            assertEquals("Cell A1 is not part of an array formula", e.getMessage());
-        }
-    }
-
-    /**
-     * create and remove array formulas
-     */
-    public void testRemoveArrayFormula() {
-        Workbook workbook = getTestDataProvider().createWorkbook();
-        Sheet sheet = workbook.createSheet();
-
-        CellRangeAddress range = new CellRangeAddress(3, 5, 2, 2);
-        assertEquals("C4:C6", range.formatAsString());
-        Cell[] cells = sheet.setArrayFormula("SUM(A1:A3*B1:B3)", range);
-        assertEquals(3, cells.length);
-
-        // remove the formula cells in C4:C6
-        Cell[] dcells = sheet.removeArrayFormula(cells[0]);
-        // removeArrayFormula should return the same cells as setArrayFormula
-        assertTrue(Arrays.equals(cells, dcells));
-
-        for(Cell acell : cells){
-            assertFalse(acell.isPartOfArrayFormulaGroup());
-            assertEquals(Cell.CELL_TYPE_BLANK, acell.getCellType());
-        }
-
-        // cells C4:C6 are not included in array formula,
-        // invocation of sheet.removeArrayFormula on any of them throws IllegalArgumentException
-        for(Cell acell : cells){
-            try {
-                sheet.removeArrayFormula(acell);
-                fail("expected exception");
-            } catch (IllegalArgumentException e){
-                String ref = new CellReference(acell).formatAsString();
-                assertEquals("Cell "+ref+" is not part of an array formula", e.getMessage());
-            }
-        }
-    }
-
-    /**
-     * Test that when reading a workbook from input stream, array formulas are recognized
-     */
-    public void testReadArrayFormula() {
-        Cell[] cells;
-
-        Workbook workbook = getTestDataProvider().createWorkbook();
-        Sheet sheet1 = workbook.createSheet();
-        cells = sheet1.setArrayFormula("SUM(A1:A3*B1:B3)", CellRangeAddress.valueOf("C4:C6"));
-        assertEquals(3, cells.length);
-
-        cells = sheet1.setArrayFormula("MAX(A1:A3*B1:B3)", CellRangeAddress.valueOf("A4:A6"));
-        assertEquals(3, cells.length);
-
-        Sheet sheet2 = workbook.createSheet();
-        cells = sheet2.setArrayFormula("MIN(A1:A3*B1:B3)", CellRangeAddress.valueOf("D2:D4"));
-        assertEquals(3, cells.length);
-
-        workbook = getTestDataProvider().writeOutAndReadBack(workbook);
-        sheet1 = workbook.getSheetAt(0);
-        for(int rownum=3; rownum <= 5; rownum++) {
-            Cell cell1 = sheet1.getRow(rownum).getCell(2);
-            assertTrue( cell1.isPartOfArrayFormulaGroup());
-
-            Cell cell2 = sheet1.getRow(rownum).getCell(0);
-            assertTrue( cell2.isPartOfArrayFormulaGroup());
-        }
-
-        sheet2 = workbook.getSheetAt(1);
-        for(int rownum=1; rownum <= 3; rownum++) {
-            Cell cell1 = sheet2.getRow(rownum).getCell(3);
-            assertTrue( cell1.isPartOfArrayFormulaGroup());
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetUpdateArrayFormulas.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetUpdateArrayFormulas.java
new file mode 100644 (file)
index 0000000..fdd11fe
--- /dev/null
@@ -0,0 +1,236 @@
+/* ====================================================================
+   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 java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.ss.ITestDataProvider;
+import org.apache.poi.ss.formula.FormulaParseException;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellReference;
+
+/**
+ * Common superclass for testing usermodel API for array formulas.<br/>
+ * Formula evaluation is not tested here.
+ *
+ * @author Yegor Kozlov
+ * @author Josh Micich
+ */
+public abstract class BaseTestSheetUpdateArrayFormulas extends TestCase {
+    protected final ITestDataProvider _testDataProvider;
+
+    protected BaseTestSheetUpdateArrayFormulas(ITestDataProvider testDataProvider) {
+        _testDataProvider = testDataProvider;
+    }
+
+    public final void testAutoCreateOtherCells() {
+        Workbook workbook = _testDataProvider.createWorkbook();
+        Sheet sheet = workbook.createSheet("Sheet1");
+
+        Row row1 = sheet.createRow(0);
+        Cell cellA1 = row1.createCell(0);
+        Cell cellB1 = row1.createCell(1);
+        String formula = "42";
+        sheet.setArrayFormula(formula, CellRangeAddress.valueOf("A1:B2"));
+
+        assertEquals(formula, cellA1.getCellFormula());
+        assertEquals(formula, cellB1.getCellFormula());
+        Row row2 = sheet.getRow(1);
+        assertNotNull(row2);
+        assertEquals(formula, row2.getCell(0).getCellFormula());
+        assertEquals(formula, row2.getCell(1).getCellFormula());
+    }
+    /**
+     *  Set single-cell array formula
+     */
+    public final void testSetArrayFormula_singleCell() {
+        Cell[] cells;
+
+        Workbook workbook = _testDataProvider.createWorkbook();
+        Sheet sheet = workbook.createSheet();
+        Cell cell = sheet.createRow(0).createCell(0);
+        assertFalse(cell.isPartOfArrayFormulaGroup());
+        try {
+            cell.getArrayFormulaRange();
+            fail("expected exception");
+        } catch (IllegalStateException e){
+            assertEquals("Cell A1 is not part of an array formula.", e.getMessage());
+        }
+
+        // row 3 does not yet exist
+        assertNull(sheet.getRow(2));
+        CellRangeAddress range = new CellRangeAddress(2, 2, 2, 2);
+        cells = sheet.setArrayFormula("SUM(C11:C12*D11:D12)", range).getFlattenedCells();
+        assertEquals(1, cells.length);
+        // sheet.setArrayFormula creates rows and cells for the designated range
+        assertNotNull(sheet.getRow(2));
+        cell = sheet.getRow(2).getCell(2);
+        assertNotNull(cell);
+
+        assertTrue(cell.isPartOfArrayFormulaGroup());
+        //retrieve the range and check it is the same
+        assertEquals(range.formatAsString(), cell.getArrayFormulaRange().formatAsString());
+        //check the formula
+        assertEquals("SUM(C11:C12*D11:D12)", cell.getCellFormula());
+    }
+
+    /**
+     * Set multi-cell array formula
+     */
+    public final void testSetArrayFormula_multiCell() {
+        Workbook workbook = _testDataProvider.createWorkbook();
+        Sheet sheet = workbook.createSheet();
+
+        // multi-cell formula
+        // rows 3-5 don't exist yet
+        assertNull(sheet.getRow(3));
+        assertNull(sheet.getRow(4));
+        assertNull(sheet.getRow(5));
+
+        CellRangeAddress range = CellRangeAddress.valueOf("C4:C6");
+        Cell[] cells = sheet.setArrayFormula("SUM(A1:A3*B1:B3)", range).getFlattenedCells();
+        assertEquals(3, cells.length);
+
+        // sheet.setArrayFormula creates rows and cells for the designated range
+        assertSame(cells[0], sheet.getRow(3).getCell(2));
+        assertSame(cells[1], sheet.getRow(4).getCell(2));
+        assertSame(cells[2], sheet.getRow(5).getCell(2));
+
+        for(Cell acell : cells){
+            assertTrue(acell.isPartOfArrayFormulaGroup());
+            assertEquals(Cell.CELL_TYPE_FORMULA, acell.getCellType());
+            assertEquals("SUM(A1:A3*B1:B3)", acell.getCellFormula());
+            //retrieve the range and check it is the same
+            assertEquals(range.formatAsString(), acell.getArrayFormulaRange().formatAsString());
+        }
+    }
+
+    /**
+     * Passing an incorrect formula to sheet.setArrayFormula
+     *  should throw FormulaParseException
+     */
+    public final void testSetArrayFormula_incorrectFormula() {
+        Workbook workbook = _testDataProvider.createWorkbook();
+        Sheet sheet = workbook.createSheet();
+
+        try {
+            sheet.setArrayFormula("incorrect-formula(C11_C12*D11_D12)",
+                    new CellRangeAddress(10, 10, 10, 10));
+            fail("expected exception");
+        } catch (FormulaParseException e){
+            //expected exception
+        }
+    }
+
+    /**
+     * Calls of cell.getArrayFormulaRange and sheet.removeArrayFormula
+     * on a not-array-formula cell throw IllegalStateException
+     */
+    public final void testArrayFormulas_illegalCalls() {
+        Workbook workbook = _testDataProvider.createWorkbook();
+        Sheet sheet = workbook.createSheet();
+
+        Cell cell = sheet.createRow(0).createCell(0);
+        assertFalse(cell.isPartOfArrayFormulaGroup());
+        try {
+            cell.getArrayFormulaRange();
+            fail("expected exception");
+        } catch (IllegalStateException e){
+            assertEquals("Cell A1 is not part of an array formula.", e.getMessage());
+        }
+
+        try {
+            sheet.removeArrayFormula(cell);
+            fail("expected exception");
+        } catch (IllegalArgumentException e){
+            assertEquals("Cell A1 is not part of an array formula.", e.getMessage());
+        }
+    }
+
+    /**
+     * create and remove array formulas
+     */
+    public final void testRemoveArrayFormula() {
+        Workbook workbook = _testDataProvider.createWorkbook();
+        Sheet sheet = workbook.createSheet();
+
+        CellRangeAddress range = new CellRangeAddress(3, 5, 2, 2);
+        assertEquals("C4:C6", range.formatAsString());
+        CellRange<?> cr = sheet.setArrayFormula("SUM(A1:A3*B1:B3)", range);
+        assertEquals(3, cr.size());
+
+        // remove the formula cells in C4:C6
+        CellRange<?> dcells = sheet.removeArrayFormula(cr.getTopLeftCell());
+        // removeArrayFormula should return the same cells as setArrayFormula
+        assertTrue(Arrays.equals(cr.getFlattenedCells(), dcells.getFlattenedCells()));
+
+        for(Cell acell : cr){
+            assertFalse(acell.isPartOfArrayFormulaGroup());
+            assertEquals(Cell.CELL_TYPE_BLANK, acell.getCellType());
+        }
+
+        // cells C4:C6 are not included in array formula,
+        // invocation of sheet.removeArrayFormula on any of them throws IllegalArgumentException
+        for(Cell acell : cr){
+            try {
+                sheet.removeArrayFormula(acell);
+                fail("expected exception");
+            } catch (IllegalArgumentException e){
+                String ref = new CellReference(acell).formatAsString();
+                assertEquals("Cell "+ref+" is not part of an array formula.", e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Test that when reading a workbook from input stream, array formulas are recognized
+     */
+    public final void testReadArrayFormula() {
+        Cell[] cells;
+
+        Workbook workbook = _testDataProvider.createWorkbook();
+        Sheet sheet1 = workbook.createSheet();
+        cells = sheet1.setArrayFormula("SUM(A1:A3*B1:B3)", CellRangeAddress.valueOf("C4:C6")).getFlattenedCells();
+        assertEquals(3, cells.length);
+
+        cells = sheet1.setArrayFormula("MAX(A1:A3*B1:B3)", CellRangeAddress.valueOf("A4:A6")).getFlattenedCells();
+        assertEquals(3, cells.length);
+
+        Sheet sheet2 = workbook.createSheet();
+        cells = sheet2.setArrayFormula("MIN(A1:A3*B1:B3)", CellRangeAddress.valueOf("D2:D4")).getFlattenedCells();
+        assertEquals(3, cells.length);
+
+        workbook = _testDataProvider.writeOutAndReadBack(workbook);
+        sheet1 = workbook.getSheetAt(0);
+        for(int rownum=3; rownum <= 5; rownum++) {
+            Cell cell1 = sheet1.getRow(rownum).getCell(2);
+            assertTrue( cell1.isPartOfArrayFormulaGroup());
+
+            Cell cell2 = sheet1.getRow(rownum).getCell(0);
+            assertTrue( cell2.isPartOfArrayFormulaGroup());
+        }
+
+        sheet2 = workbook.getSheetAt(1);
+        for(int rownum=1; rownum <= 3; rownum++) {
+            Cell cell1 = sheet2.getRow(rownum).getCell(3);
+            assertTrue( cell1.isPartOfArrayFormulaGroup());
+        }
+    }
+}