How to Clone Function with relative Reference on HSSF apache poi (JAVA) - java

I'm try to make a export Excel, and i need some formula for count number in my excel , but i have a data from database and it will be more than one. so i wanna clone that formula to next data.
it goes well , but the reference cell can't change to next cell, like if we tried copy cell from excel
cellOne[data].setCellFormula(celltwo[data].getCellFormula());
i want to copy count from data1 to data2

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 HSSFas 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);
}
}
}

Related

How to get the checkbox value selected from excel sheet

I am working on a task related to reading an excel which contains radio buttons and checkbox using Java, I have tried a lot but can't move further on this, when I am trying to read the data of cell containing Checkbox it is returning a null value.
Can someone please help with this.
Controls are not contained in the cells but hovers in the drawing layer over the sheet as shapes and only are anchored to the cells. So the cell may be null (because of no content in it) although a shape hovers over it and is anchored to it.
Moreover there are two different kinds of controls possible. There are form controls and ActiveX controls. The states of ActiveX controls are stored in binary code parts activeX1.bin and thus getting the state of them is very hard.
In earlier Excel versions (2007 for example) the anchor informations of all controls are stored in /xl/drawings/vmlDrawing1.vml only. Later versions are storing them in default drawing and within
<controls>
<mc:AlternateContent>
...
</mc:AlternateContent>
</controls>
parts in the sheet XML too. Fortunately there is also /xl/drawings/vmlDrawing1.vml for backwards compatibility.
The following code does parsing the /xl/drawings/vmlDrawing1.vml to get the control which is possible anchored to a cell. If found, it gets this control and if this control is a form control and not a ActiveX control, then it also can get it's state. For ActiveX controls it gets only the information, that a "Pict" is anchored to this cell.
Excel:
Code:
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.util.Units;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import javax.xml.namespace.QName;
class ReadExcelXSSFControls {
public ReadExcelXSSFControls() throws Exception {
XSSFWorkbook wb = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("ExcelWithControls.xlsx"));
Sheet sheet = wb.getSheetAt(0);
for (Row row : sheet) {
for (int c = 0; c < 2; c++) {
Cell cell = row.getCell(c);
if (row.getRowNum() == 0) {
System.out.print(cell + "\t");
} else {
if (c == 0) {
System.out.print(cell + "\t");
} else if (c == 1) {
if (cell == null) cell = row.createCell(c);
Control contol = getControlAt((XSSFCell)cell);
System.out.print(contol);
}
}
}
System.out.println();
}
wb.close();
}
private Control getControlAt(XSSFCell cell) throws Exception {
XSSFSheet sheet = cell.getSheet();
Row row = cell.getRow();
int r = row.getRowNum();
int c = cell.getColumnIndex();
int drheight = (int)Math.round(sheet.getDefaultRowHeightInPoints() * Units.PIXEL_DPI / Units.POINT_DPI);
int rheight = (int)Math.round(row.getHeightInPoints() * Units.PIXEL_DPI / Units.POINT_DPI);
row = null;
if(r > 0) row = sheet.getRow(r-1);
int rheightbefore = (row!=null)?(int)Math.round(row.getHeightInPoints() * Units.PIXEL_DPI / Units.POINT_DPI):drheight;
row = sheet.getRow(r+1);
int rheightafter = (row!=null)?(int)Math.round(row.getHeightInPoints() * Units.PIXEL_DPI / Units.POINT_DPI):drheight;
String name = null;
String objectType = null;
String checked = null;
XmlCursor xmlcursor = null;
if (sheet.getCTWorksheet().getLegacyDrawing() != null) {
String legacyDrawingId = sheet.getCTWorksheet().getLegacyDrawing().getId();
POIXMLDocumentPart part = sheet.getRelationById(legacyDrawingId);
XmlObject xmlDrawing = XmlObject.Factory.parse(part.getPackagePart().getInputStream());
xmlcursor = xmlDrawing.newCursor();
QName qnameClientData = new QName("urn:schemas-microsoft-com:office:excel", "ClientData", "x");
QName qnameAnchor = new QName("urn:schemas-microsoft-com:office:excel", "Anchor", "x");
boolean controlFound = false;
while (xmlcursor.hasNextToken()) {
XmlCursor.TokenType tokentype = xmlcursor.toNextToken();
if (tokentype.isStart()) {
if (qnameClientData.equals(xmlcursor.getName())) {
controlFound = true;
XmlObject clientdata = xmlcursor.getObject();
XmlObject[] xmlchecked = clientdata.selectPath("declare namespace x='urn:schemas-microsoft-com:office:excel' x:Checked");
if (xmlchecked.length > 0) {
checked = "Checked";
} else {
checked = "Not checked";
}
while (xmlcursor.hasNextToken()) {
tokentype = xmlcursor.toNextToken();
if (tokentype.isAttr()) {
if (new QName("ObjectType").equals(xmlcursor.getName())) {
objectType = xmlcursor.getTextValue();
name = objectType + " in row " + (r+1);
}
} else {
break;
}
}
} else if (qnameAnchor.equals(xmlcursor.getName()) && controlFound) {
controlFound = false;
String anchorContent = xmlcursor.getTextValue().trim();
String[] anchorparts = anchorContent.split(",");
int fromCol = Integer.parseInt(anchorparts[0].trim());
int fromColDx = Integer.parseInt(anchorparts[1].trim());
int fromRow = Integer.parseInt(anchorparts[2].trim());
int fromRowDy = Integer.parseInt(anchorparts[3].trim());
int toCol = Integer.parseInt(anchorparts[4].trim());
int toColDx = Integer.parseInt(anchorparts[5].trim());
int toRow = Integer.parseInt(anchorparts[6].trim());
int toRowDy = Integer.parseInt(anchorparts[7].trim());
if (fromCol == c /*needs only starting into the column*/
&& (fromRow == r || (fromRow == r-1 && fromRowDy > rheightbefore/2f))
&& (toRow == r || (toRow == r+1 && toRowDy < rheightafter/2f))) {
//System.out.print(fromCol + ":" +fromColDx + ":" + fromRow + ":" + fromRowDy + ":" + toCol + ":" + toColDx + ":" + toRow + ":" + toRowDy);
break;
}
}
}
}
}
if (xmlcursor!=null && xmlcursor.hasNextToken())
return new Control(name, objectType, checked, r, c);
return new Control("Not found", "unknown", "undefined", r, c);
}
public static void main(String[] args) throws Exception {
ReadExcelXSSFControls o = new ReadExcelXSSFControls();
}
private class Control {
private String name;
private String objectType;
private String checked;
private int row;
private int col;
public Control(String name, String objectType, String checked, int row, int col) {
this.name = name;
this.objectType = objectType;
this.checked = checked;
this.row = row;
this.col= col;
}
public String getName() {
return this.name;
}
public String getObjectType() {
return this.objectType;
}
public String getChecked() {
return this.checked;
}
public int getRow() {
return this.row;
}
public int getCol() {
return this.col;
}
public String toString() {
return this.name + ":r/c:" +row+ "/" + col + ":" + this.checked;
}
}
}
Result:
axel#arichter:~/Dokumente/JAVA/poi/poi-3.17$ java -cp .:./*:./lib/*:./ooxml-lib/* ReadExcelXSSFControls
Product Status
a Checkbox in row 2:r/c:1/1:Checked
b Not found:r/c:2/1:undefined
c Checkbox in row 4:r/c:3/1:Not checked
d Checkbox in row 5:r/c:4/1:Checked
e Radio in row 6:r/c:5/1:Checked
f Not found:r/c:6/1:undefined
g Not found:r/c:7/1:undefined
e Checkbox in row 9:r/c:8/1:Checked
f Not found:r/c:9/1:undefined
h Radio in row 11:r/c:10/1:Not checked
ActiveX Pict in row 14:r/c:13/1:Not checked

