sheet.getRow(rowIndex) returns NULL - APACHE POI - java

I am using the APACHE POI library to read an excel file in xlsx format.
My problem is that I want to remove the last row from each sheet of this excel, I found a way to find the last row to remove, but it returns an int. The method sheet.removeRow(Row var1); would solve my problem.
Well, having the row number that I want to delete and a method to delete that row. I just need to convert the lastRow (int) in a Row type so I can use the sheet.removeRow method.
To do that I used the following code: Row a = sheet.getRow(lastRow) this method should return a Row with that index. But instead it returns NULL.
Any idea what I'm doing wrong or how to convert the line number I want to remove into a Row type?
Appreciate your help!
Here is an excerpt of the code that read my excel file
public static List<Measure> excelToMeasures(InputStream is, ProjectMeasureFile projectMeasureFile) throws IOException {
List<Measure> measures = new ArrayList<>();
try (Workbook workbook = new XSSFWorkbook(is)) {
for (int i = 0; i < 3; i++) {
Sheet sheet = workbook.getSheetAt(i);
int lastRow = sheet.getPhysicalNumberOfRows() -1;
removeRow(sheet, lastRow);
int rowNumber = 0;
for (Row row : sheet) {
// skip header
if (rowNumber == 0) {
rowNumber++;
continue;
}
Iterator<Cell> cellIterator = row.iterator();
List<Cell> cellObject = new ArrayList<>();
while (cellIterator.hasNext()) {
cellObject.add(cellIterator.next());
}
if (cellObject.size() > 0) {
Measure measure = new Measure();
measure.setArea(sheet.getSheetName());
measure.setCode(convertStringCell(cellObject.get(0)));
measure.setBehavior(convertStringCell(cellObject.get(1)));
measure.setHumanDependencyFactor(convertNumericCell(cellObject.get(2)));
Double measureType = convertToPercentage(measure.getHumanDependencyFactor());
measure.setHumanDependencyFactor(measureType);
measure.setMeasurementResults(convertNumericCell(cellObject.get(3)));
Double value = convertToPercentage(measure.getMeasurementResults());
measure.setMeasurementResults(value);
measure.setProjectMeasureFile(projectMeasureFile);
measures.add(measure);
}
rowNumber++;
}
}
} catch (Exception e) {
log.error("An error occurred when trying to parse the file.");
e.printStackTrace();
}
return measures;
}
And here is the method for removing a row:
public static void removeRow(Sheet sheet, int rowIndex) {
int lastRowNum = sheet.getPhysicalNumberOfRows() -1;
if (rowIndex == lastRowNum) {
Row removingRow = sheet.getRow(rowIndex);
sheet.getRow(rowIndex);
Row a = sheet.getRow(rowIndex);
if (removingRow != null) {
System.out.println(sheet.getRow(lastRowNum).getCell(0).toString());
sheet.removeRow(removingRow);
}
}
}

At first to title of your question: Sheet.getRow will return NULL by design. It returns NULL if the row behind the row index is not stored in sheet. So you always need to check for NULL after Sheet.getRow. Same is for Row.getCell which returns NULL by design for cells which are not stored in the row.
And Sheet.getPhysicalNumberOfRows is not the correct way to get the last row in sheet.
A Excel sheet only physically contains rows having cells stored in them. Rows which are completely empty are not physically stored. So if a sheet only contains data in rows 1, 2, 5, 6 and 7, then Sheet.getPhysicalNumberOfRows will return 5 but last row is 7.
There is Sheet.getLastRowNum to get the last row number (0-based) in sheet. So that would return 6 in the example above and sheet.getRow(6) would get the last row and not NULL.
But there is another problem to consider. In Excel rows might not be totally empty but only contains cells which are blank. Blank cells might be stored because they have cell formats or they had content before. Sheet.getLastRowNum gets the last stored row, even if this row only contains blank cells. So you need to check whether the row behind Sheet.getLastRowNum contains only blank cells by iterating over the cells and check for CellType.BLANK if you need the last filled row.
The following method gets the last filled row in a sheet. It returns NULL if no filled row was found.
Row getLastFilledRow(Sheet sheet) {
int lastStoredRowNum = sheet.getLastRowNum();
for (int r = lastStoredRowNum; r >= 0; r--) {
Row row = sheet.getRow(r);
if (row != null) {
for (Cell cell : row) {
if (cell.getCellType() != CellType.BLANK) return row;
}
}
}
return null;
}

Related

Java merge two cells into one in EXCEL

