Line 0
Link Here
|
|
|
1 |
package org.apache.poi.ss.formula; |
2 |
|
3 |
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; |
4 |
import org.apache.poi.hssf.usermodel.HSSFCell; |
5 |
import org.apache.poi.hssf.usermodel.HSSFWorkbook; |
6 |
import org.apache.poi.hssf.util.CellReference; |
7 |
import org.apache.poi.ss.SpreadsheetVersion; |
8 |
import org.apache.poi.ss.formula.FormulaParseException; |
9 |
import org.apache.poi.ss.formula.FormulaParser; |
10 |
import org.apache.poi.ss.formula.FormulaRenderer; |
11 |
import org.apache.poi.ss.formula.FormulaShifter; |
12 |
import org.apache.poi.ss.formula.FormulaType; |
13 |
import org.apache.poi.ss.formula.ptg.Area3DPtg; |
14 |
import org.apache.poi.ss.formula.ptg.Area3DPxg; |
15 |
import org.apache.poi.ss.formula.ptg.AreaPtg; |
16 |
import org.apache.poi.ss.formula.ptg.Ptg; |
17 |
import org.apache.poi.ss.formula.ptg.Ref3DPtg; |
18 |
import org.apache.poi.ss.formula.ptg.Ref3DPxg; |
19 |
import org.apache.poi.ss.formula.ptg.RefPtg; |
20 |
import org.apache.poi.ss.usermodel.Cell; |
21 |
import org.apache.poi.ss.usermodel.CellType; |
22 |
import org.apache.poi.ss.usermodel.Row; |
23 |
import org.apache.poi.ss.usermodel.Sheet; |
24 |
import org.apache.poi.ss.usermodel.Workbook; |
25 |
import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook; |
26 |
import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
27 |
|
28 |
/*** |
29 |
* Wrapper around FormulaShifter, with added functionality. Takes formula string as input (instead of Ptg[]), so it makes better automation. |
30 |
* Works with both rows and columns. |
31 |
* @author Dragan Jovanović (zmau3012 @ skype) |
32 |
* |
33 |
*/ |
34 |
public class CompleteFormulaShifter { |
35 |
private FormulaShifter formulaShifter; |
36 |
private SpreadsheetVersion spreadSheetVersion; |
37 |
private Sheet shiftingSheet; |
38 |
private boolean rowModeElseColumn; |
39 |
|
40 |
public static CompleteFormulaShifter createForItemShift(Sheet sheet, boolean rowModeElseColumn, int firstShiftItemIndex, int lastShiftItemIndex, int shiftStep){ |
41 |
CompleteFormulaShifter instance = new CompleteFormulaShifter(sheet, rowModeElseColumn); |
42 |
instance.configureForItemShifting(firstShiftItemIndex, lastShiftItemIndex, shiftStep); |
43 |
return instance; |
44 |
} |
45 |
public static CompleteFormulaShifter createForItemCopy(Sheet sheet, boolean rowModeElseColumn, int firstShiftItemIndex, int lastShiftItemIndex, int shiftStep){ |
46 |
CompleteFormulaShifter instance = new CompleteFormulaShifter(sheet, rowModeElseColumn); |
47 |
instance.configureForItemCopiing(firstShiftItemIndex, lastShiftItemIndex, shiftStep); |
48 |
return instance; |
49 |
} |
50 |
|
51 |
public CompleteFormulaShifter(Sheet shiftingSheet, boolean rowModeElseColumn){ |
52 |
this.shiftingSheet = shiftingSheet; |
53 |
this.spreadSheetVersion = getSpreadsheetVersion(shiftingSheet); |
54 |
this.rowModeElseColumn = rowModeElseColumn; |
55 |
} |
56 |
|
57 |
public void configureForItemShifting(int firstShiftItemIndex, int lastShiftItemIndex, int shiftStep){ |
58 |
formulaShifter = FormulaShifter.createForRowShift(shiftingSheet.getWorkbook().getSheetIndex(shiftingSheet), shiftingSheet.getSheetName(), |
59 |
firstShiftItemIndex, lastShiftItemIndex, shiftStep, spreadSheetVersion); |
60 |
} |
61 |
public void configureForItemCopiing(int firstShiftRowIndex, int lastShiftItemIndex, int shiftStep){ |
62 |
formulaShifter = FormulaShifter.createForRowCopy(shiftingSheet.getWorkbook().getSheetIndex(shiftingSheet), shiftingSheet.getSheetName(), |
63 |
firstShiftRowIndex, lastShiftItemIndex, shiftStep, spreadSheetVersion); |
64 |
} |
65 |
public void processFormulasOnOtherSheets(){ |
66 |
Workbook wb = shiftingSheet.getWorkbook(); |
67 |
int sheetIndex = 0; |
68 |
while(sheetIndex < wb.getNumberOfSheets()){ |
69 |
Sheet aSheet = wb.getSheetAt(sheetIndex++); |
70 |
if(!aSheet.getSheetName().equals(shiftingSheet.getSheetName())){ |
71 |
shiftAllFormulas(aSheet); |
72 |
} |
73 |
} |
74 |
} |
75 |
|
76 |
private void shiftAllFormulas(Sheet aSheet){ |
77 |
for(int rowNo = 0; rowNo < aSheet.getPhysicalNumberOfRows(); rowNo++) |
78 |
{ |
79 |
Row row = aSheet.getRow(rowNo); |
80 |
for (int i = 0; i <= row.getPhysicalNumberOfCells(); i++){ |
81 |
org.apache.poi.ss.usermodel.Cell currentCell = row.getCell(i); |
82 |
if(currentCell != null){ |
83 |
if(currentCell.getCellTypeEnum() == CellType.FORMULA) |
84 |
shiftFormula(currentCell); |
85 |
} |
86 |
} |
87 |
} |
88 |
} |
89 |
|
90 |
public void shiftFormula(Cell cell) { |
91 |
switch (spreadSheetVersion) { |
92 |
case EXCEL2007: |
93 |
int currentSheetIndex = getSheetIndex(cell.getSheet()); |
94 |
String shiftedFormula = getShiftedFormulaForXSSF(cell.toString(), currentSheetIndex); |
95 |
cell.setCellFormula(shiftedFormula); |
96 |
break; |
97 |
case EXCEL97: |
98 |
shiftFormulaForHSSF((HSSFCell)cell); |
99 |
break; |
100 |
} |
101 |
} |
102 |
|
103 |
// original code in org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter.shiftFormula() |
104 |
/** Recalculates formula, for EXCEL2007 spreadsheets |
105 |
* @param formula string representing formula |
106 |
* */ |
107 |
public String getShiftedFormulaForXSSF(String formula, int currentSheetIndex) { |
108 |
boolean adjustSucceeded; |
109 |
Workbook wb = shiftingSheet.getWorkbook(); |
110 |
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create((XSSFWorkbook) wb); |
111 |
try { |
112 |
Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, currentSheetIndex); |
113 |
String shiftedFmla = null; |
114 |
if(rowModeElseColumn) |
115 |
adjustSucceeded = formulaShifter.adjustFormula(ptgs, currentSheetIndex); |
116 |
else { |
117 |
Ptg[] transponedPtgs = transpose(ptgs); |
118 |
adjustSucceeded = formulaShifter.adjustFormula(transponedPtgs, currentSheetIndex); |
119 |
if (adjustSucceeded) |
120 |
ptgs = transpose(transponedPtgs); |
121 |
} |
122 |
if(adjustSucceeded){ |
123 |
shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs); |
124 |
// dont use logger here, because of transfer to POI project |
125 |
//System.out.println(String.format("original : %s; shifted : %s", formula, shiftedFmla)); |
126 |
return shiftedFmla; |
127 |
} |
128 |
else return formula; // better return some error msg ? |
129 |
} catch (FormulaParseException fpe) { |
130 |
// Log, but don't change, rather than breaking; uncomment this in POI context |
131 |
//logger.log(POILogger.WARN, "Error shifting formula on row ", row.getRowNum(), fpe); |
132 |
return formula; |
133 |
} |
134 |
} |
135 |
|
136 |
// original code in org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate.updateFormulasAfterRowShift() |
137 |
/** Recalculates formula, for EXCEL97 spreadsheets |
138 |
* @param formula string representing formula |
139 |
* */ |
140 |
void shiftFormulaForHSSF(HSSFCell cell) { |
141 |
boolean adjustSucceeded; |
142 |
int sheetIndex = getSheetIndex(cell.getSheet()); |
143 |
FormulaRecordAggregate fra = (FormulaRecordAggregate)cell.getCellValueRecord(); |
144 |
Ptg[] ptgs = fra.getFormulaTokens(); |
145 |
if(rowModeElseColumn) |
146 |
adjustSucceeded = formulaShifter.adjustFormula(ptgs, sheetIndex); |
147 |
else { |
148 |
Ptg[] transposedPtgs = transpose(ptgs); |
149 |
adjustSucceeded = formulaShifter.adjustFormula(transposedPtgs, sheetIndex); |
150 |
if (adjustSucceeded) |
151 |
ptgs = transpose(transposedPtgs); |
152 |
} |
153 |
fra.setParsedExpression(ptgs); |
154 |
} |
155 |
|
156 |
private Ptg[] transpose(Ptg[] original){ |
157 |
Ptg[] transposed = new Ptg[original.length]; |
158 |
int index = 0; |
159 |
for (Ptg ptg : original) { |
160 |
String ptgType = ptg.getClass().getSimpleName(); |
161 |
if(ptgType.equals("Ref3DPtg")){ //3D means (sheetNo, col, row) reference, for example Sheet1!B3; xls version |
162 |
int oldColumnIndex = ((Ref3DPtg) ptg).getColumn(); |
163 |
((Ref3DPtg) ptg).setColumn(((Ref3DPtg) ptg).getRow()); |
164 |
((Ref3DPtg) ptg).setRow(oldColumnIndex); |
165 |
transposed[index] = ptg; |
166 |
} else if(ptgType.equals("Ref3DPxg")){ //3D means (sheetNo, col, row) reference, for example Sheet1!B3; xlsx version |
167 |
int oldColumnIndex = ((Ref3DPxg) ptg).getColumn(); |
168 |
((Ref3DPxg) ptg).setColumn(((Ref3DPxg) ptg).getRow()); |
169 |
((Ref3DPxg) ptg).setRow(oldColumnIndex); |
170 |
transposed[index] = ptg; |
171 |
} else if(ptgType.equals("AreaPtg")){ // region for aggregate function, for example A1:B3 or Sheet1!B3:Sheet1!C3 |
172 |
int oldFirstColumnIndex = ((AreaPtg) ptg).getFirstColumn(); |
173 |
((AreaPtg) ptg).setFirstColumn(((AreaPtg) ptg).getFirstRow()); |
174 |
((AreaPtg) ptg).setFirstRow(oldFirstColumnIndex); |
175 |
int oldLastColumnIndex = ((AreaPtg) ptg).getLastColumn(); |
176 |
((AreaPtg) ptg).setLastColumn(((AreaPtg) ptg).getLastRow()); |
177 |
((AreaPtg) ptg).setLastRow(oldLastColumnIndex); |
178 |
transposed[index] = ptg; |
179 |
} |
180 |
else if(ptgType.equals("Area3DPtg")){ //for example SUM(Sheet1!B3:C3); xls version |
181 |
int oldFirstColumnIndex = ((Area3DPtg) ptg).getFirstColumn(); |
182 |
((Area3DPtg) ptg).setFirstColumn(((Area3DPtg) ptg).getFirstRow()); |
183 |
((Area3DPtg) ptg).setFirstRow(oldFirstColumnIndex); |
184 |
int oldLastColumnIndex = ((Area3DPtg) ptg).getLastColumn(); |
185 |
((Area3DPtg) ptg).setLastColumn(((Area3DPtg) ptg).getLastRow()); |
186 |
((Area3DPtg) ptg).setLastRow(oldLastColumnIndex); |
187 |
transposed[index] = ptg; |
188 |
} |
189 |
else if(ptgType.equals("Area3DPxg")){ //for example SUM(Sheet1!B3:C3); xlsx version |
190 |
int oldFirstColumnIndex = ((Area3DPxg) ptg).getFirstColumn(); |
191 |
((Area3DPxg) ptg).setFirstColumn(((Area3DPxg) ptg).getFirstRow()); |
192 |
((Area3DPxg) ptg).setFirstRow(oldFirstColumnIndex); |
193 |
int oldLastColumnIndex = ((Area3DPxg) ptg).getLastColumn(); |
194 |
((Area3DPxg) ptg).setLastColumn(((Area3DPxg) ptg).getLastRow()); |
195 |
((Area3DPxg) ptg).setLastRow(oldLastColumnIndex); |
196 |
transposed[index] = ptg; |
197 |
} else if(ptgType.equals("RefPtg")){ // common simple reference, like A2 |
198 |
RefPtg transponedCellRefToken = new RefPtg(transpose(ptg.toFormulaString())); |
199 |
transposed[index] = transponedCellRefToken; |
200 |
} |
201 |
else // operators like + or SUM, for example |
202 |
transposed[index] = ptg; |
203 |
index++; |
204 |
} |
205 |
return transposed; |
206 |
} |
207 |
|
208 |
public static String transpose(String cellreference){ |
209 |
CellReference original = new CellReference(cellreference); |
210 |
// transpose, calling public CellReference(int *pRow*, int *pCol*) !!!! |
211 |
CellReference transposed = new CellReference(original.getCol(), original.getRow(), original.isColAbsolute(), original.isRowAbsolute()); |
212 |
return transposed.formatAsString(); |
213 |
} |
214 |
|
215 |
private int getSheetIndex(Sheet sheet){ |
216 |
return sheet.getWorkbook().getSheetIndex(sheet); |
217 |
} |
218 |
|
219 |
public static SpreadsheetVersion getSpreadsheetVersion(Sheet sheet){ |
220 |
if(sheet.getWorkbook() instanceof XSSFWorkbook) |
221 |
return SpreadsheetVersion.EXCEL2007; |
222 |
else if(sheet.getWorkbook() instanceof HSSFWorkbook) |
223 |
return SpreadsheetVersion.EXCEL97; |
224 |
else return null; |
225 |
} |
226 |
|
227 |
} |