How to Get Excel Data Validations Drop Down values for Referenced Cell Ranges

I have stumbled upon a problem when reading in an Excel document, specifically acquiring drop down values (Data Validation) from a Cells. I am able to get the values defined explicitly.
I am able to get the values (720x486, etc) with the following by seeing if cell is within CellRangeAddress.:
Map<CellRangeAddress, String[]> dropDownValues = new HashMap<>();
List<? extends DataValidation> dataValidations = sheet.getDataValidations();
for(DataValidation dataValidation : dataValidations)
{
for(CellRangeAddress cellRangeAddress : dataValidation.getRegions().getCellRangeAddresses())
{
String[] explicitListValues = dataValidation.getValidationConstraint().getExplicitListValues();
if(explicitListValues == null)
{
continue;
}
dropDownValues.put(cellRangeAddress, explicitListValues);
}
}
The code above works only for explicit values. The problem I see is when a range is defined in the source of the Data Validation for a cell:
sheet.getDataValidations();
Does not return anything in regards to the range or any info on the Data Validations. Has anyone been able to get a hold of the Source and evaluate the formula to attain values?
I was able to retrieve the data validations defined by a formula for Excel Sheets newer than 2003.
I had to parse the XSSFSheet for the specific info and then reconstruct and evaluate formula.
Here is what I did to attain all DataValidation values:
Map<CellRangeAddress, String[]> dropDownValues = new HashMap<>();
List<ExtendedDataValidations> extendedDataValidationsList = getExtendedDataValidations(sheet);
for (ExtendedDataValidations extendedDataValidations : extendedDataValidationsList)
{
AreaReference formulaReference = new AreaReference(extendedDataValidations.formula);
CellReference[] allReferencedCells = formulaReference.getAllReferencedCells();
FormulaEvaluator formulaEvaluator = wb.getCreationHelper().createFormulaEvaluator();
String[] values = new String[allReferencedCells.length];
for (int j = 0; j < allReferencedCells.length; j++)
{
CellReference cellReference = allReferencedCells[j];
Sheet valueSheet = wb.getSheet(cellReference.getSheetName());
Cell cell = valueSheet.getRow(cellReference.getRow()).getCell(cellReference.getCol());
CellValue evaluate = formulaEvaluator.evaluate(cell);
values[j] = StringUtils.trimToEmpty(StringUtils.removeStart(StringUtils.removeEnd(evaluate.formatAsString(), "\""), "\""));
}
String stRef = extendedDataValidations.sqref;
String[] regions = stRef.split(" ");
for (String region : regions)
{
String[] parts = region.split(":");
CellReference begin = new CellReference(parts[0]);
CellReference end = parts.length > 1 ? new CellReference(parts[1]) : begin;
CellRangeAddress cellRangeAddress = new CellRangeAddress(begin.getRow(), end.getRow(), begin.getCol(), end.getCol());
dropDownValues.put(cellRangeAddress, values);
}
}
In addition I defined a Struc for the formula and cell reference.
private static class ExtendedDataValidations
{
public String formula;
public String sqref;
}
getExtendedDataValidations grabbed the CTExtensionList where the data validation forumla appeared in the sheet:
public static List<ExtendedDataValidations> getExtendedDataValidations(Sheet sheet)
{
List<ExtendedDataValidations> extendedDataValidationsList = new ArrayList<>();
if (sheet instanceof XSSFSheet)
{
CTExtensionList extLst = ((XSSFSheet) sheet).getCTWorksheet().getExtLst();
if (extLst == null)
{
return extendedDataValidationsList;
}
CTExtension[] extArray = extLst.getExtArray();
List<Node> dataValidationNodes = new ArrayList<>();
for (CTExtension anExtArray : extArray)
{
searchForDataValidation(anExtArray.getDomNode(), dataValidationNodes);
}
for (Node dataValidationNode : dataValidationNodes)
{
ExtendedDataValidations dataValidations = new ExtendedDataValidations();
getDataValidationInfo(dataValidationNode, dataValidations);
extendedDataValidationsList.add(dataValidations);
}
}
return extendedDataValidationsList;
}
searchForDataValidation had to traverse the DOM nodes of the sheet looking for specific info on DataValidation. If found Save it in List:
private static void searchForDataValidation(Node node, List<Node> nodesInQuestion)
{
if (StringUtils.equalsIgnoreCase("x14:dataValidation", node.getNodeName()))
{
nodesInQuestion.add(node);
return;
}
for (int i = 0; i < node.getChildNodes().getLength(); i++)
{
searchForDataValidation(node.getChildNodes().item(i), nodesInQuestion);
}
}
getDataValidationInfo was in charge of getting the formula and Cell Reference.
private static void getDataValidationInfo(Node node, ExtendedDataValidations dataValidations)
{
if (StringUtils.equalsIgnoreCase("#text", node.getNodeName()))
{
if (StringUtils.equalsIgnoreCase("xm:sqref", node.getParentNode().getNodeName()))
{
dataValidations.sqref = node.getNodeValue();
}
else if (StringUtils.equalsIgnoreCase("xm:f", node.getParentNode().getNodeName()))
{
dataValidations.formula = node.getNodeValue();
}
return;
}
for (int i = 0; i < node.getChildNodes().getLength(); i++)
{
getDataValidationInfo(node.getChildNodes().item(i), dataValidations);
}
}
Might appear to be complicated, but it does the trick. Hope it helps!

