public int getLastRowNum() {
return _hs.getLastRowNum();
}
-
+
+ /* (non-Javadoc)
+ * @see org.apache.poi.ss.formula.EvaluationSheet#isRowHidden(int)
+ * @since POI 4.0.2
+ */
+ public boolean isRowHidden(int rowIndex) {
+ HSSFRow row = _hs.getRow(rowIndex);
+ if (row == null) return false;
+ return row.getZeroHeight();
+ }
+
@Override
public EvaluationCell getCell(int rowIndex, int columnIndex) {
HSSFRow row = _hs.getRow(rowIndex);
* @since POI 4.0.0
*/
public int getLastRowNum();
+
+ /**
+ * Used by SUBTOTAL and similar functions that have options to ignore hidden rows
+ * @param rowIndex
+ * @return true if the row is hidden, false if not
+ * @since POI 4.0.2
+ */
+ public boolean isRowHidden(int rowIndex);
}
SheetRefEvaluator _sre = _evaluator.getSheetEvaluator(_evaluator.getFirstSheetIndex());
return _sre.isSubTotal(getFirstRow() + rowIndex, getFirstColumn() + columnIndex);
}
+
+ /**
+ * @return whether the row at rowIndex is hidden
+ * @see org.apache.poi.ss.formula.eval.AreaEvalBase#isRowHidden(int)
+ */
+ public boolean isRowHidden(int rowIndex) {
+ // delegate the query to the sheet evaluator which has access to internal ptgs
+ SheetRefEvaluator _sre = _evaluator.getSheetEvaluator(_evaluator.getFirstSheetIndex());
+ return _sre.isRowHidden(getFirstRow() + rowIndex);
+ }
}
return new LazyAreaEval(area, _evaluator);
}
+ /**
+ * @return true if the cell is a subtotal
+ */
public boolean isSubTotal() {
SheetRefEvaluator sheetEvaluator = _evaluator.getSheetEvaluator(getFirstSheetIndex());
return sheetEvaluator.isSubTotal(getRow(), getColumn());
}
+
+ /**
+ * @return whether the row at rowIndex is hidden
+ */
+ public boolean isRowHidden() {
+ // delegate the query to the sheet evaluator which has access to internal ptgs
+ SheetRefEvaluator _sre = _evaluator.getSheetEvaluator(_evaluator.getFirstSheetIndex());
+ return _sre.isRowHidden(getRow());
+ }
public String toString() {
CellReference cr = new CellReference(getRow(), getColumn());
}
/**
+ * @param rowIndex
+ * @param columnIndex
* @return whether cell at rowIndex and columnIndex is a subtotal
* @see org.apache.poi.ss.formula.functions.Subtotal
*/
return subtotal;
}
+ /**
+ * Used by functions that calculate differently depending on row visibility, like some
+ * variations of SUBTOTAL()
+ * @see org.apache.poi.ss.formula.functions.Subtotal
+ * @param rowIndex
+ * @return true if the row is hidden
+ */
+ public boolean isRowHidden(int rowIndex) {
+ return getSheet().isRowHidden(rowIndex);
+ }
}
import org.apache.poi.ss.formula.eval.AreaEval;
import org.apache.poi.ss.formula.eval.ValueEval;
+import org.apache.poi.ss.formula.functions.Subtotal;
/**
* Common interface of {@link AreaEval} and {@link org.apache.poi.ss.formula.eval.AreaEvalBase},
* @return true if the cell at row and col is a subtotal
*/
boolean isSubTotal(int rowIndex, int columnIndex);
+
+ /**
+ *
+ * @param rowIndex
+ * @return true if the row is hidden
+ * @see Subtotal
+ */
+ boolean isRowHidden(int rowIndex);
}
return false;
}
+ /**
+ * @return false by default, meaning all rows are calculated
+ * @see org.apache.poi.ss.formula.TwoDEval#isRowHidden(int)
+ */
+ public boolean isRowHidden(int rowIndex) {
+ return false;
+ }
}
return _masterSheet.getLastRowNum();
}
+ /* (non-Javadoc)
+ * @see org.apache.poi.ss.formula.EvaluationSheet#isRowHidden(int)
+ * @since POI 4.0.2
+ */
+ public boolean isRowHidden(int rowIndex) {
+ return _masterSheet.isRowHidden(rowIndex);
+ }
+
@Override
public EvaluationCell getCell(int rowIndex, int columnIndex) {
RowColKey key = new RowColKey(rowIndex, columnIndex);
* @param func the function to wrap
* @return wrapped instance. The actual math is delegated to the argument function.
*/
- /*package*/ static Function subtotalInstance(Function func) {
+ /*package*/ static Function subtotalInstance(Function func, boolean countHiddenRows) {
final AggregateFunction arg = (AggregateFunction)func;
return new AggregateFunction() {
@Override
return false;
}
+ public boolean isHiddenRowCounted() {
+ return countHiddenRows;
+ }
};
}
}
};
+ /**
+ * matches hidden rows but not subtotals
+ */
private static final I_MatchPredicate subtotalPredicate = new I_MatchAreaPredicate() {
public boolean matches(ValueEval valueEval) {
return defaultPredicate.matches(valueEval);
}
};
+ /**
+ * matches nither hidden rows or subtotals
+ */
+ private static final I_MatchPredicate subtotalVisibleOnlyPredicate = new I_MatchAreaPredicate() {
+ public boolean matches(ValueEval valueEval) {
+ return defaultPredicate.matches(valueEval);
+ }
+
+ /**
+ * don't count cells that are subtotals
+ */
+ public boolean matches(TwoDEval areEval, int rowIndex, int columnIndex) {
+ return !areEval.isSubTotal(rowIndex, columnIndex) && !areEval.isRowHidden(rowIndex);
+ }
+ };
+
/**
* Create an instance of Count to use in {@link Subtotal}
* <p>
* If there are other subtotals within argument refs (or nested subtotals),
* these nested subtotals are ignored to avoid double counting.
* </p>
+ * @param includeHiddenRows true to include hidden rows in the aggregate, false to skip them
+ * @return function
*
* @see Subtotal
*/
- public static Count subtotalInstance() {
- return new Count(subtotalPredicate );
+ public static Count subtotalInstance(boolean includeHiddenRows) {
+ return new Count(includeHiddenRows ? subtotalPredicate : subtotalVisibleOnlyPredicate);
}
}
return true;
}
};
- private static final I_MatchPredicate subtotalPredicate = new I_MatchAreaPredicate() {
+
+ private static final I_MatchPredicate subtotalPredicate = new I_MatchAreaPredicate() {
public boolean matches(ValueEval valueEval) {
return defaultPredicate.matches(valueEval);
}
}
};
- public static Counta subtotalInstance() {
- return new Counta(subtotalPredicate);
+ private static final I_MatchPredicate subtotalVisibleOnlyPredicate = new I_MatchAreaPredicate() {
+ public boolean matches(ValueEval valueEval) {
+ return defaultPredicate.matches(valueEval);
+ }
+
+ /**
+ * don't count cells in rows that are hidden or subtotal cells
+ */
+ public boolean matches(TwoDEval areEval, int rowIndex, int columnIndex) {
+ return !areEval.isSubTotal(rowIndex, columnIndex) && ! areEval.isRowHidden(rowIndex);
+ }
+ };
+
+ public static Counta subtotalInstance(boolean includeHiddenRows) {
+ return new Counta(includeHiddenRows ? subtotalPredicate : subtotalVisibleOnlyPredicate);
}
}
return true;
}
+ /**
+ * @return true if values in hidden rows are counted
+ * @see Subtotal
+ */
+ public boolean isHiddenRowCounted() {
+ return true;
+ }
+
/**
* Collects values from a single argument
*/
for (int rcIx = 0; rcIx < width; rcIx++) {
ValueEval ve = ae.getValue(sIx, rrIx, rcIx);
if (!isSubtotalCounted() && ae.isSubTotal(rrIx, rcIx)) continue;
+ if (!isHiddenRowCounted() && ae.isRowHidden(rrIx)) continue;
collectValue(ve, true, temp);
}
}
* <tr align='center'><td>9</td><td>SUM</td></tr>
* <tr align='center'><td>10</td><td>VAR *</td></tr>
* <tr align='center'><td>11</td><td>VARP *</td></tr>
- * <tr align='center'><td>101-111</td><td>*</td></tr>
+ * <tr align='center'><td>101</td><td>AVERAGE</td></tr>
+ * <tr align='center'><td>102</td><td>COUNT</td></tr>
+ * <tr align='center'><td>103</td><td>COUNTA</td></tr>
+ * <tr align='center'><td>104</td><td>MAX</td></tr>
+ * <tr align='center'><td>105</td><td>MIN</td></tr>
+ * <tr align='center'><td>106</td><td>PRODUCT</td></tr>
+ * <tr align='center'><td>107</td><td>STDEV</td></tr>
+ * <tr align='center'><td>108</td><td>STDEVP *</td></tr>
+ * <tr align='center'><td>109</td><td>SUM</td></tr>
+ * <tr align='center'><td>110</td><td>VAR *</td></tr>
+ * <tr align='center'><td>111</td><td>VARP *</td></tr>
* </table><br>
* * Not implemented in POI yet. Functions 101-111 are the same as functions 1-11 but with
* the option 'ignore hidden values'.
private static Function findFunction(int functionCode) throws EvaluationException {
switch (functionCode) {
- case 1: return subtotalInstance(AggregateFunction.AVERAGE);
- case 2: return Count.subtotalInstance();
- case 3: return Counta.subtotalInstance();
- case 4: return subtotalInstance(AggregateFunction.MAX);
- case 5: return subtotalInstance(AggregateFunction.MIN);
- case 6: return subtotalInstance(AggregateFunction.PRODUCT);
- case 7: return subtotalInstance(AggregateFunction.STDEV);
+ case 1: return subtotalInstance(AggregateFunction.AVERAGE, true);
+ case 2: return Count.subtotalInstance(true);
+ case 3: return Counta.subtotalInstance(true);
+ case 4: return subtotalInstance(AggregateFunction.MAX, true);
+ case 5: return subtotalInstance(AggregateFunction.MIN, true);
+ case 6: return subtotalInstance(AggregateFunction.PRODUCT, true);
+ case 7: return subtotalInstance(AggregateFunction.STDEV, true);
case 8: throw new NotImplementedFunctionException("STDEVP");
- case 9: return subtotalInstance(AggregateFunction.SUM);
+ case 9: return subtotalInstance(AggregateFunction.SUM, true);
case 10: throw new NotImplementedFunctionException("VAR");
case 11: throw new NotImplementedFunctionException("VARP");
- }
- if (functionCode > 100 && functionCode < 112) {
- throw new NotImplementedException("SUBTOTAL - with 'exclude hidden values' option");
+ case 101: return subtotalInstance(AggregateFunction.AVERAGE, false);
+ case 102: return Count.subtotalInstance(false);
+ case 103: return Counta.subtotalInstance(false);
+ case 104: return subtotalInstance(AggregateFunction.MAX, false);
+ case 105: return subtotalInstance(AggregateFunction.MIN, false);
+ case 106: return subtotalInstance(AggregateFunction.PRODUCT, false);
+ case 107: return subtotalInstance(AggregateFunction.STDEV, false);
+ case 108: throw new NotImplementedFunctionException("STDEVP SUBTOTAL with 'exclude hidden values' option");
+ case 109: return subtotalInstance(AggregateFunction.SUM, false);
+ case 110: throw new NotImplementedFunctionException("VAR SUBTOTAL with 'exclude hidden values' option");
+ case 111: throw new NotImplementedFunctionException("VARP SUBTOTAL with 'exclude hidden values' option");
}
throw EvaluationException.invalidValue();
}
}
final Function innerFunc;
+ int functionCode = 0;
try {
ValueEval ve = OperandResolver.getSingleValue(args[0], srcRowIndex, srcColumnIndex);
- int functionCode = OperandResolver.coerceValueToInt(ve);
+ functionCode = OperandResolver.coerceValueToInt(ve);
innerFunc = findFunction(functionCode);
} catch (EvaluationException e) {
return e.getErrorEval();
if(lazyRefEval.isSubTotal()) {
it.remove();
}
+ if (functionCode > 100 && lazyRefEval.isRowHidden()) {
+ it.remove();
+ }
}
}
return _xs.getLastRowNum();
}
+ /* (non-Javadoc)
+ * @see org.apache.poi.ss.formula.EvaluationSheet#isRowHidden(int)
+ * @since POI 4.0.2
+ */
+ public boolean isRowHidden(int rowIndex) {
+ SXSSFRow row = _xs.getRow(rowIndex);
+ if (row == null) return false;
+ return row.getZeroHeight();
+ }
+
@Override
public EvaluationCell getCell(int rowIndex, int columnIndex) {
SXSSFRow row = _xs.getRow(rowIndex);
return _xs.getLastRowNum();
}
+ /* (non-Javadoc)
+ * @see org.apache.poi.ss.formula.EvaluationSheet#isRowHidden(int)
+ * @since POI 4.0.2
+ */
+ public boolean isRowHidden(int rowIndex) {
+ final XSSFRow row = _xs.getRow(rowIndex);
+ if (row == null) return false;
+ return row.getZeroHeight();
+ }
+
/* (non-JavaDoc), inherit JavaDoc from EvaluationWorkbook
* @since POI 3.15 beta 3
*/
confirmExpectedResult(evaluator, "SUBTOTAL(COUNT;B2:B8,C2:C8)", cellC2, 3.0);
confirmExpectedResult(evaluator, "SUBTOTAL(COUNTA;B2:B8,C2:C8)", cellC3, 5.0);
+ // test same functions ignoring hidden rows over a copy of the same data
+ cellC1 = sheet.getRow(11).getCell(3);
+ cellC2 = sheet.getRow(12).getCell(3);
+ cellC3 = sheet.getRow(13).getCell(3);
+ confirmExpectedResult(evaluator, "SUBTOTAL(SUM NO HIDDEN;B22:B28;C22:C28)", cellC1, 17.0);
+ confirmExpectedResult(evaluator, "SUBTOTAL(COUNT NO HIDDEN;B22:B28,C22:C28)", cellC2, 2.0);
+ confirmExpectedResult(evaluator, "SUBTOTAL(COUNTA NO HIDDEN;B22:B28,C22:C28)", cellC3, 4.0);
+
+
workbook.close();
}
{ "SUBTOTAL(8,B2:B3)", NotImplementedException.class.getName() },
{ "SUBTOTAL(10,B2:B3)", NotImplementedException.class.getName() },
{ "SUBTOTAL(11,B2:B3)", NotImplementedException.class.getName() },
- { "SUBTOTAL(107,B2:B3)", NotImplementedException.class.getName() },
{ "SUBTOTAL(0,B2:B3)", null },
{ "SUBTOTAL(9)", FormulaParseException.class.getName() },
{ "SUBTOTAL()", FormulaParseException.class.getName() },
try {
a3.setCellFormula(f[0]);
fe.evaluateAll();
- assertEquals(FormulaError.VALUE.getCode(), a3.getErrorCellValue());
+ assertEquals(f[0], FormulaError.VALUE.getCode(), a3.getErrorCellValue());
} catch (Exception e) {
actualEx = e;
}