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;
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 {
private static final int OPT_ALWAYS_RECALCULATE = 0x0001;
private static final int OPT_CALCULATE_ON_OPEN = 0x0002;
- private int _options;
+ private int _options;
private int _field3notUsed;
private Formula _formula;
_formula = Formula.read(formulaTokenLen, in, totalFormulaLen);
}
+ public ArrayRecord(Formula formula, CellRangeAddress8Bit range ) {
+ super(range);
+ _options = 0; //YK: Excel 2007 leaves this field unset
+ _field3notUsed = 0;
+ _formula = formula;
+ }
+
public boolean isAlwaysRecalculate() {
return (_options & OPT_ALWAYS_RECALCULATE) != 0;
}
return (_options & OPT_CALCULATE_ON_OPEN) != 0;
}
- protected int getExtraDataSize() {
+ public void setOptions(int val){
+ _options = val;
+ }
+
+ protected int getExtraDataSize() {
return 2 + 4
+ _formula.getEncodedSize();
}
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);
+ */
+ }
}
package org.apache.poi.hssf.record.aggregates;
+import org.apache.poi.hssf.record.ArrayRecord;
import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.StringRecord;
import org.apache.poi.hssf.record.formula.ExpPtg;
import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.util.CellRangeAddress8Bit;
import org.apache.poi.hssf.util.CellReference;
+import org.apache.poi.ss.formula.Formula;
+import org.apache.poi.ss.util.CellRangeAddress;
/**
* The formula record aggregate is used to join together the formula record and it's
* (optional) string record and (optional) Shared Formula Record (template reads, excel optimization).
*
* @author Glen Stampoultzis (glens at apache.org)
+ * @author Vladimirs Abramovs(Vladimirs.Abramovs at exigenservices.com) - Array Formula support
*/
public final class FormulaRecordAggregate extends RecordAggregate implements CellValueRecordInterface {
}
public Ptg[] getFormulaTokens() {
- if (_sharedFormulaRecord == null) {
- return _formulaRecord.getParsedExpression();
+ if (_sharedFormulaRecord != null) {
+ return _sharedFormulaRecord.getFormulaTokens(_formulaRecord);
+ }
+ CellReference expRef = _formulaRecord.getFormula().getExpReference();
+ if (expRef != null) {
+ ArrayRecord arec = _sharedValueManager.getArrayRecord(expRef.getRow(), expRef.getCol());
+ return arec.getFormulaTokens();
}
- return _sharedFormulaRecord.getFormulaTokens(_formulaRecord);
+ return _formulaRecord.getParsedExpression();
}
/**
_sharedValueManager.unlink(_sharedFormulaRecord);
}
}
+
+ public boolean isPartOfArrayFormula() {
+ if (_sharedFormulaRecord != null) {
+ return false;
+ }
+ return _formulaRecord.getFormula().getExpReference() != null;
+ }
+
+ public CellRangeAddress getArrayFormulaRange() {
+ if (_sharedFormulaRecord != null) {
+ throw new IllegalStateException("not an array formula cell.");
+ }
+ CellReference expRef = _formulaRecord.getFormula().getExpReference();
+ if (expRef == null) {
+ 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());
+ }
+ CellRangeAddress8Bit a = arec.getRange();
+ return new CellRangeAddress(a.getFirstRow(), a.getLastRow(), a.getFirstColumn(),a.getLastColumn());
+ }
+
+ 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);
+ }
+ /**
+ * Removes an array formula
+ * @return the range of the array formula containing the specified cell. Never <code>null</code>
+ */
+ public CellRangeAddress removeArrayFormula(int rowIndex, int columnIndex) {
+ CellRangeAddress8Bit a = _sharedValueManager.removeArrayFormula(rowIndex, columnIndex);
+ return new CellRangeAddress(a.getFirstRow(), a.getLastRow(), a.getFirstColumn(), a.getLastColumn());
+ }
}
package org.apache.poi.hssf.record.aggregates;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.apache.poi.hssf.record.ArrayRecord;
* </ul>
*
* @author Josh Micich
+ * @author Vladimirs Abramovs(Vladimirs.Abramovs at exigenservices.com) - handling of ArrayRecords
*/
public final class SharedValueManager {
/**
* @return a new empty {@link SharedValueManager}.
*/
- public static final SharedValueManager createEmpty() {
+ public static SharedValueManager createEmpty() {
// Note - must create distinct instances because they are assumed to be mutable.
return new SharedValueManager(
new SharedFormulaRecord[0], new CellReference[0], new ArrayRecord[0], new TableRecord[0]);
}
- private final ArrayRecord[] _arrayRecords;
+ private final List<ArrayRecord> _arrayRecords;
private final TableRecord[] _tableRecords;
private final Map<SharedFormulaRecord, SharedFormulaGroup> _groupsBySharedFormulaRecord;
/** cached for optimization purposes */
if (nShF != firstCells.length) {
throw new IllegalArgumentException("array sizes don't match: " + nShF + "!=" + firstCells.length + ".");
}
- _arrayRecords = arrayRecords;
+ _arrayRecords = new ArrayList<ArrayRecord>();
+ _arrayRecords.addAll(Arrays.asList(arrayRecords));
_tableRecords = tableRecords;
Map<SharedFormulaRecord, SharedFormulaGroup> m = new HashMap<SharedFormulaRecord, SharedFormulaGroup>(nShF * 3 / 2);
for (int i = 0; i < nShF; i++) {
_groupsBySharedFormulaRecord = m;
}
- /**
- * @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) {
return tr;
}
}
- for (int i = 0; i < _arrayRecords.length; i++) {
- ArrayRecord ar = _arrayRecords[i];
+ for (ArrayRecord ar : _arrayRecords) {
if (ar.isFirstCell(row, column)) {
return ar;
}
}
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;
+ }
+
}
import org.apache.poi.hssf.record.TextObjectRecord;
import org.apache.poi.hssf.record.UnicodeString;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
+import org.apache.poi.hssf.record.formula.ExpPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.ss.usermodel.Cell;
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.formula.FormulaType;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.util.POILogger;
}
return ((FormulaRecordAggregate)_record).getFormulaRecord().getCachedResultType();
}
+
+ void setCellArrayFormula(CellRangeAddress range) {
+ int row = _record.getRow();
+ short col = _record.getColumn();
+ short styleIndex = _record.getXFIndex();
+ setCellType(CELL_TYPE_FORMULA, false, row, col, styleIndex);
+
+ // Billet for formula in rec
+ Ptg[] ptgsForCell = {new ExpPtg(range.getFirstRow(), range.getFirstColumn())};
+ FormulaRecordAggregate agg = (FormulaRecordAggregate) _record;
+ agg.setParsedExpression(ptgsForCell);
+ }
+
+ 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");
+ }
+ return ((FormulaRecordAggregate)_record).getArrayFormulaRange();
+ }
+
+ public boolean isPartOfArrayFormulaGroup() {
+ if (_cellType != CELL_TYPE_FORMULA) {
+ return false;
+ }
+ return ((FormulaRecordAggregate)_record).isPartOfArrayFormula();
+ }
}
import java.util.TreeMap;
import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
import org.apache.poi.hssf.model.InternalSheet;
import org.apache.poi.hssf.model.InternalWorkbook;
import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.WSBoolRecord;
import org.apache.poi.hssf.record.WindowTwoRecord;
import org.apache.poi.hssf.record.aggregates.DataValidityTable;
+import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
import org.apache.poi.hssf.record.formula.FormulaShifter;
+import org.apache.poi.hssf.record.formula.Ptg;
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.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.SpreadsheetVersion;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
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);
+ int firstRow = range.getFirstRow();
+ int firstColumn = range.getFirstColumn();
+ for (int rowIn = firstRow; rowIn <= range.getLastRow(); rowIn++) {
+ for (int colIn = firstColumn; colIn <= range.getLastColumn(); colIn++) {
+ HSSFRow row = getRow(rowIn);
+ if (row == null) {
+ row = createRow(rowIn);
+ }
+ HSSFCell cell = row.getCell(colIn);
+ if (cell == null) {
+ cell = row.createCell(colIn);
+ }
+ cell.setCellArrayFormula(range);
+ cells[k++] = cell;
+ }
+ }
+ HSSFCell mainArrayFormulaCell = getRow(firstRow).getCell(firstColumn);
+ FormulaRecordAggregate agg = (FormulaRecordAggregate)mainArrayFormulaCell.getCellValueRecord();
+ agg.setArrayFormula(range, ptgs);
+ return cells;
+ }
+
+
+ public HSSFCell[] removeArrayFormula(Cell cell) {
+ ArrayList<HSSFCell> lst = new ArrayList<HSSFCell>();
+ 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");
+ }
+ 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");
+ }
+ // 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);
+ }
+ }
+ return lst.toArray(new HSSFCell[lst.size()]);
+ }
}
case FormulaType.CELL:
rootNodeOperandClass = Ptg.CLASS_VALUE;
break;
+ case FormulaType.ARRAY:
+ rootNodeOperandClass = Ptg.CLASS_ARRAY;
+ break;
case FormulaType.NAMEDRANGE:
case FormulaType.DATAVALIDATION_LIST:
rootNodeOperandClass = Ptg.CLASS_REF;
import java.util.Date;
import org.apache.poi.ss.formula.FormulaParseException;
+import org.apache.poi.ss.util.CellRangeAddress;
/**
* High level representation of a cell in a row of a spreadsheet.
* @param link hypelrink associated with this cell
*/
void setHyperlink(Hyperlink link);
+
+
+ /**
+ * Only valid for array formula cells
+ *
+ * @return range of the array formula group that the cell belongs to.
+ */
+ CellRangeAddress getArrayFormulaRange();
+
+ /**
+ * @return <code>true</code> if this cell is part of group of cells having a common array formula.
+ */
+ boolean isPartOfArrayFormulaGroup();
+
}
*/
boolean isSelected();
+
+ /**
+ * Sets array formula to specified region for result.
+ *
+ * @param formula Formula
+ * @param range Region of array formula for result.
+ */
+ 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
+ */
+ Cell[] removeArrayFormula(Cell cell);
+
}
CellReference cellRefFrom = new CellReference(getFirstRow(), getFirstColumn());
CellReference cellRefTo = new CellReference(getLastRow(), getLastColumn());
sb.append(cellRefFrom.formatAsString());
- sb.append(':');
- sb.append(cellRefTo.formatAsString());
+ //for a single-cell reference return A1 instead of A1:A1
+ if(!cellRefFrom.equals(cellRefTo)){
+ sb.append(':');
+ sb.append(cellRefTo.formatAsString());
+ }
return sb.toString();
}
import org.apache.poi.hssf.record.formula.SheetNameFormatter;
import org.apache.poi.ss.SpreadsheetVersion;
+import org.apache.poi.ss.usermodel.Cell;
/**
*
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(int pRow, int pCol, boolean pAbsRow, boolean pAbsCol) {
this(null, pRow, pCol, pAbsRow, pAbsCol);
}
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);
+ }
}
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
//validate through the FormulaParser
- FormulaParser.parse(formula, fpb, FormulaType.CELL, wb.getSheetIndex(getSheet()));
+ FormulaParser.parse(formula, fpb, formulaType, wb.getSheetIndex(getSheet()));
- CTCellFormula f = CTCellFormula.Factory.newInstance();
+ CTCellFormula f = CTCellFormula.Factory.newInstance();
f.setStringValue(formula);
_cell.setF(f);
if(_cell.isSetV()) _cell.unsetV();
--- /dev/null
+/* ====================================================================
+ 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
//existing cells are invalidated
assertEquals(0, wsh.getSheetData().getRowArray(0).sizeOfCArray());
}
-
- public void testSetArrayFormula() throws Exception {
- XSSFCell[] cells;
-
- XSSFWorkbook workbook = new XSSFWorkbook();
- XSSFSheet sheet = workbook.createSheet();
- XSSFCell cell = sheet.createRow(0).createCell(0);
- assertFalse(cell.isPartOfArrayFormulaGroup());
- assertFalse(sheet.isCellInArrayFormulaContext(cell));
- try {
- CellRangeAddress range = cell.getArrayFormulaRange();
- fail("expected exception");
- } catch (IllegalStateException e){
- assertEquals("Cell A1 is not part of an array formula", e.getMessage());
- }
-
- // 1. single-cell formula
-
- // 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);
- 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());
- assertSame(cells[0], sheet.getFirstCellInArrayFormula(cells[0]));
- //retrieve the range and check it is the same
- assertEquals(range.formatAsString(), cell.getArrayFormulaRange().formatAsString());
-
- // 2. multi-cell formula
- //rows 3-5 don't exist yet
- assertNull(sheet.getRow(3));
- assertNull(sheet.getRow(4));
- assertNull(sheet.getRow(5));
-
- 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]));
-
- /*
- * 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;
- */
- assertEquals("SUM(A1:A3*B1:B3)", cells[0].getCTCell().getF().getStringValue());
- assertNull(cells[1].getCTCell().getF());
- assertNull(cells[2].getCTCell().getF());
-
- for(XSSFCell 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());
- }
- }
-
- public void testRemoveArrayFormula() throws Exception {
- XSSFCell[] cells;
-
- XSSFWorkbook workbook = new XSSFWorkbook();
- 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);
-
- // remove the formula cells in C4:C6
- XSSFCell[] dcells = sheet.removeArrayFormula(cells[0]);
- // removeArrayFormula should return the same cells as setArrayFormula
- assertTrue(Arrays.equals(cells, dcells));
-
- for(XSSFCell acell : cells){
- assertFalse(acell.isPartOfArrayFormulaGroup());
- assertEquals(Cell.CELL_TYPE_BLANK, acell.getCellType());
- }
-
- //invocation on a not-array-formula cell throws IllegalStateException
- try {
- sheet.removeArrayFormula(cells[0]);
- fail("expected exception");
- } catch (IllegalArgumentException e){
- assertEquals("Cell C4 is not part of an array formula", e.getMessage());
- }
- }
-
- public void testReadArrayFormula() throws Exception {
- XSSFCell[] cells;
-
- XSSFWorkbook workbook = new XSSFWorkbook();
- XSSFSheet 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);
-
- XSSFSheet 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++) {
- XSSFCell cell1 = sheet1.getRow(rownum).getCell(2);
- assertTrue( sheet1.isCellInArrayFormulaContext(cell1));
- assertTrue( cell1.isPartOfArrayFormulaGroup());
-
- XSSFCell cell2 = sheet1.getRow(rownum).getCell(0);
- assertTrue( sheet1.isCellInArrayFormulaContext(cell2));
- assertTrue( cell2.isPartOfArrayFormulaGroup());
- }
-
- sheet2 = workbook.getSheetAt(1);
- for(int rownum=1; rownum <= 3; rownum++) {
- XSSFCell cell1 = sheet2.getRow(rownum).getCell(3);
- assertTrue( sheet2.isCellInArrayFormulaContext(cell1));
- assertTrue( cell1.isPartOfArrayFormulaGroup());
- }
- XSSFCell acnhorCell = sheet2.getRow(1).getCell(3);
- XSSFCell fmlaCell = sheet2.getRow(2).getCell(3);
- assertSame(acnhorCell, sheet2.getFirstCellInArrayFormula(fmlaCell));
- assertSame(acnhorCell, sheet2.getFirstCellInArrayFormula(acnhorCell));
- }
}
--- /dev/null
+/* ====================================================================
+ 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.record;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.ComparisonFailure;
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.util.CellRangeAddress8Bit;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.RefPtg;
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.ss.usermodel.CellValue;
+import org.apache.poi.ss.formula.FormulaParser;
+import org.apache.poi.ss.formula.FormulaRenderer;
+import org.apache.poi.ss.formula.FormulaType;
+import org.apache.poi.ss.formula.Formula;
+import org.apache.poi.util.LittleEndianInput;
+import org.apache.poi.util.HexRead;
+import org.apache.poi.util.HexDump;
+
+import java.util.Arrays;
+
+public final class TestArrayRecord extends TestCase {
+
+ public void testRead() {
+ String hex =
+ "21 02 25 00 01 00 01 00 01 01 00 00 00 00 00 00 " +
+ "17 00 65 00 00 01 00 02 C0 02 C0 65 00 00 01 00 " +
+ "03 C0 03 C0 04 62 01 07 00";
+ byte[] data = HexRead.readFromString(hex);
+ RecordInputStream in = TestcaseRecordInputStream.create(data);
+ ArrayRecord r1 = new ArrayRecord(in);
+ CellRangeAddress8Bit range = r1.getRange();
+ assertEquals(1, range.getFirstColumn());
+ assertEquals(1, range.getLastColumn());
+ assertEquals(1, range.getFirstRow());
+ assertEquals(1, range.getLastRow());
+
+ Ptg[] ptg = r1.getFormulaTokens();
+ assertEquals("MAX(C1:C2-D1:D2)", FormulaRenderer.toFormulaString(null, ptg));
+
+ //construct a new ArrayRecord with the same contents as r1
+ Ptg[] fmlaPtg = FormulaParser.parse("MAX(C1:C2-D1:D2)", null, FormulaType.ARRAY, 0);
+ ArrayRecord r2 = new ArrayRecord(Formula.create(fmlaPtg), new CellRangeAddress8Bit(1, 1, 1, 1));
+ byte[] ser = r2.serialize();
+ //serialize and check that the data is the same as in r1
+ assertEquals(HexDump.toHex(data), HexDump.toHex(ser));
+
+
+ }
+}
\ No newline at end of file
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.hssf.record.StringRecord;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.ExpPtg;
import org.apache.poi.hssf.usermodel.RecordInspector.RecordCollector;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
+import org.apache.poi.ss.formula.FormulaType;
+import org.apache.poi.ss.formula.FormulaRenderer;
+import org.apache.poi.ss.util.CellRangeAddress;
/**
*
s.setString("abc");
FormulaRecordAggregate fagg = new FormulaRecordAggregate(f, s, SharedValueManager.createEmpty());
assertEquals("abc", fagg.getStringValue());
- }
+ assertFalse(fagg.isPartOfArrayFormula());
+ }
/**
* Sometimes a {@link StringRecord} appears after a {@link FormulaRecord} even though the
assertEquals(1, vraRecs.length);
assertEquals(fr, vraRecs[0]);
}
+
+ public void testArrayFormulas() {
+ int rownum = 4;
+ int colnum = 4;
+
+ 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);
+
+ 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);
+ }
}
--- /dev/null
+/* ====================================================================
+ 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
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.util.Arrays;
import junit.framework.AssertionFailedError;
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
import org.apache.poi.hssf.usermodel.RecordInspector.RecordCollector;
import org.apache.poi.ss.usermodel.BaseTestSheet;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.TempFile;
s.setRightToLeft(true);
assertEquals(true, s.isRightToLeft());
}
-
}
--- /dev/null
+/* ====================================================================
+ 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