Instead of manipulating the formula strings to update formula references, best practice is really using a FormulaParser
in my opinion. Advantage is that really all possible formulas can be updated as long as the FormulaParser
knows them. Manipulating the formula strings easily leads to problems. For example a formula template like "SUM(H%d;I%d)"
has %d
as a variable to be replaced. But the percent sign could also be a not variable part in some formulas.
The apache poi
has a FormulaParser
which can be used. In Apache POI update formula references when copying I have shown this already for XSSF
only.
Since this question is about HSSF
I will show an String copyFormula(Sheet sheet, String formula, int coldiff, int rowdiff)
method which works for SS
that is for HSSF
as well as for XSSF
.
import java.io.FileOutputStream;
import org.apache.poi.ss.formula.*;
import org.apache.poi.ss.formula.ptg.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
public class ExcelCopyFormula {
private static String copyFormula(Sheet sheet, String formula, int coldiff, int rowdiff) {
Workbook workbook = sheet.getWorkbook();
EvaluationWorkbook evaluationWorkbook = null;
if (workbook instanceof HSSFWorkbook) {
evaluationWorkbook = HSSFEvaluationWorkbook.create((HSSFWorkbook) workbook);
} else if (workbook instanceof XSSFWorkbook) {
evaluationWorkbook = XSSFEvaluationWorkbook.create((XSSFWorkbook) workbook);
}
Ptg[] ptgs = FormulaParser.parse(formula, (FormulaParsingWorkbook)evaluationWorkbook,
FormulaType.CELL, sheet.getWorkbook().getSheetIndex(sheet));
for (int i = 0; i < ptgs.length; i++) {
if (ptgs[i] instanceof RefPtgBase) { // base class for cell references
RefPtgBase ref = (RefPtgBase) ptgs[i];
if (ref.isColRelative())
ref.setColumn(ref.getColumn() + coldiff);
if (ref.isRowRelative())
ref.setRow(ref.getRow() + rowdiff);
}
else if (ptgs[i] instanceof AreaPtgBase) { // base class for range references
AreaPtgBase ref = (AreaPtgBase) ptgs[i];
if (ref.isFirstColRelative())
ref.setFirstColumn(ref.getFirstColumn() + coldiff);
if (ref.isLastColRelative())
ref.setLastColumn(ref.getLastColumn() + coldiff);
if (ref.isFirstRowRelative())
ref.setFirstRow(ref.getFirstRow() + rowdiff);
if (ref.isLastRowRelative())
ref.setLastRow(ref.getLastRow() + rowdiff);
}
}
formula = FormulaRenderer.toFormulaString((FormulaRenderingWorkbook)evaluationWorkbook, ptgs);
return formula;
}
public static void main(String[] args) throws Exception {
//String type = "XSSF";
String type = "HSSF";
try (Workbook workbook = ("XSSF".equals(type))?new XSSFWorkbook():new HSSFWorkbook();
FileOutputStream out = new FileOutputStream(("XSSF".equals(type))?"Excel.xlsx":"Excel.xls") ) {
Sheet sheet = workbook.createSheet();
for (int r = 2 ; r < 10; r++) {
Row row = sheet.createRow(r);
for (int c = 2 ; c < 5; c++) {
Cell cell = row.createCell(c);
if (r == 2) {
if (c == 2) cell.setCellValue("No");
if (c == 3) cell.setCellValue("Number One");
if (c == 4) cell.setCellValue("Number Two");
} else {
if (c == 2) cell.setCellValue("data" + (r-2));
if (c == 3) cell.setCellValue(r*c);
if (c == 4) cell.setCellValue(r*c);
}
}
}
for (int r = 2 ; r < 10; r++) {
Row row = sheet.getRow(r);
Cell cell = row.createCell(5);
String formula = "D4+E4";
if (r == 2) cell.setCellValue("Formula");
else cell.setCellFormula(copyFormula(sheet, formula, 0, r-3));
}
for (int r = 2 ; r < 10; r++) {
Row row = sheet.getRow(r);
Cell cell = row.createCell(6);
String formula = "G4+F5";
if (r == 2) cell.setCellValue("Cumulative");
else if (r == 3) cell.setCellFormula("F4");
else cell.setCellFormula(copyFormula(sheet, formula, 0, r-4));
}
workbook.getCreationHelper().createFormulaEvaluator().evaluateAll();
workbook.write(out);
}
}
}