I am trying to put a title in cell (0,0) and then use a for() loop to add file paths into each following row. However, I am only able to add one line of code; after the title, no other cells in the Excel document are modified.
Here's where I create the file:
private void chooseSheetActionPerformed(java.awt.event.ActionEvent evt) {
sheetNum = chooseSheet.getSelectedIndex() - 1;
try {
WritableWorkbook workbook = Workbook.createWorkbook(newXls);
workbook.createSheet("Sheet1", 0);
workbook.write();
workbook.close();
writeXls();
} catch (Exception ex) {
Logger.getLogger(NewScore.class.getName()).log(Level.SEVERE, null, ex);
}
}
And here's where I try to write to it:
public void writeXls() throws Exception {
Workbook wb = Workbook.getWorkbook(newXls);
WritableWorkbook copy = Workbook.createWorkbook(newXls, wb);
WritableSheet ws = copy.getSheet(sheetNum);
WritableCell cell;
// Body Part - Title
Label lab = new Label(0,0,partName);
cell = (WritableCell) lab;
ws.addCell(cell);
copy.write();
// Image Info
int i = 1;
for (File file : imageArray(dir)) {
Label label = new Label (0,i,"test" + i);
cell = label;
ws.addCell(cell);
copy.write();
i++;
}
copy.close();
}
Is there a way to make my for() loop work, or do I need to go about this a different way?
Thanks!
Okay. Initially I was confused wether you are trying to create a new workbook or edit an existing one. But it looks like you need a new one here.
It appears there were two issues in the code example.
First one is that you retrieved just created excel file and copied its content into itself.
Workbook wb = Workbook.getWorkbook(newXls);
WritableWorkbook copy = Workbook.createWorkbook(newXls, wb);
Another thing that I noticed is that in order to save all the changes it is required to call write on the WritableWorkbook instance only once in the end.
I finished with such code.
private static final String FILE_NAME = "D:/test_out.xls";
private static final String SHEET_NAME = "Test sheet name";
private static final int SHEET_INDEX = 0;
private static final String HEADER = "My header";
public static void main(String[] args) throws Exception {
WritableWorkbook writableWorkbook = Workbook.createWorkbook(new File(FILE_NAME));
WritableSheet writableSheet = writableWorkbook.createSheet(SHEET_NAME, SHEET_INDEX);
int columnIndex = 0;
int rowIndex = 0;
writableSheet.addCell(new Label(columnIndex, rowIndex, HEADER));
for (String value : Arrays.asList("First value", "Second value", "Another value")) {
writableSheet.addCell(new Label (columnIndex, ++rowIndex, value));
}
writableWorkbook.write();
writableWorkbook.close();
}
It created for me an excel file with one sheet. The sheet contains one column with 4 cells: My header, First value, Second value, Another value. Of course, you are free to put there any values you need :)
Related
I am iterating through a list of data which I am sending from the runner file(FunctionVerifier.java).
When I am calling the function writeExcel() in excelHandler.java it is entering only the last data from the list that I am iterating through.
Can someone please let me know the reason and how to fix this
public void writeExcel(String sheetName, int r, int c, String data) throws IOException {
file = new FileInputStream(new File(inFilePath));
wb = new XSSFWorkbook(file);
Sheet sh;
sh = wb.getSheet(sheetName);
Row row = sh.createRow(r);
row.createCell(c).setCellValue(data);
closeExcelInstance();
FileOutputStream outputStream = new FileOutputStream(inFilePath);
wb.write(outputStream);
wb.close();
outputStream.close();
}
public void closeExcelInstance() {
try {
file.close();
} catch (Exception e) {
System.out.println(e);
}
}
FunctionVerifier.java
package Sterling.oms;
import Sterling.oms.Process.CouponValidationProcess;
import Sterling.oms.Utilities.ExcelHandler;
import java.util.ArrayList;
public class FuncVerify {
public static void main(String args[]) throws Exception {
String filePath = System.getProperty("user.dir") + "/src/test/resources/TestData/test.xlsx";
ExcelHandler excelHandler = new ExcelHandler(filePath);
excelHandler.readExcelData("Validation");
CouponValidationProcess couponValidationProcess = new CouponValidationProcess("OMS-T781");
excelHandler.createSheet();
// couponValidationProcess.enterValidationHeaderRowInExcel(filePath);
String sheet = "ValidationData";
if (excelHandler.getRowCountWhenNull(sheet) < 1) {
ArrayList<String> header = new ArrayList<>();
header.add("Test Case");
header.add("Coupon ID");
header.add("Grand Total");
header.add("Manual Grand Total");
for (int i = 0; i < header.size(); i++) {
// excelHandler = new ExcelHandler(filePath);
excelHandler.writeExcel(sheet, 0, i, header.get(i));
// excelHandler.closeExcelInstance();
}
}
}
}
The reason for only storing the last item is that Sheet.createRow as well as Row.createCell are doing exactly what their method names tell. They create a new empty row or cell each time they get called. So every times Row row = sh.createRow(r) gets called, it creates a new empty row at row index r and looses all former created cells in that row.
The correct way to use rows would be first trying to get the row from the sheet. And only if it is not present (null), then create a new row. The same is for cells in rows. First try to get them. And only if not present, then create them.
...
Sheet sh;
sh = wb.getSheet(sheetName);
Row row = sh.getRow(r); if (row == null) row = sh.createRow(r);
Cell cell = row.getCell(c); if (cell == null) cell = row.createCell(c);
cell.setCellValue(data);
...
That's the answer to your current question.
But the whole approach, to open the Excel file to create a Workbook, then set data of only one cell in and then write the whole workbook out to the file, and doing this for each cell, is very sub optimal. Instead the workbook should be opened, then all known new cell values should be set into the sheet and then the workbook should be written out.
Your approach is wrong, you open your files again for each line you want to write to Excel, then save again. You just have to create one FileInputStream and send it to your Workbook where you do all your Excel work. After you have finished writing all your lines, you can create only one FileOutputStream and export your changes in your Workbook to a file of your choice.
writeExcel()
public void writeExcel(String sheetName, int r, int c, ArrayList<String> data) throws IOException {
file = new FileInputStream(new File(inFilePath));
wb = new XSSFWorkbook(file);
Sheet sh;
sh = wb.getSheet(sheetName);
Row row = sh.createRow(r);
//Adding data column wise
for (String h : data) {
row.createCell(c++).setCellValue(h);
}
closeExcelInstance();
FileOutputStream outputStream = new FileOutputStream(inFilePath);
wb.write(outputStream);
wb.close();
outputStream.close();
}
Given this code:
protected Sheet getSheet(String pathName, int sheetNumber) throws IOException {
File file = new File(pathName);
Workbook workbook = WorkbookFactory.create(file);
return workbook.getSheetAt(sheetNumber);
}
public List<String> getRowAsString(String pathName, int rowNumber) throws IOException {
Sheet sheet = getSheet(pathName, 0);
Row row = sheet.getRow(rowNumber);
List<String> cellsString = new ArrayList<>();
for (int i = 0; i < row.getLastCellNum(); i ++) {
cellsString.add(row.getCell(i).getStringCellValue());
}
return cellsString;
}
I can test it with this:
#Before
public void init() throws IOException {
when(workbookMock.getSheetAt(anyInt())).thenReturn(sheetMock);
PowerMockito.mockStatic(WorkbookFactory.class);
when(WorkbookFactory.create((File) any())).thenReturn(workbookMock);
}
#Test
public void should_return_wanted_row_as_string() throws IOException {
Row rowMock = mock(Row.class);
Cell cellMOck1 = mock(Cell.class);
when(cellMOck1.getStringCellValue()).thenReturn("Valor1");
Cell cellMOck2 = mock(Cell.class);
when(cellMOck2.getStringCellValue()).thenReturn("Valor2");
when(rowMock.getCell(0)).thenReturn(cellMOck1);
when(rowMock.getCell(1)).thenReturn(cellMOck2);
when(sheetMock.getRow(anyInt())).thenReturn(rowMock);
when(rowMock.getLastCellNum()).thenReturn((short) 2);
List<String> expected = List.of("Valor1", "Valor2");
List<String> result = sut.getRowAsString("/", 0);
assertEquals(expected, result);
}
But, if I want to use forEach() instead of for loop in getRowAsString() method, like this:
public List<String> getRowAsString(String pathName, int rowNumber) throws IOException {
Sheet sheet = getSheet(pathName, 0);
Row row = sheet.getRow(rowNumber);
List<String> cellsString = new ArrayList<>();
row.forEach(cell -> {
cellsString.add(cell.getStringCellValue());
});
return cellsString;
}
The test doesn't work because when use stream.forEach() the code is not calling method getCell(index).
How should I test the method to be able to use forEach() instead of traditional for loop?
Yes, I can create a real instance of workbook using, for example, XSSFWorkbook class.
I was trying wrong to do something like this:
Workbook workbook = new XSSFWorkbook();
Sheet sheet = new XSSFSheet();
workbook.setSheet(sheet);
No problem with XSSFWorkbook, but XSSFSheet has protected access and, obviously, I can't create an instance of it.
The correct way is:
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow();
Cell cell = row.createCell();
...
I did not know that way of working with sheets and I was trying to inject by the classic way.
I am reading excel file using POI library in my java code. So far fine. But now I have one requirement. The excel file contains many records (e.g. 1000 rows). It also has column headers (1st row). Now I am doing excel filtering on it. Say I have one 'year' column and I am filtering all rows for year=2019. I get 15 rows.
Question: I want to process only these 15 rows in my java code. Is there any method in poi library or way to know if the row being read is filtered or (the other way i.e. not filtered).
Thanks.
I already have working code but right now I am looking for how to read only filtered row. Nothing new tried yet other than searching in library and forums.
The below code is inside a method. I am not used to formatting with stackoverflow so kindly ignore any formatting issue.
// For storing data into CSV files
StringBuffer data = new StringBuffer();
try {
SimpleDateFormat dtFormat = new SimpleDateFormat(CommonConstants.YYYY_MM_DD); // "yyyy-MM-dd"
String doubleQuotes = "\"";
FileOutputStream fos = new FileOutputStream(outputFile);
// Get the workbook object for XLSX file
XSSFWorkbook wBook = new XSSFWorkbook(new FileInputStream(inputFile));
wBook.setMissingCellPolicy(Row.RETURN_BLANK_AS_NULL);
// Get first sheet from the workbook
//XSSFSheet sheet = wBook.getSheetAt(0);
XSSFSheet sheet = wBook.getSheet(CommonConstants.METADATA_WORKSHEET);
//Row row;
//Cell cell;
// Iterate through each rows from first sheet
int rows = sheet.getLastRowNum();
int totalRows = 0;
int colTitelNumber = 0;
Row firstRowRecord = sheet.getRow(1);
for (int cn = 0; cn < firstRowRecord.getLastCellNum(); cn++) {
Cell cellObj = firstRowRecord.getCell(cn);
if(cellObj != null) {
String str = cellObj.toString();
if(CommonConstants.COLUMN_TITEL.equalsIgnoreCase(str)) {
colTitelNumber = cn;
break;
}
}
}
// Start with row Number 1. We don't need 0th number row as it is for Humans to read but not required for processing.
for (int rowNumber = 1; rowNumber <= rows; rowNumber++) {
StringBuffer rowData = new StringBuffer();
boolean skipRow = false;
Row rowRecord = sheet.getRow(rowNumber);
if (rowRecord == null) {
LOG.error("Empty/Null record found");
} else {
for (int cn = 0; cn < rowRecord.getLastCellNum(); cn++) {
Cell cellObj = rowRecord.getCell(cn);
if(cellObj == null) {
if(cn == colTitelNumber) {
skipRow = true;
break; // The first column cell value is empty/null. Which means Titel column cell doesn't have value so don't add this row in csv.
}
rowData.append(CommonConstants.CSV_SEPARTOR);
continue;
}
switch (cellObj.getCellType()) {
case Cell.CELL_TYPE_BOOLEAN:
rowData.append(cellObj.getBooleanCellValue() + CommonConstants.CSV_SEPARTOR);
//LOG.error("Boolean:" + cellObj.getBooleanCellValue());
break;
case Cell.CELL_TYPE_NUMERIC:
if (DateUtil.isCellDateFormatted(cellObj)) {
Date date = cellObj.getDateCellValue();
rowData.append(dtFormat.format(date).toString() + CommonConstants.CSV_SEPARTOR);
//LOG.error("Date:" + cellObj.getDateCellValue());
} else {
rowData.append(cellObj.getNumericCellValue() + CommonConstants.CSV_SEPARTOR);
//LOG.error("Numeric:" + cellObj.getNumericCellValue());
}
break;
case Cell.CELL_TYPE_STRING:
String cellValue = cellObj.getStringCellValue();
// If string contains double quotes then replace it with pair of double quotes.
cellValue = cellValue.replaceAll(doubleQuotes, doubleQuotes + doubleQuotes);
// If string contains comma then surround the string with double quotes.
rowData.append(doubleQuotes + cellValue + doubleQuotes + CommonConstants.CSV_SEPARTOR);
//LOG.error("String:" + cellObj.getStringCellValue());
break;
case Cell.CELL_TYPE_BLANK:
rowData.append("" + CommonConstants.CSV_SEPARTOR);
//LOG.error("Blank:" + cellObj.toString());
break;
default:
rowData.append(cellObj + CommonConstants.CSV_SEPARTOR);
}
}
if(!skipRow) {
rowData.append("\r\n");
data.append(rowData); // Appending one entire row to main data string buffer.
totalRows++;
}
}
}
pTransferObj.put(CommonConstants.TOTAL_ROWS, (totalRows));
fos.write(data.toString().getBytes());
fos.close();
wBook.close();
} catch (Exception ex) {
LOG.error("Exception Caught while generating CSV file", ex);
}
All rows which are not visible in the sheet have a zero height. So if the need is only reading the visible rows, one could check via Row.getZeroHeight.
Example
Sheet:
Code:
import java.io.FileInputStream;
import org.apache.poi.ss.usermodel.*;
class ReadExcelOnlyVisibleRows {
public static void main(String[] args) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("SAMPLE.xlsx"));
DataFormatter dataFormatter = new DataFormatter();
CreationHelper creationHelper = workbook.getCreationHelper();
FormulaEvaluator formulaEvaluator = creationHelper.createFormulaEvaluator();
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
if (!row.getZeroHeight()) { // if row.getZeroHeight() is true then this row is not visible
for (Cell cell : row) {
String cellContent = dataFormatter.formatCellValue(cell, formulaEvaluator);
System.out.print(cellContent + "\t");
}
System.out.println();
}
}
workbook.close();
}
}
Result:
F1 F2 F3 F4
V2 2 2-Mai FALSE
V4 4 4-Mai FALSE
V2 6 6-Mai FALSE
V4 8 8-Mai FALSE
You have to use auto filter provided in Apache Poi library and also you have set the freezing. I provide below the brief code snippet, you can use accordingly.
XSSFSheet sheet = wBook.getSheet(CommonConstants.METADATA_WORKSHEET);
sheet.setAutoFilter(new CellRangeAddress(0, 0, 0, numColumns));
sheet.createFreezePane(0, 1);
I had to override some hooks and come up with my own approach to incorporate filtering of hidden rows in order to prevent processing of those. Below is code snippet. My approach consists of opening a second copy of the same sheet just so that I can query the current row getting processed to see if it's hidden or not. The answer above touches on this, the below expands on it to show how it can be nicely incorporated into the Spring batch excel framework. One drawback is that you have to open a second copy of the same file, but I couldn't figure out a way (perhaps there's none!) to get my hands on the internal Workbook sheet, among other reasons because org.springframework.batch.item.excel.poi.PoiSheet is package private (Note that below syntax is Groovy!!!):
/**
* Produces a reader that knows how to ingest a file in excel format.
*/
private PoiItemReader<String[]> createExcelReader(String filePath) {
File f = new File(filePath)
PoiItemReader<String[]> reader = new PoiItemReader<>()
reader.setRowMapper(new PassThroughRowMapper())
Resource resource = new DefaultResourceLoader().getResource("file:" + f.canonicalPath)
reader.setResource(resource)
reader.setRowSetFactory(new VisibleRowsOnlyRowSetFactory(resource))
reader.open(new ExecutionContext())
reader
}
...
// The "hooks" I overwrote to inject my logic
static class VisibleRowsOnlyRowSet extends DefaultRowSet {
Workbook workbook
Sheet sheet
VisibleRowsOnlyRowSet(final Sheet sheet, final RowSetMetaData metaData) {
super(sheet, metaData)
}
VisibleRowsOnlyRowSet(final Sheet sheet, final RowSetMetaData metaData, Workbook workbook) {
this(sheet, metaData)
this.workbook = workbook
this.sheet = sheet
}
boolean next() {
boolean moreLeft = super.next()
if (moreLeft) {
Row row = workbook.getSheet(sheet.name).getRow(getCurrentRowIndex())
if (row?.getZeroHeight()) {
log.warn("Row $currentRow is hidden in input excel sheet, will omit it from output.")
currentRow.eachWithIndex { _, int i ->
currentRow[i] = ''
}
}
}
moreLeft
}
}
static class VisibleRowsOnlyRowSetFactory extends DefaultRowSetFactory {
Workbook workbook
VisibleRowsOnlyRowSetFactory(Resource resource) {
this.workbook = WorkbookFactory.create(resource.inputStream)
}
RowSet create(Sheet sheet) {
new VisibleRowsOnlyRowSet(sheet, super.create(sheet).metaData, workbook)
}
}
I'm new to Java and Selenium, Selenium is quite fun, I'm working on Selenium WebDriver with TestNG Data Driven framework.
by refer to this tutorial
http://software-testing-tutorials-automation.blogspot.sg/2014/07/framework-for-selenium-webdriver-report.html#comment-form
there is a Excel Utility where write data to excel with using HSSF
SuiteUtility.WriteResultUtility(FilePath, TestCaseName, "Pass/Fail/Skip", DataSet+1, "PASS");
Instead of hardcode, I'm planning to using Constant file to replace it. eg,
public static final String KEYWORD_PASS = "PASS";
public static final int COL_TEST_CASE_RESULT = 10; // put it in column no 10
so it will be like these and become more manageable
SuiteUtility.WriteResultUtility(FilePath_TestResult, TestCaseName, Constant.COL_TEST_CASE_RESULT, DataSet+1, Constant.KEYWORD_PASS);
My question is how to modify the code below so I can change the String colName to int column number.
public boolean writeResult(String wsName, String colName, int rowNumber, String Result){
try{
int sheetIndex=wb.getSheetIndex(wsName);
if(sheetIndex==-1)
return false;
int colNum = retrieveNoOfCols(wsName);
int colNumber=-1;
HSSFRow Suiterow = ws.getRow(0);
for(int i=0; i<colNum; i++){
if(Suiterow.getCell(i).getStringCellValue().equals(colName.trim())){
colNumber=i;
}
}
if(colNumber==-1){
return false;
}
HSSFRow Row = ws.getRow(rowNumber);
HSSFCell cell = Row.getCell(colNumber);
if (cell == null)
cell = Row.createCell(colNumber);
cell.setCellValue(Result);
opstr = new FileOutputStream(filelocation);
wb.write(opstr);
opstr.close();
}catch(Exception e){
e.printStackTrace();
return false;
}
return true;
}
public void setData(String adr,String sht,int rn,int cn,String val) throws Exception{
FileInputStream fsIP= new FileInputStream(new File(adr)); //Read the spreadsheet that needs to be updated
XSSFWorkbook wb = new XSSFWorkbook(fsIP); //Access the workbook
XSSFSheet worksheet = wb.getSheet(sht); //Access the worksheet, so that we can update / modify it.
Cell cell = null; // declare a Cell object
cell = worksheet.getRow(rn).getCell(cn); // Access the second cell in second row to update the value
cell.setCellValue(val); // Get current cell value value and overwrite the value
fsIP.close(); //Close the InputStream
FileOutputStream output_file =new FileOutputStream(new File(adr)); //Open FileOutputStream to write updates
wb.write(output_file); //write changes
output_file.close(); //close the stream
}
It might be helped.
I m not able to edit the existing excel sheet using jxl.
It always creates a new one.
Can anyone please help me out with it.
Please give a small sample code.
jxl is designed for increased read efficiency (since this is the primary use of the API). In order to improve performance, data which relates to output information (eg. all the formatting information such as fonts) is not interpreted when the spreadsheet is read, since this is superfluous when interrogating the raw data values.
However, if we need to modify this spreadsheet a handle to the various write interfaces is needed, which can be obtained using the copy method.
Workbook workbook = Workbook.getWorkbook(new File("myfile.xls"));
WritableWorkbook copy = Workbook.createWorkbook(new File("temp.xls"), workbook);
This copies the information that has already been read in as well as performing the additional processing to interpret the fields that are necessary to for writing spreadsheets. The disadvantage of this read-optimized strategy is that we have two spreadsheets held in memory rather than just one, thus doubling the memory requirements.
But after this, you can do whatever you want. Like:
WritableSheet sheet2 = copy.getSheet(1);
WritableCell cell = sheet2.getWritableCell(1, 2);
if (cell.getType() == CellType.LABEL)
{
Label l = (Label) cell;
l.setString("modified cell");
}
copy.write();
copy.close();
workbook.close();
Note: this is directly taken from Andy Khan's tutorial page.
I know that this is quite an old question, but if anyone will encounter the same problem, then to preserve the correct formatting (font type, colouring, etc. )
you should save the cell format before casting it to Label, and then force the cell to the previous formatting.
Code:
CellFormat cfm = cell.getCellFormat();
Label l = (Label) cell;
l.setString("modified cell");
cell.setCellFormat(cfm);
//there is god example of it, you can copy in ur project and check it out, to
//understand how it works
Workbook wk = Workbook.getWorkbook(new File("ex.xls"));
//
WritableWorkbook wkr = Workbook.createWorkbook(new File("modifed.xls"), wk);
/* second line makes copy of wk excel file object /creates a readable spreadsheet.
both are now similar and i can Modify exiting wkr spreadsheets */
//next 2 line retrieve sheet number 0 and cell (1,1)
WritableSheet getsht = wkr.getSheet(0);
WritableCell getcl = getsht.getWritableCell(1, 1);
//making own font
WritableFont ft = new WritableFont(WritableFont.ARIAL, 20 , WritableFont.BOLD, true , UnderlineStyle.SINGLE);
//making Format, which uses font
WritableCellFormat form = new WritableCellFormat( ft);
Number nb = ( Number ) getcl ;
nb.setCellFormat( form );
wkr.write();
wkr.close();
I personally use this code to append the xls file and create one if it doesn't exist.
Using jxl 2.6:
public class Excel {
private String fileName = "excel_file.xls";
private String sheetName = "sheet1";
private WritableWorkbook writableWorkbook;
private int rowCount;
private Workbook wb;
// assigns checks if file exists or not, both cases we assign it to a WritableWorkbook // object so that we can write to it.
private void assignWorkBook() throws IOException, BiffException {
// File f = new File(System.getProperty("user.dir") +"\\"+fileName);
File inp = new File(fileName);
try{
wb = Workbook.getWorkbook(inp);
writableWorkbook = Workbook.createWorkbook(inp, wb);
} catch (FileNotFoundException e){
writableWorkbook = Workbook.createWorkbook(inp); //Create a new one
}
}
public int getRowCount() {
return rowCount;
}
// this function writes a vector to an excel file, checks if there is already a sheet
// with that name or not, and uses it. then we have to close the Workbook object before
// we could write to the file, and then we save the file.
// That is, the file is always saved after writing to it.
public void writeRow(Vector<String> playerVector) throws WriteException, IOException, BiffException {
assignWorkBook();
WritableSheet excelSheet;
if(writableWorkbook.getNumberOfSheets() == 0) {
excelSheet = writableWorkbook.createSheet(sheetName, 0);
}
else {
excelSheet = writableWorkbook.getSheet(sheetName);
}
rowCount = excelSheet.getRows();
int colCount = 0;
for(String playerStat:playerVector) {
Label label = new Label(colCount++, rowCount, playerStat);
excelSheet.addCell(label);
}
if(wb != null) {
wb.close();
}
writableWorkbook.write();
writableWorkbook.close(); //everytime save it.
}
}