Reading multiple excel sheet

I am trying to read the sheets of a spread sheet uisng a foor loop. I wanted to know is this the right way of reading especially the use of Sheet Propety [highlighted in the code] :
Cell[][] newcell=new Cell[200][200];
int newsheet = workbook1.getNumberOfSheets();
for (int q=1;q < newsheet;q++)
{
for(int p=0;p < sheet(q).getColumns();p++)
{
for(int p1=0;p1<sheet(q).getRows();p1++)
/*^^^^^^^^^*/
{
newcell[p][p1] = sheet(q).getCell(p, p1);
/*^^^^^^^^^*/
if(newcell[p][p1].equals(saved[j]))
{
System.out.print( newcell[p][0]);
}
}
}
}
Can I use the property of sheet() as sheet(q) because its throwing NullPointerException?
The usual style for working with all the cells in POI is:
for(int sheetNum=0; sheetNum < wb.getNumberOfSheets(); sheetNum++) {
Sheet sheet = wb.getSheetAt(sheetNum);
for (Row row : sheet) {
for (Cell cell : row) {
// Do something here
}
}
}
Maybe switch your code to something more like that?
With jxl (JExcelAPI), this should work:
for (Sheet sheet:workbook1.getSheets()) { // getSheet() returns a Sheet[]
int numCols = sheet.getColumns(); // getColumns() returns an int
for(for int i = 0; i <= numCols; i++) {
Cell[] column = sheet.getColumn(i);
for(Cell cell:column) { // column is a Cell[]
if(cell.equals(saved[j])) {
System.out.print(cell);
}
}
}
}

