Apache POI Java - Write to excel and dynamically update cells - java

My java spring boot app needs to create a new excel file based on the contents of my DB. My current solution places all the data from my DB and inserts it in my excel sheet, but I want to improve it by not stating what the cell values are. For example, although it works, my solution has 34 fields so I am stating the userRow.createCell line 34 times for each field which is repetitive. Ideally I want to say create the cell(n) and take all the values from each row in the DB. How can this be done? Another for loop within this for loop? Every example I looked at online seems to specifically state what the cell value is.
List<CaseData> cases = (List<CaseData>) model.get("cases");
Sheet sheet = workbook.createSheet("PIE Cases");
int rowCount = 1;
for (CaseData pieCase : cases) {
Row userRow = sheet.createRow(rowCount++);
userRow.createCell(0).setCellValue(pieCase.getCaseId());
userRow.createCell(1).setCellValue(pieCase.getAcknowledgementReceivedDate());
}

Use the Reflection API
Example:
try {
Class caseDataObj = CaseData.class;
Method [] methods = caseDataObj.getDeclaredMethods();
Sheet sheet = workbook.createSheet("PIE Cases");
int rowCount = 1;
for(CaseData cd : cases) {
int cellIndex = 0;
Row userRow = sheet.createRow(rowCount++);
for (Method method : methods) {
String methodName = method.getName();
if(methodName.startsWith("get")) {
// Assuming all getters return String
userRow.createCell(cellIndex++).setCellValue((String) method.invoke(cd));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}

There are probably many ways to do this, You can try something like this, this is how I usually go about it for things like what you are doing.
public enum DATA {
CASE_ID(0),
ACK_RECIEVED(1),
ETC(2);
//ETC(3) and so on
public int index;
DATA(int index) {
this.index = index;
}
public Object parse(CaseData data) throws Exception {
switch (this) {
case CASE_ID:
return data.getCaseId();
case ACK_RECIEVED:
return data.getAcknowledgementReceivedDate();
case ETC:
return "etc...";
default: return null;
}
}
}
Then, the implementation is:
List<CaseData> cases = (List<CaseData>) model.get("cases");
Sheet sheet = workbook.createSheet("PIE Cases");
int rowCount = 1;
for (CaseData pieCase : cases) {
Row userRow = sheet.createRow(rowCount++);
for (DATA DAT : DATA.values()) {
userRow.createCell(DAT.index).setCellValue(DAT.parse(pieCase));
}
}

Related

How to find data based on multiple column values in excel using apache POI

I am using the apache poi for reading and writing values to excel file with java. I have below excel file, from that file i need to fetch data which comes under my required criteria.
Application
CaseID
FeeType
Comments
AppA
1234
Security
Add Comments
AppB
1235
Other
Case created
AppA
1236
Security
Added Comments
In my code I want to get all the cases which comes under below criteria,
Application set to "AppA",Fee Type set to "Security" and Comments with "Add Comments".
I used below code to filter using two columns for search criteria, but i cannot add the third criteria. Please help me.
ArrayList<Object> ApplicationCases = new ArrayList<Object>();
for (Row row : sheetName) {
for (Cell cell : row) {
if (cell.getCellType() == CellType.STRING) {
if (cell.getRichStringCellValue().getString().trim().equals(requiredCellContent1)) {
int rowNumber = row.getRowNum();
XSSFRow row1 = sheetName.getRow(rowNumber);
XSSFCell selectedCellValue = null;
short cellcount = row1.getLastCellNum();
for (int i = 0; i < cellcount; i++) {
if (row1.getCell(i).getRichStringCellValue().getString().trim()
.equals(requiredCellContent2)) {
selectedCellValue = row1.getCell(1);
ApplicationCases.add(selectedCellValue);
} else if (selectedCellValue == null || row1.getCell(i).getCellType() == CellType.BLANK) {
}
}
}
}
Can you so something like below
Iterator<Row> rows = sheet.rowIterator();
while(rows.hasNext()) {
Row row = rows.next();
String app = row.getCell(0).getRichStringCellValue().getString();
String feeType = row.getCell(2).getRichStringCellValue().getString();
String comment = row.getCell(3).getRichStringCellValue().getString();
if(app.equals("AppA") && feeType.equals("Security") && comment.equals("Add Comments")) {
//Here is your condition satisfied
}
}

NegativeArraySizeException, DataProvider, Excel

I'd like to implement the data from Excel file to different tests depending on the scenario correct data/invalid one, however when I want to get the cell value I get the "NegativeArraySizeException".
The first row has just a title so I don't want to read it, that's why I have the parameters [rows-1].
Could you please indicate what is my mistake?
Thank you
public class SignInTest extends Driver {
#BeforeMethod
public void setUp() {
Driver.initConfiguration();
}
public Object[][] getData(String excelPath, String sheetName) {
int rows = excel.getRowCount(sheetName);
int cols = excel.getColumnCount(sheetName);
Object[][] data = new Object[rows - 1][1];
excel = new ExcelReader(excelPath);
for (int rowNum = 1; rowNum < rows; rowNum++) {
for (int colNum = 0; colNum < cols; colNum++) {
data[rowNum - 1][colNum] = excel.getCellData(sheetName, colNum, rowNum);
}
}
return data;
}
#DataProvider(name = "credentials")
public Object[][] getCredentials() {
Object[][] data = getData(excelPath, sheetName);
return data;
}
#Test(dataProviderClass = DataProviders.class, dataProvider = "credentials")
public void loginWithCorrectCredentials(String email, String password) {
HomePageActions hp = new HomePageActions();
SignInActions sign = new SignInActions();
DataProviders dp = new DataProviders();
dp.getData(excelPath, "correctData");
System.out.println("email " + email);
System.out.println("password " + password);
}
This function "excel.getRowCount(sheetName)"
On this line:
int rows = excel.getRowCount(sheetName);
Is returning 0 (or possibly null), thus when you do rows-1, you get a number less than zero. I should hope that much is obvious. So the question becomes WHY?
Things to look for in troubleshooting:
Is the getColumnCount also returning zero? If so, this points to a possible
error in the worksheet reference.
Is the sheetName actually correctly being passed into the function?
Can you insert an explicit value into a specific place on the worksheet? Meaning is that reference working? Throw in a test line and see what happens.
What happens if you hard set the array to say:
Object[][] data = new Object[100][1];
My gut is telling me you have an issue with the reference to the worksheet, but without knowing more about your worksheet referencing, it's impossible to know for sure.
I hope some of this points you in the right direction and gets you going. Good luck!

Unable to append lists in a list using java

Using a list of strings, I am trying to match the string in a excel sheet and add the cell elements in the inner list. Adding the inner list in the outer list using a loop. Please refer the code below
public static List<ArrayList<String>> getKeywords(List<String> testCaseIdList, String fileName, String sheetName){
try {
ArrayList<String> listTestSteps = new ArrayList<String>();
List<ArrayList<String>> listTestCases = new ArrayList<ArrayList<String>>(0);
Sheet sheetKW = ReadExcelFile.readExcel(ST_KEYWORDS);
String columnValue = null;
int matchFlag, addListFlag = 0;
for(String testCaseId : testCaseIdList) {
Iterator<Row> rowIterator = sheetKW.rowIterator();
listTestSteps.clear();
while(rowIterator.hasNext()) {
Row rowNext = (Row) rowIterator.next();
Iterator<Cell> cellIterator = rowNext.cellIterator();
matchFlag = 0;
addListFlag = 0;
//listTestSteps.clear();
while(cellIterator.hasNext()) {
Cell nextCell = cellIterator.next();
columnValue = nextCell.getStringCellValue();
//System.out.println("Column value " +columnValue);
if((columnValue.equalsIgnoreCase(testCaseId)) && (columnValue != "" )) {
matchFlag = 1;
}
if(matchFlag == 1 && columnValue != "") {
listTestSteps.add(columnValue);
addListFlag = 1;
System.out.println("Add Value : "+columnValue);
}
}
if((listTestSteps.isEmpty() == false) && (addListFlag == 1)) {
System.out.println("Adding to the Main list");
listTestCases.add(listTestSteps);
//listTestCases.forEach(System.out::println);
}
}
}
//listTestSteps.forEach(System.out::println);
// Return ArrayList of ArrayLists
return listTestCases;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
I am getting the output as
[TC_0003, login, createUser, deleteUser]
[TC_0003, login, createUser, deleteUser]
Firstly added list listTestSteps is getting replaced by the last iteration list.
Expected output is
[[TC_0002, login, createUser, deleteUser, newUser], [TC_0003, login, createUser, deleteUser]]
Something is wrong for sure. Any help will be appreciated.
Using the
listTestSteps.clear();
instruction in the loop lets you use always the same list, so in every iteration you just empty and refill the same list and add it to the outer list. For this reason the outer list will at the end contain x entries pointing always at the same list, which is filled with the data you put there in last iteration.
So you just have to do
ArrayList<String> listTestSteps = new ArrayList<String>();
instead of clearing the list
The problem is you add the reference of listTestSteps to listTestCases and then in the next loop you clear the listTestSteps, but the cleared list is still referenced in listTestCases. So would suggest using the answer to Add an object to an ArrayList and modify it later to ensure that both lists are resolved properly.

how to efficiently write filter with timestamp in row for scanner

I have a hbase table where all keys have the following structure ID,DATE,OTHER_DETAILS
For example:
10,2012-05-01,"some details"
10,2012-05-02,"some details"
10,2012-05-03,"some details"
10,2012-05-04,"some details"
...
How can I write a scan that get all the rows that older than some date?
For example 2012-05-01 and 2012-05-02 are older than 2012-05-03.
Scan scan = new Scan();
Filter f = ???
scan.setFilter(f);
scan.setCaching(1000);
ResultScanner rs = table.getScanner(scan);
You can create your own Filter and implement the method filterRowKey. To make scan more faster you can also implement the method getNextKeyHint, but this is a bit complicated. The disadvantage of this approach is that you need to put jar file with your filter into the HBase classpath and restart cluster.
This approximate implementation of this filter.
#Override
public void reset() {
this.filterOutRow = false;
}
#Override
public Filter.ReturnCode filterKeyValue(KeyValue v) {
if(this.filterOutRow) {
return ReturnCode.SEEK_NEXT_USING_HINT;
}
return Filter.ReturnCode.INCLUDE;
}
#Override
public boolean filterRowKey(byte[] data, int offset, int length) {
if(startDate < getDate(data) && endDate > getDate(data)) {
this.filterOutRow = true;
}
return this.filterOutRow;
}
#Override
public KeyValue getNextKeyHint(KeyValue currentKV) {
if(getDate(currentKV) < startDate){
String nextKey = getId(currentKV)+","+startDate.getTime();
return KeyValue.createFirstOnRow(Bytes.toBytes(nextKey));
}
if(getDate(currentKV) > endDate){
String nextKey = (getId(currentKV)+1)+","+startDate.getTime();
return KeyValue.createFirstOnRow(Bytes.toBytes(nextKey));
}
return null;
}
#Override
public boolean filterRow() {
return this.filterOutRow;
}
store the key of the very first row somewhere. it will always be there in your final resultset, being the 'first' row, which makes it older than all other rows(am i correct??)
now take the date, which you want to use to filter out the results and create a RowFilter with RegexStringComparator using this date. this will give the row matching the specified criteria. now, using this row and the first row, which you had store earlier, do a range query.
and if you have multiple rows having the same date, say:
10,2012-05-04,"some details"
10,2012-05-04,"some new details"
take the last row, which you would have got after the RowFilter, and use the same technique.
HTH
i was trying to say that you can use range query to achieve this. where the "startrowkey" will be the first row of your table. being the first row it'll always be the oldest row which means you will always have this row in your result. and the "stoprowkey" for your range query will be the row which contains the given date. to find the stoprowkey you can set a "RowFilter" with "RegexStringComparator".
byte[] startRowKey = FIRST_ROW_OF_THE_TABLE;
Scan scan = new Scan();
Filter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL,new RegexStringComparator("YOUR_REGEX"));
scan.setFilter(filter);
ResultScanner scanner1 = table.getScanner(scan);
for (Result res : scanner1) {
byte[] stopRowKey = res.getRow();
}
scanner1.close();
scan.setStartRow(startRowKey);
scan.setStopRow(stopRowKey);
ResultScanner scanner2 = table.getScanner(scan);
for (Result res : scanner2) {
//you final result
}

Google Spreadsheet API - find the first empty cell in a column?

Is there a good way to get the first empty cell in a column from Google's spreadsheet service via Java?
I know I can use:
public CellFeed CheckColumn(int row, int col)
throws IOException, ServiceException {
CellQuery query = new CellQuery(cellFeedUrl);
query.setMinimumRow(row);
query.setMaximumRow(row);
query.setMinimumCol(col);
query.setMaximumCol(col);
query.setReturnEmpty(true);
CellFeed feed = service.query(query, CellFeed.class);
int cell_loc[];
for (CellEntry entry : feed.getEntries()) {
cell_loc=CheckIfEmpty(entry);
}
return cell_loc;
}
And walk through the entries, but I'd rather not load the entire column at once, it's slow for my users and it seems bad to just walkthrough the entire column
Any thoughts?
This small snippet will create a function in Google Spreadsheet with Google Apps Script:
function emptySpace(array) {
// set counter
var counter = 0;
// itterate through values
for (i in array){
if (array[i].length > 1) {
throw ("Only single column of data");
} else {
if(array[i][0] != "") {
counter++;
} else {
break;
}
}
}
// return value + 1
return counter + 1;
}
Add this script, via the script editor, to your spreadsheet and the function emptySpace is available throughout the worksheet, like so: =emptySpace(A1:A7).
See example file I've created: empty space

Categories

Resources