I am trying to merge two cells into one
for (String cellLLL : sheetNumd) {
XSSFSheet sheets = workbook.getSheet(cellLLL);
if (sheets != null) {
for (int rowNum = 4; rowNum < sheets.getLastRowNum() + 1; rowNum++) {
Row row = sheets.getRow(rowNum);
//sheets.addMergedRegion(new CellRangeAddress(4, 5, 6, 6));
if (row != null) {
for (Cell cell : row) {
int columnIndex = cell.getColumnIndex();
switch (columnIndex) {
case 6:
cellValue6 = cell.getStringCellValue();
break;
}
}
}
if (cellLLL.equals("Sheet1")) {
list6.add(cellValue6);
}
The cells I want to merge are G5 and G6
In my example I comment out the code where I am trying to do it. But if I run it, then I get an error -
java.lang.IllegalStateException: Cannot add merged region G5:G6 to sheet because it overlaps with an existing merged region (G5:G6).
Maybe I'm doing something wrong. Please tell me
The library I am using is Apache Poi

iterating over the columns of a row through poi in java

I have a excel file named abc.xls in my c: drive (local computer) , now in that excel file in the first sheet itself there is a table as shown below and this below table can lie in any range with in the sheet so i have developed the below java program which will scan the entire sheet first by row basis and then on column basis and will find the cell where
TradeRef is there
TradeRef TMS Deal Date
12 45 DRT 23/97/2014
23 36 QWE 21/07/2015
now the problem in my below program is that it captures the cell where TradeRef is there and then it iterates over the columns and then in similar fashion it captures the next row and iterating over the columns
but the logic that i want to apply is that when it captures the TradeRef cell and iterating over the columns and reached to the last column of the table which is Date in the above table then it should further scan the next 20 columns within the same row and if within the next 20 columns there is no cell having any value then it should move to the next row and if within the 20 columns it mite be that any cell can have value then in that case it should read that cell value
so it would be like
TradeRef TMS Deal Date <----- scan next 20 columns is there is no value in next 20 cells then move to next row else include that cell value also------->
12 45 DRT 23/97/2014
23 36 QWE 21/07/2015
so please advise how to implement the above logic of scanning the next 20 columns within the row below is my earlier implementation that is
public class AAA {
public static void main(String[] args) throws IOException {
FileInputStream file = null ;
try {
file = new FileInputStream(new File("C:\\abc.xls"));
HSSFWorkbook workbook = new HSSFWorkbook(file);
HSSFSheet firstSheet = workbook.getSheetAt(0);
Iterator<Row> iterator = firstSheet.iterator();
Cell c = findFirstRow(firstSheet);
}
catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
file.close();
}
}
public static Cell findFirstRow(HSSFSheet firstSheet) {
for (Row row : firstSheet) {
for (Cell cell : row) {
cell.setCellType(cell.CELL_TYPE_STRING);
if ("TradeRef".equals(cell.getStringCellValue())) {
int row1 = cell.getRowIndex() + 1;
int col = cell.getColumnIndex();
if (firstSheet.getRow(row1) == null)
throw new RuntimeException("Row " + row1 + 1 + " is empty!");
Cell startOfFirstDataRow = firstSheet.getRow(row1).getCell(col);
if (startOfFirstDataRow == null) {
CellReference ref = new CellReference(row1, col);
throw new RuntimeException("Data not found at " + ref.formatAsString());
}
return startOfFirstDataRow;
}
}
}
throw new RuntimeException("TradingRef header cell not found!");
}
}
so please advise how can i implement the above logic of scanning next 20 columns
First, you should probably use the org.apache.poi.ss.usermodel API, which work with all Excel files while the HSSF* classes only work with .xls files.
The org.apache.poi.ss.usermodel.Sheet class has a getFirstRow() method that you can use to start your search. Next you want to find a sequence of Cells containing the given Strings, like:
// This is an array containing the headers
final String[] headers = { "TradeRef", "TMS", "Deal", "Date" };
// now we take row from above to be the Row object where we seek our headers
int last = row.getLastCellNum();
for (int c = row.getFirstCellNum(); c < last; c++) {
int h = 0;
// check if the cell at (c + h) has the required value
for (; h < headers.length && c + h < last; h++) {
if (!headers[h].equals(row.getCell(c + h).getStringCellValue())) {
break; // if the cell value differs from our header
}
}
if (h == headers.length) // this means the break was never invoked
return c; // found it
}
return -1; // not found
if you want to skip column you can use
while (cellsInRow.hasNext()) {
Cell currentCell = cellsInRow.next();
if (currentCell.getColumnIndex() >= 3 && currentCell.getColumnIndex() <= 6) {
continue;
}
}
to skip column 4, 5, 6 and 7 from excel file. index starts at 0

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

JTable Empty cell validation for the filled rows

I am doing a project in java swing in which I have to use a JTable.
By default jtable can validate the inputs (float,int) by turning into red when wrong input is given.
But I want to validate the empty cell left in the table at the time of save button click.
Here is my code:
public boolean validCheck() {
if (jTable.getCellEditor() != null) {
jTable.getCellEditor().stopCellEditing();
}
for (int i = 0; i < jTable3.getRowCount(); i++) {
for (int j = 0; j < jTable3.getColumnCount(); j++) {
String val = jTable3.getValueAt(row, col).toString();
if (val.trim().length() == 0) {
return false;(joptionpane.showmessagedialog(null,"field empty");)
}
}
}
return true;
}
This code is checking all the rows in the table and displaying the field empty message. I need to check only filled rows(rows which have data) not all the rows in the jtable.
Any suggestions would be helpful.
You need to change your logic to first look if a row had data at all. Then (and only then), take a second pass to validate that all columns in that row are filled.

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