<!-- Don't forget to update status.xml too! -->
<release version="3.2-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="add">45890 - fixed HSSFSheet.shiftRows to also update conditional formats</action>
<action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
<action dev="POI-DEVELOPERS" type="add">Optimised the FormulaEvaluator to take cell dependencies into account</action>
<action dev="POI-DEVELOPERS" type="add">16936 - Initial support for whole-row cell styling</action>
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.2-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="add">45890 - fixed HSSFSheet.shiftRows to also update conditional formats</action>
<action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
<action dev="POI-DEVELOPERS" type="add">Optimised the FormulaEvaluator to take cell dependencies into account</action>
<action dev="POI-DEVELOPERS" type="add">16936 - Initial support for whole-row cell styling</action>
import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.RecordAggregate.PositionTrackingVisitor;
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
+import org.apache.poi.hssf.record.formula.FormulaShifter;
import org.apache.poi.hssf.util.CellRangeAddress;
import org.apache.poi.hssf.util.PaneInformation;
import org.apache.poi.util.POILogFactory;
return _mergedCellsTable;
}
+ /**
+ * Updates formulas in cells and conditional formats due to moving of cells
+ * @param externSheetIndex the externSheet index of this sheet
+ */
+ public void updateFormulasAfterCellShift(FormulaShifter shifter, int externSheetIndex) {
+ getRowsAggregate().updateFormulasAfterRowShift(shifter, externSheetIndex);
+ getConditionalFormattingTable().updateFormulasAfterCellShift(shifter, externSheetIndex);
+ // TODO - adjust data validations
+ }
public int addMergedRegion(int rowFrom, int colFrom, int rowTo, int colTo) {
// Validate input
public int getNumMergedRegions() {
return getMergedRecords().getNumberOfMergedRegions();
}
- private ConditionalFormattingTable getConditionalFormattingTable() {
+ public ConditionalFormattingTable getConditionalFormattingTable() {
if (condFormatting == null) {
condFormatting = new ConditionalFormattingTable();
RecordOrderer.addNewSheetRecord(records, condFormatting);
return condFormatting;
}
-
- public int addConditionalFormatting(CFRecordsAggregate cfAggregate) {
- ConditionalFormattingTable cft = getConditionalFormattingTable();
- return cft.add(cfAggregate);
- }
-
- public void removeConditionalFormatting(int index) {
- getConditionalFormattingTable().remove(index);
- }
-
- public CFRecordsAggregate getCFRecordsAggregateAt(int index) {
- return getConditionalFormattingTable().get(index);
- }
-
- public int getNumConditionalFormattings() {
- return getConditionalFormattingTable().size();
- }
-
/**
* Per an earlier reported bug in working with Andy Khan's excel read library. This
* sets the values in the sheet's DimensionsRecord object to be correct. Excel doesn't
{
return field_17_formula1;
}
+ public void setParsedExpression1(Ptg[] ptgs) {
+ field_17_formula1 = safeClone(ptgs);
+ }
+ private static Ptg[] safeClone(Ptg[] ptgs) {
+ if (ptgs == null) {
+ return null;
+ }
+ return (Ptg[]) ptgs.clone();
+ }
/**
* get the stack of the 2nd expression as a list
{
return field_18_formula2;
}
+ public void setParsedExpression2(Ptg[] ptgs) {
+ field_18_formula2 = safeClone(ptgs);
+ }
/**
* called by constructor, should throw runtime exception in the event of a
import org.apache.poi.hssf.record.CFHeaderRecord;
import org.apache.poi.hssf.record.CFRuleRecord;
import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.formula.AreaErrPtg;
+import org.apache.poi.hssf.record.formula.AreaPtg;
+import org.apache.poi.hssf.record.formula.FormulaShifter;
+import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.util.CellRangeAddress;
/**
rv.visitRecord(rule);
}
}
+
+ /**
+ * @return <code>false</code> if this whole {@link CFHeaderRecord} / {@link CFRuleRecord}s should be deleted
+ */
+ public boolean updateFormulasAfterCellShift(FormulaShifter shifter, int currentExternSheetIx) {
+ CellRangeAddress[] cellRanges = header.getCellRanges();
+ boolean changed = false;
+ List temp = new ArrayList();
+ for (int i = 0; i < cellRanges.length; i++) {
+ CellRangeAddress craOld = cellRanges[i];
+ CellRangeAddress craNew = shiftRange(shifter, craOld, currentExternSheetIx);
+ if (craNew == null) {
+ changed = true;
+ continue;
+ }
+ temp.add(craNew);
+ if (craNew != craOld) {
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ int nRanges = temp.size();
+ if (nRanges == 0) {
+ return false;
+ }
+ CellRangeAddress[] newRanges = new CellRangeAddress[nRanges];
+ temp.toArray(newRanges);
+ header.setCellRanges(newRanges);
+ }
+
+ for(int i=0; i<rules.size(); i++) {
+ CFRuleRecord rule = (CFRuleRecord)rules.get(i);
+ Ptg[] ptgs;
+ ptgs = rule.getParsedExpression1();
+ if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) {
+ rule.setParsedExpression1(ptgs);
+ }
+ ptgs = rule.getParsedExpression2();
+ if (ptgs != null && shifter.adjustFormula(ptgs, currentExternSheetIx)) {
+ rule.setParsedExpression2(ptgs);
+ }
+ }
+ return true;
+ }
+
+ private static CellRangeAddress shiftRange(FormulaShifter shifter, CellRangeAddress cra, int currentExternSheetIx) {
+ // FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here
+ AreaPtg aptg = new AreaPtg(cra.getFirstRow(), cra.getLastRow(), cra.getFirstColumn(), cra.getLastColumn(), false, false, false, false);
+ Ptg[] ptgs = { aptg, };
+
+ if (!shifter.adjustFormula(ptgs, currentExternSheetIx)) {
+ return cra;
+ }
+ Ptg ptg0 = ptgs[0];
+ if (ptg0 instanceof AreaPtg) {
+ AreaPtg bptg = (AreaPtg) ptg0;
+ return new CellRangeAddress(bptg.getFirstRow(), bptg.getLastRow(), bptg.getFirstColumn(), bptg.getLastColumn());
+ }
+ if (ptg0 instanceof AreaErrPtg) {
+ return null;
+ }
+ throw new IllegalStateException("Unexpected shifted ptg class (" + ptg0.getClass().getName() + ")");
+ }
}
import org.apache.poi.hssf.model.RecordStream;
import org.apache.poi.hssf.record.CFHeaderRecord;
+import org.apache.poi.hssf.record.formula.FormulaShifter;
/**
* Holds all the conditional formatting for a workbook sheet.<p/>
+ " is outside the allowable range (0.." + (_cfHeaders.size() - 1) + ")");
}
}
+
+ public void updateFormulasAfterCellShift(FormulaShifter shifter, int externSheetIndex) {
+ for (int i = 0; i < _cfHeaders.size(); i++) {
+ CFRecordsAggregate subAgg = (CFRecordsAggregate) _cfHeaders.get(i);
+ boolean shouldKeep = subAgg.updateFormulasAfterCellShift(shifter, externSheetIndex);
+ if (!shouldKeep) {
+ _cfHeaders.remove(i);
+ i--;
+ }
+ }
+ }
}
int sheetIndex = workbook.getSheetIndex(this);
short externSheetIndex = book.checkExternSheet(sheetIndex);
FormulaShifter shifter = FormulaShifter.createForRowShift(externSheetIndex, startRow, endRow, n);
- sheet.getRowsAggregate().updateFormulasAfterRowShift(shifter, externSheetIndex);
+ sheet.updateFormulasAfterCellShift(shifter, externSheetIndex);
int nSheets = workbook.getNumberOfSheets();
for(int i=0; i<nSheets; i++) {
continue;
}
short otherExtSheetIx = book.checkExternSheet(i);
- otherSheet.getRowsAggregate().updateFormulasAfterRowShift(shifter, otherExtSheetIx);
+ otherSheet.updateFormulasAfterCellShift(shifter, otherExtSheetIx);
}
// TODO - adjust formulas in named ranges
}
import org.apache.poi.hssf.model.Sheet;\r
import org.apache.poi.hssf.record.CFRuleRecord;\r
import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;\r
+import org.apache.poi.hssf.record.aggregates.ConditionalFormattingTable;\r
import org.apache.poi.hssf.util.CellRangeAddress;\r
import org.apache.poi.hssf.util.Region;\r
\r
public final class HSSFSheetConditionalFormatting {\r
\r
private final HSSFWorkbook _workbook;\r
- private final Sheet _sheet;\r
+ private final ConditionalFormattingTable _conditionalFormattingTable;\r
\r
/* package */ HSSFSheetConditionalFormatting(HSSFWorkbook workbook, Sheet sheet) {\r
_workbook = workbook;\r
- _sheet = sheet;\r
+ _conditionalFormattingTable = sheet.getConditionalFormattingTable();\r
}\r
\r
/**\r
public int addConditionalFormatting( HSSFConditionalFormatting cf ) {\r
CFRecordsAggregate cfraClone = cf.getCFRecordsAggregate().cloneCFAggregate();\r
\r
- return _sheet.addConditionalFormatting(cfraClone);\r
+ return _conditionalFormattingTable.add(cfraClone);\r
}\r
/**\r
* @deprecated use <tt>CellRangeAddress</tt> instead of <tt>Region</tt>\r
rules[i] = cfRules[i].getCfRuleRecord();\r
}\r
CFRecordsAggregate cfra = new CFRecordsAggregate(regions, rules);\r
- return _sheet.addConditionalFormatting(cfra);\r
+ return _conditionalFormattingTable.add(cfra);\r
}\r
\r
public int addConditionalFormatting(CellRangeAddress[] regions,\r
* @return Conditional Formatting object\r
*/\r
public HSSFConditionalFormatting getConditionalFormattingAt(int index) {\r
- CFRecordsAggregate cf = _sheet.getCFRecordsAggregateAt(index);\r
+ CFRecordsAggregate cf = _conditionalFormattingTable.get(index);\r
if (cf == null) {\r
return null;\r
}\r
* @return number of Conditional Formatting objects of the sheet\r
*/\r
public int getNumConditionalFormattings() {\r
- return _sheet.getNumConditionalFormattings();\r
+ return _conditionalFormattingTable.size();\r
}\r
\r
/**\r
* @param index of a Conditional Formatting object to remove\r
*/\r
public void removeConditionalFormatting(int index) {\r
- _sheet.removeConditionalFormatting(index);\r
+ _conditionalFormattingTable.remove(index);\r
}\r
}\r
*
* @author Dmitriy Kumshayev
*/
-public final class TestHSSFConditionalFormatting extends TestCase
-{
- public void testCreateCF()
- {
+public final class TestHSSFConditionalFormatting extends TestCase {
+ public void testCreateCF() {
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet();
String formula = "7";
}
assertEquals(2, wb.getNumberOfSheets());
}
+
+ public void testShiftRows() {
+
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet();
+
+ HSSFSheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ HSSFConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule(
+ ComparisonOperator.BETWEEN, "sum(A10:A15)", "1+sum(B16:B30)");
+ HSSFFontFormatting fontFmt = rule1.createFontFormatting();
+ fontFmt.setFontStyle(true, false);
+
+ HSSFPatternFormatting patternFmt = rule1.createPatternFormatting();
+ patternFmt.setFillBackgroundColor(HSSFColor.YELLOW.index);
+ HSSFConditionalFormattingRule [] cfRules = { rule1, };
+
+ CellRangeAddress [] regions = {
+ new CellRangeAddress(2, 4, 0, 0), // A3:A5
+ };
+ sheetCF.addConditionalFormatting(regions, cfRules);
+
+ // This row-shift should destroy the CF region
+ sheet.shiftRows(10, 20, -9);
+ assertEquals(0, sheetCF.getNumConditionalFormattings());
+
+ // re-add the CF
+ sheetCF.addConditionalFormatting(regions, cfRules);
+
+ // This row shift should only affect the formulas
+ sheet.shiftRows(14, 17, 8);
+ HSSFConditionalFormatting cf = sheetCF.getConditionalFormattingAt(0);
+ assertEquals("SUM(A10:A23)", cf.getRule(0).getFormula1());
+ assertEquals("1+SUM(B24:B30)", cf.getRule(0).getFormula2());
+
+ sheet.shiftRows(0, 8, 21);
+ cf = sheetCF.getConditionalFormattingAt(0);
+ assertEquals("SUM(A10:A21)", cf.getRule(0).getFormula1());
+ assertEquals("1+SUM(#REF!)", cf.getRule(0).getFormula2());
+ }
}