POI / Excel : applying formulas in a "relative" way

I'm using Apache's POI to manipulate Excel (.xls) files with Java.
I'm trying to create a new cell whom content is the result of a formula as if the user had copied/pasted the formula (what i call the "relative" way, as opposite to "absolute").
To make myself clearer, here is a simple example:
Cell A1 contains "1",B1 contains "2", A2 contains "3", B2 contains "4".
Cell A3 contains the following formula "=A1+B1".
If I copy the formula to the A4 cell under excel, it becomes "=A2+B2" : excel is adapting the content of the formula dynamically.
Unfortunately I cannot get the same result programatically. The only solution I found is to tokenize the formula and do the dirty work myself, but I really doubt that this is supposed to be done that way. I was not able to find what I'm looking for in the guides or in the API.
Is there an easier way to solve this problem ? If it's the case, can you please point me in the right direction ?
Best regards,
Nils
In my sense, user2622016 is right, except his solution manages only cell references, as opposed to area references (it won't work for =SUM(A1:B8) for instance).
Here's how I fixed this :
private void copyFormula(Sheet sheet, Cell org, Cell dest) {
if (org == null || dest == null || sheet == null
|| org.getCellType() != Cell.CELL_TYPE_FORMULA)
return;
if (org.isPartOfArrayFormulaGroup())
return;
String formula = org.getCellFormula();
int shiftRows = dest.getRowIndex() - org.getRowIndex();
int shiftCols = dest.getColumnIndex() - org.getColumnIndex();
XSSFEvaluationWorkbook workbookWrapper =
XSSFEvaluationWorkbook.create((XSSFWorkbook) sheet.getWorkbook());
Ptg[] ptgs = FormulaParser.parse(formula, workbookWrapper, FormulaType.CELL
, sheet.getWorkbook().getSheetIndex(sheet));
for (Ptg ptg : ptgs) {
if (ptg instanceof RefPtgBase) // base class for cell references
{
RefPtgBase ref = (RefPtgBase) ptg;
if (ref.isColRelative())
ref.setColumn(ref.getColumn() + shiftCols);
if (ref.isRowRelative())
ref.setRow(ref.getRow() + shiftRows);
} else if (ptg instanceof AreaPtg) // base class for range references
{
AreaPtg ref = (AreaPtg) ptg;
if (ref.isFirstColRelative())
ref.setFirstColumn(ref.getFirstColumn() + shiftCols);
if (ref.isLastColRelative())
ref.setLastColumn(ref.getLastColumn() + shiftCols);
if (ref.isFirstRowRelative())
ref.setFirstRow(ref.getFirstRow() + shiftRows);
if (ref.isLastRowRelative())
ref.setLastRow(ref.getLastRow() + shiftRows);
}
}
formula = FormulaRenderer.toFormulaString(workbookWrapper, ptgs);
dest.setCellFormula(formula);
}
I still don't know if I had it correct for all cell formulas, but it works for me, fast and reliable.
I too think that there isn't an easy way to do this.
Even the HSSF and XSSD examples on the POI site e.g. TimesheetDemo do the formula construction manually. e.g. around line 110
String ref = (char)('A' + j) + "3:" + (char)('A' + j) + "12";
cell.setCellFormula("SUM(" + ref + ")");
I looked inside FormulaEvaluator class and found some POI internal classes that can do the work for us.
FormulaParser, which parses String to array of "parse things":
String formula = cell.getCellFormula();
XSSFEvaluationWorkbook workbookWrapper =
XSSFEvaluationWorkbook.create((XSSFWorkbook) workbook);
/* parse formula */
Ptg[] ptgs = FormulaParser.parse(formula, workbookWrapper,
FormulaType.CELL, 0 /*sheet index*/ );
ptgs is now our formula in reverse polish notation. Now go through all elements and modify references one by one as you wish:
/* re-calculate cell references */
for( Ptg ptg : ptgs )
if( ptg instanceof RefPtgBase ) //base class for cell reference "things"
{
RefPtgBase ref = (RefPtgBase)ptg;
if( ref.isColRelative() )
ref.setColumn( ref.getColumn() + 0 );
if( ref.isRowRelative() )
ref.setRow( ref.getRow() + 1 );
}
And you're ready to render "parse things" back to String:
formula = FormulaRenderer.toFormulaString(workbookWrapper, ptgs);
cell.setCellFormula( formula );
Another way to copy formula relatively, tested with poi 3.12
public static void copyCellFormula(Workbook workbook, int sheetIndex, int rowIndex, int sourceColumnIndex, int destinationColumnIndex){
XSSFEvaluationWorkbook formulaParsingWorkbook = XSSFEvaluationWorkbook.create((XSSFWorkbook) workbook);
SharedFormula sharedFormula = new SharedFormula(SpreadsheetVersion.EXCEL2007);
Sheet sheet = workbook.getSheetAt(sheetIndex);
Row lookupRow = sheet.getRow(rowIndex);
Cell sourceCell = lookupRow.getCell(sourceColumnIndex);
Ptg[] sharedFormulaPtg = FormulaParser.parse(sourceCell.getCellFormula(), formulaParsingWorkbook, FormulaType.CELL, sheetIndex);
Ptg[] convertedFormulaPtg = sharedFormula.convertSharedFormulas(sharedFormulaPtg, 0, 1);
Cell destinationCell = lookupRow.createCell(destinationColumnIndex);
destinationCell.setCellFormula(FormulaRenderer.toFormulaString(formulaParsingWorkbook, convertedFormulaPtg));
}
Update shared formula as needed:
sharedFormula.convertSharedFormulas(sharedFormulaPtg, rowIndexOffset, columnIndexOffset);
As of poi 3.12, SharedFormula doesn't support cell reference/formula from other sheets (='Sheet1'!A1). Here's an update to SharedFormula:
public class SharedFormula {
private final int _columnWrappingMask;
private final int _rowWrappingMask;
public SharedFormula(SpreadsheetVersion ssVersion) {
this._columnWrappingMask = ssVersion.getLastColumnIndex();
this._rowWrappingMask = ssVersion.getLastRowIndex();
}
public Ptg[] convertSharedFormulas(Ptg[] ptgs, int formulaRow, int formulaColumn) {
Ptg[] newPtgStack = new Ptg[ptgs.length];
RefPtgBase areaNPtg = null;
AreaPtgBase var9 = null;
Object ptg = null;
byte originalOperandClass = 0;
for(int k = 0; k < ptgs.length; ++k) {
ptg = ptgs[k];
originalOperandClass = -1;
if(!((Ptg)ptg).isBaseToken()) {
originalOperandClass = ((Ptg)ptg).getPtgClass();
}
if(ptg instanceof RefPtgBase) {
if(ptg instanceof Ref3DPxg) {
areaNPtg = (Ref3DPxg)ptg;
this.fixupRefRelativeRowAndColumn(areaNPtg, formulaRow, formulaColumn);
ptg = areaNPtg;
}else if(ptg instanceof Ref3DPtg) {
areaNPtg = (Ref3DPtg)ptg;
this.fixupRefRelativeRowAndColumn(areaNPtg, formulaRow, formulaColumn);
ptg = areaNPtg;
}else {
areaNPtg = (RefPtgBase)ptg;
ptg = new RefPtg(this.fixupRelativeRow(formulaRow, areaNPtg.getRow(), areaNPtg.isRowRelative()), this.fixupRelativeColumn(formulaColumn, areaNPtg.getColumn(), areaNPtg.isColRelative()), areaNPtg.isRowRelative(), areaNPtg.isColRelative());
}
((Ptg)ptg).setClass(originalOperandClass);
}else if(ptg instanceof AreaPtgBase) {
if(ptg instanceof Area3DPxg) {
var9 = (Area3DPxg)ptg;
this.fixupAreaRelativeRowAndColumn(var9, formulaRow, formulaColumn);
ptg = var9;
}else if(ptg instanceof Area3DPxg) {
var9 = (Area3DPtg)ptg;
this.fixupAreaRelativeRowAndColumn(var9, formulaRow, formulaColumn);
ptg = var9;
}else {
var9 = (AreaPtgBase)ptg;
ptg = new AreaPtg(this.fixupRelativeRow(formulaRow, var9.getFirstRow(), var9.isFirstRowRelative()), this.fixupRelativeRow(formulaRow, var9.getLastRow(), var9.isLastRowRelative()), this.fixupRelativeColumn(formulaColumn, var9.getFirstColumn(), var9.isFirstColRelative()), this.fixupRelativeColumn(formulaColumn, var9.getLastColumn(), var9.isLastColRelative()), var9.isFirstRowRelative(), var9.isLastRowRelative(), var9.isFirstColRelative(), var9.isLastColRelative());
}
((Ptg)ptg).setClass(originalOperandClass);
}else if(ptg instanceof OperandPtg) {
ptg = ((OperandPtg)ptg).copy();
}
newPtgStack[k] = (Ptg)ptg;
}
return newPtgStack;
}
protected void fixupRefRelativeRowAndColumn(RefPtgBase areaNPtg, int formulaRow, int formulaColumn){
areaNPtg.setRow(this.fixupRelativeRow(formulaRow, areaNPtg.getRow(), areaNPtg.isRowRelative()));
areaNPtg.setColumn(this.fixupRelativeColumn(formulaColumn, areaNPtg.getColumn(), areaNPtg.isColRelative()));
areaNPtg.setRowRelative(areaNPtg.isRowRelative());
areaNPtg.setColRelative(areaNPtg.isColRelative());
}
protected void fixupAreaRelativeRowAndColumn(AreaPtgBase var9, int formulaRow, int formulaColumn){
var9.setFirstRow(this.fixupRelativeRow(formulaRow, var9.getFirstRow(), var9.isFirstRowRelative()));
var9.setLastRow(this.fixupRelativeRow(formulaRow, var9.getLastRow(), var9.isLastRowRelative()));
var9.setFirstColumn(this.fixupRelativeColumn(formulaColumn, var9.getFirstColumn(), var9.isFirstColRelative()));
var9.setLastColumn(this.fixupRelativeColumn(formulaColumn, var9.getLastColumn(), var9.isLastColRelative()));
var9.setFirstRowRelative(var9.isFirstRowRelative());
var9.setLastRowRelative(var9.isLastRowRelative());
var9.setFirstColRelative(var9.isFirstColRelative());
var9.setLastColRelative(var9.isLastColRelative());
}
protected int fixupRelativeColumn(int currentcolumn, int column, boolean relative) {
return relative?column + currentcolumn & this._columnWrappingMask:column;
}
protected int fixupRelativeRow(int currentrow, int row, boolean relative) {
return relative?row + currentrow & this._rowWrappingMask:row;
}
}
I don't think there is. POI would have to parse the formula (taking into account A1 vs. $A$1 vs. $A1 etc.) and I don't believe it has that capacity. When I've done this in the past I've always had to manage this myself. Sorry - not the answer you hoped for!
you can try some third party excel librarys,most of them can handle the relative/absolute range formulas.

Apache POI xls column Remove

I don't find how to remove a column with the Apache POI API.
I would appreciate a sample code or help on this point.
Alan Williamson on the mailing list wrote a small helper for column removal
package org.alanwilliamson.openbd.plugin.spreadsheet;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
/*
* Helper functions to aid in the management of sheets
*/
public class SheetUtility extends Object {
/**
* Given a sheet, this method deletes a column from a sheet and moves
* all the columns to the right of it to the left one cell.
*
* Note, this method will not update any formula references.
*
* #param sheet
* #param column
*/
public static void deleteColumn( Sheet sheet, int columnToDelete ){
int maxColumn = 0;
for ( int r=0; r < sheet.getLastRowNum()+1; r++ ){
Row row = sheet.getRow( r );
// if no row exists here; then nothing to do; next!
if ( row == null )
continue;
// if the row doesn't have this many columns then we are good; next!
int lastColumn = row.getLastCellNum();
if ( lastColumn > maxColumn )
maxColumn = lastColumn;
if ( lastColumn < columnToDelete )
continue;
for ( int x=columnToDelete+1; x < lastColumn + 1; x++ ){
Cell oldCell = row.getCell(x-1);
if ( oldCell != null )
row.removeCell( oldCell );
Cell nextCell = row.getCell( x );
if ( nextCell != null ){
Cell newCell = row.createCell( x-1, nextCell.getCellType() );
cloneCell(newCell, nextCell);
}
}
}
// Adjust the column widths
for ( int c=0; c < maxColumn; c++ ){
sheet.setColumnWidth( c, sheet.getColumnWidth(c+1) );
}
}
/*
* Takes an existing Cell and merges all the styles and forumla
* into the new one
*/
private static void cloneCell( Cell cNew, Cell cOld ){
cNew.setCellComment( cOld.getCellComment() );
cNew.setCellStyle( cOld.getCellStyle() );
switch ( cNew.getCellType() ){
case Cell.CELL_TYPE_BOOLEAN:{
cNew.setCellValue( cOld.getBooleanCellValue() );
break;
}
case Cell.CELL_TYPE_NUMERIC:{
cNew.setCellValue( cOld.getNumericCellValue() );
break;
}
case Cell.CELL_TYPE_STRING:{
cNew.setCellValue( cOld.getStringCellValue() );
break;
}
case Cell.CELL_TYPE_ERROR:{
cNew.setCellValue( cOld.getErrorCellValue() );
break;
}
case Cell.CELL_TYPE_FORMULA:{
cNew.setCellFormula( cOld.getCellFormula() );
break;
}
}
}
}
The answer of cporte is perfectly fine but imho a bit hard to read.
The Idea:
For every row, delete the cell representing the column which shall be deleted and move all cells to the right of this column one to the left.
The simplified Implementation:
//Variables for completeness
Sheet sheet;
int columnToDelete;
for (int rId = 0; rId <= sheet.getLastRowNum(); rId++) {
Row row = sheet.getRow(rId);
for (int cID = columnToDelete; cID < row.getLastCellNum(); cID++) {
Cell cOld = row.getCell(cID);
if (cOld != null) {
row.removeCell(cOld);
}
Cell cNext = row.getCell(cID + 1);
if (cNext != null) {
Cell cNew = row.createCell(cID, cNext.getCellType());
cloneCell(cNew, cNext);
sheet.setColumnWidth(cID, sheet.getColumnWidth(cID + 1));
}
}
}
The clone cell method copied from the other answer for completeness:
private static void cloneCell( Cell cNew, Cell cOld ){
cNew.setCellComment( cOld.getCellComment() );
cNew.setCellStyle( cOld.getCellStyle() );
switch ( cNew.getCellType() ){
case Cell.CELL_TYPE_BOOLEAN:{
cNew.setCellValue( cOld.getBooleanCellValue() );
break;
}
case Cell.CELL_TYPE_NUMERIC:{
cNew.setCellValue( cOld.getNumericCellValue() );
break;
}
case Cell.CELL_TYPE_STRING:{
cNew.setCellValue( cOld.getStringCellValue() );
break;
}
case Cell.CELL_TYPE_ERROR:{
cNew.setCellValue( cOld.getErrorCellValue() );
break;
}
case Cell.CELL_TYPE_FORMULA:{
cNew.setCellFormula( cOld.getCellFormula() );
break;
}
}
}
codewing's solution worked for me like a charm with the following minor changes:
When we clone the cell, the call should be cloneCell(cNew, cNext)
We should set the column width only for the first row.
I'm using version 3.17 of the api, so a few things changed (like CellType changed from int to an enum).
Full code is below (for clarity):
private void deleteColumn(Sheet sheet, int columnToDelete) {
for (int rId = 0; rId < sheet.getLastRowNum(); rId++) {
Row row = sheet.getRow(rId);
for (int cID = columnToDelete; cID < row.getLastCellNum(); cID++) {
Cell cOld = row.getCell(cID);
if (cOld != null) {
row.removeCell(cOld);
}
Cell cNext = row.getCell(cID + 1);
if (cNext != null) {
Cell cNew = row.createCell(cID, cNext.getCellTypeEnum());
cloneCell(cNew, cNext);
//Set the column width only on the first row.
//Other wise the second row will overwrite the original column width set previously.
if(rId == 0) {
sheet.setColumnWidth(cID, sheet.getColumnWidth(cID + 1));
}
}
}
}
}
private void cloneCell(Cell cNew, Cell cOld) {
cNew.setCellComment(cOld.getCellComment());
cNew.setCellStyle(cOld.getCellStyle());
if (CellType.BOOLEAN == cNew.getCellTypeEnum()) {
cNew.setCellValue(cOld.getBooleanCellValue());
} else if (CellType.NUMERIC == cNew.getCellTypeEnum()) {
cNew.setCellValue(cOld.getNumericCellValue());
} else if (CellType.STRING == cNew.getCellTypeEnum()) {
cNew.setCellValue(cOld.getStringCellValue());
} else if (CellType.ERROR == cNew.getCellTypeEnum()) {
cNew.setCellValue(cOld.getErrorCellValue());
} else if (CellType.FORMULA == cNew.getCellTypeEnum()) {
cNew.setCellValue(cOld.getCellFormula());
}
}
I think you have to go down each HSSFRow and call HSSFRow.getCell and then HSSFRow.removeCell. The API is oriented towards rows, rather than columns, and very few operations work at the whole column level.
Sample code (untested):
HSSFSheet sheet = ...
int colToRemove = 5;
Iterator rowIter = sheet.iterator();
while (rowIter.hasNext()) {
HSSFRow row = (HSSFRow)rowIter.next();
HSSFCell cell = row.getCell(colToRemove);
row.removeCell(cell);
}
There is a term confusion: the action that author author would like to achieve is called column shift it terms of Apache POI interface. org.apache.poi.ss.usermodel.Sheet interface provide a clean method to do such thing:
sheet.shiftColumns(startRangeIndex, endRangeIndex, directionQuantifier);
For instance, moving Column B to one position left is easily achievable by calling:
Sheet sheet = loadRequiredSheet();
sheet.shiftColumns(2, 3, -1);
Column A Column B Column C
Data here to be removed <- t should be moved to the left
The code above is working perfectly but I did some modification over the POI version which we are using in case you are using POI version4.0.0.
You can refer the code below for deleting column in excel by using java and POI.
public static void deleteColumn(XSSFSheet sheet, int columnToDelete) {
for (int rId = 0; rId < sheet.getLastRowNum(); rId++) {
Row row = sheet.getRow(rId);
for (int cID = columnToDelete; cID < row.getLastCellNum(); cID++) {
Cell cOld = row.getCell(cID);
if (cOld != null) {
row.removeCell(cOld);
}
Cell cNext = row.getCell(cID + 1);
if (cNext != null) {
Cell cNew = row.createCell(cID, cNext.getCellType());
cloneCell(cNew, cNext);
//Set the column width only on the first row.
//Other wise the second row will overwrite the original column width set previously.
if(rId == 0) {
sheet.setColumnWidth(cID, sheet.getColumnWidth(cID + 1));
}
}
}
}
}
public static void cloneCell(Cell cNew, Cell cOld) {
cNew.setCellComment(cOld.getCellComment());
cNew.setCellStyle(cOld.getCellStyle());
if (CellType.BOOLEAN == cNew.getCellType()) {
cNew.setCellValue(cOld.getBooleanCellValue());
} else if (CellType.NUMERIC == cNew.getCellType()) {
cNew.setCellValue(cOld.getNumericCellValue());
} else if (CellType.STRING == cNew.getCellType()) {
cNew.setCellValue(cOld.getStringCellValue());
} else if (CellType.ERROR == cNew.getCellType()) {
cNew.setCellValue(cOld.getErrorCellValue());
} else if (CellType.FORMULA == cNew.getCellType()) {
cNew.setCellValue(cOld.getCellFormula());
}
}

Categories