Please read full before marking as duplicate. I have seen all the related/similar questions already and I guess I have something weird happening here.
I'm using Apache POI version 3.9 to generate a 4 sheet workbook. Every time I'm done filling the sheet, I auto-size the columns. Here's the code:
for (String alias : map.keySet()) {
Sheet sheet = workbook.createSheet(getSheetNameForAlias(alias));
List<Object[]> resultList = (List<Object[]>) map.get(alias);
Collections.sort(resultList.subList(1, resultList.size()), getComparator(alias));
int rownum = 0;
for (Object[] array : resultList) {
rownum = populateRow(cellStyles, sheet, rownum, array, false);
}
for(int j=0; j< resultList.get(0).length; j++){
sheet.autoSizeColumn(j, false);
}
}
the populateRow function is:
private int populateRow(CellStyle[] cellStyles, Sheet sheet, int rownum, Object[] dataArr, boolean doAutoSizing) {
if (logger.isDebugEnabled()) {
logger.debug("Populating row ... #{} : {} ", sheet.getSheetName(), rownum);
}
int cellnum;
Row row = sheet.createRow(rownum++);
cellnum = 0;
if (ArrayUtils.isEmpty(dataArr)) {
logger.error("No data found for row ... #{} : {} ", sheet.getSheetName(), rownum);
return rownum;
}
for (Object obj : dataArr) {
Cell cell = row.createCell(cellnum++);
// get the type of the data inserted in the cell.
// Accordingly set the cell value
String data = String.valueOf(obj);
if (StringUtils.isBlank(data) || "null".equals(data)) {
cell.setCellValue(StringUtils.EMPTY);
} else {
if (NumberUtils.isNumber(data)) {
Double dbVal = NumberUtils.createDouble(data);
// Millions separator for Numeric fields
if (dbVal == Math.ceil(dbVal)) {
cell.setCellStyle(cellStyles[0]);
} else {
cell.setCellStyle(cellStyles[1]);
}
cell.setCellType(Cell.CELL_TYPE_NUMERIC); // for numeric , set cell type as numeric
cell.setCellValue(dbVal);
} else {
if(obj instanceof java.sql.Timestamp || obj instanceof java.sql.Date){
cell.setCellStyle(cellStyles[2]);
}
cell.setCellValue(data);
}
if (doAutoSizing) {
sheet.autoSizeColumn(cellnum - 1);
}
data = null;
}
obj = null;
}
return rownum;
}
The weird part is, columns of one out of the four sheets are getting resized incorrectly.
I tried re-writing the auto size logic with different approaches in my mind and discovered that the sheet, for which I'm getting this incorrect behaviour, if I try sheet.getRow(0), it gives out null which specifies that there is no data for that row but the excel being generated does not hold good with that, all four sheets have appropriate data. And in every case, the first row is always the headings so It couldn't be null even if there is no data beneath it.
I don't know what I'm missing but I tried and found that it is not a Font issue with the JVM.
Please let me know in case you need some other info.
EDIT: Observed that auto-sizing is working incorrectly for the sheet which has more than 100 rows. I don't know how to relate it to some logic. This is real WEIRD.
EDIT 2: I found the official reference for the reason given by Axel while looking for something else. This does suffice my confusion. Read the para on the following
link
Since you say it works up to 100 rows, it looks like you are using SXSSFWorkbook, not XSSFWorkbook. Then that behaviour has to be expected, since only a fixed number of rows are kept in memory, and only these rows will be considered. So, if I am right, you will have to switch to using XSSFWorkbook (provided this doesn't lead to OOMs when dealing with a large number of rows).
Related
I've to create a java swing app that allows the user to menage some Oracle tables.
I've implemented the JTable in this way:
public class TableModel extends AbstractTableModel{
.
.
.
public List <List <String> > result = null;
.
.
.
public void createTable(ResultSet r) throws SQLException {
rs = r;
numcols = getColumnCount();
result = new ArrayList();
while (rs.next()) {
List <String> row = new ArrayList<>(numcols);
for (int i=1; i<= numcols; i++)
row.add(rs.getString(i).replace("00:00:00.0", "")); //just managing stuffs .. nothing important. Don't care of this replace.
result.add(row);
}
fireTableStructureChanged();
}
public void addRow(){
List <String> row = new ArrayList<>();
for(int i=1;i<=getColumnCount();i++)
row.add("");
result.add(row);
fireTableStructureChanged();
}
public Object getValueAt(int row, int col) {
return result.get(row).get(col);
}
public int getColumnCount() {
if (rs == null) {
return 0;
}
try {
return rs.getMetaData().getColumnCount();
} catch (SQLException e) {
System.err.println("error in colcount");
return 0;
}
}
public int getRowCount() {
int totalRows = 0;
try {
rs.last();
totalRows = rs.getRow();
rs.beforeFirst();
} catch(Exception ex) {
return 0;
}
return totalRows ;
}
}
so, I store database data into the arraylist and then I print the table using the arraylist.
Now in the addRow() function I add to that arraylist an empty record (in my mind an empty record is a record composed of all null ("") strings).
Finally using the fireTableStructureChanged() method I expect the table to refresh itself.. but this will never occur and I don't understand why.
I've already checked that the new row is successfully loaded into the arraylist:
I got all the data stored in the table of the Database into the arraylist and the new row is successfully loaded in the arraylist.
so I swear the problem is about that fireTableStructureChanged() method.
Thanks :3
Try using fireTableRowsInserted(int firstRow, int lastRow) instead of fireTableStructureChanged()
Here's the javadoc for the structure changed ...
Notifies all listeners that the table's structure has changed. The number of columns in the table, and the names and types of the new columns may be different from the previous state. If the JTable receives this event and its autoCreateColumnsFromModel flag is set it discards any table columns that it had and reallocates default columns in the order they appear in the model. This is the same as calling setModel(TableModel) on the JTable.
So that one will only work if the columns types changed ...
If you are lazy go for this one: fireTableDataChanged()
I found the solution.
That's quite embarassing..
#tsolakp you helped me a lot!
Since before using ArrayList I was working on ResultSet directly, I forgot to update getrowcount.
So, since the inserting was only "client side" on the arraylist, the rows displayed were only "n" (where n was the number of rows on the database). So I would never displayed my new row since it was not loaded into the database.
That's all :)
Maybe working hard on coding, makes these errors invisible :X
Let's say I have a sqlite table named "table" with the rows id, text1, text2
now I have an ArrayList with the content, let's say:
id:0,text1:this is the first text,text2: this is the second text
id:21, text1: blabla, text2: blabla2
...
now i want to make a comparision between sqlite row and every single element row in arraylist.
for example i want to say delete every row in sqlite table which is not equal with any row in arraylist.
I could not find any equal thread that contains my question.. maybe you can help me or give me some link. thank u
Here you have some example code. You should get the idea:
public void removeRows(DatabaseAdapter dbAdapter, ArrayList<SomeObject> list) {
SQLiteDatabase db = dbAdapter.openWritableDatabase();
Cursor rows = db.query(/ *perform some query on SQLite db*/);
while(rows.moveToNext()) {
int id = rows.getInt(0);
String text1 = rows.getString(1);
String text2 = rows.getString(2);
boolean included = false;
for(SomeObject obj : list) {
if(obj.getId() == id && obj.getText1().equals(text1) && obj.getText2().equals(text2)) {
included = true;
break;
}
}
if(!included) {
db.delete(/*perform deletion of row*/);
}
}
}
Edit: To add to database those objects from list which are not yet in it do something like this:
public void addListToDb(DatabaseAdapter dbAdapter, ArrayList<SomeObject> list) {
SQLiteDatabase db = dbAdapter.openWritableDatabase();
Cursor rows = db.query(/ *perform some query on SQLite db*/);
for(SomeObject obj : list) {
boolean included = false;
while(rows.moveToNext()) {
int id = rows.getInt(0);
String text1 = rows.getString(1);
String text2 = rows.getString(2);
if(obj.getId() == id && obj.getText1().equals(text1) && obj.getText2().equals(text2)) {
included = true;
break;
}
}
rows.moveToPosition(-1);
if(!included) {
db.insert(/*perform insert of object*/);
}
}
}
There is also another way to accomplish this task. Loop over elements of your list and add every element to database using SQLiteDatabase#insertWithOnConflict(String table, String nullColumnHack, ContentValues initialValues, int conflictAlgorithm) method and pass SQLiteDatabase.CONFLICT_IGNORE as a conflictAlgorithm. Above method will add element if it not yet exists in database. Otherwise it will ignore it. Important thing to remember is that when we try to insert a row which already exists in database with CONFLICT_IGNORE conflict algorithm we will get -1 result instead of primary key of existing row (Android documentation is misleading here).
I have used a tutorial to see how to implement JTable but I don't know how exactly access the data of each cell to extract the data that the user put in these.
The table has 2 column and N rows
In the first column there is a String in the second there is a int
The tutorial that I have used is this
Every JTable has a data-model connected with it. Users may add data to this data model (e.g. by calling to the javax.swing.table.TableModel.setValueAt(Object, int, int) method) and JTable then displays them. In order to process the data from the JTable one can use the following approach:
JTable t = new JTable(/* set some table-model that will contain the data */);
...
/* get some table-model that will contain the data */
TableModel tm = t.getModel();
for (int i = 0; i < tm.getRowCount(); i++) {
for (int j = 0; j < tm.getColumnCount(); j++) {
Object o = tm.getValueAt(i, j);
if (o instanceof Integer) {
System.out.println((Integer)o);
} else if (o instanceof String) {
System.out.println((String)o);
}
}
}
You have to see the paragraph "Listening for Data Changes"
In general, you have to get the model associated with the table and call it "getValueAt" method. It returns the Object associated with the cell, so you have to cast it to String or Integer.
For example if you want to get the value of the second column and third row, the code is:
(Integer) model.getValueAt(2,1)
getRowCount() gets the number of rows that are set so i can't use it.
This is what I did and it doesn't work:
private String[][] getContentsFromTable(){
int cols= jTable1.getColumnCount();
int rows=jTable1.getRowCount();
System.out.print(rows);
String items[][]= new String[rows][cols];
if(jTable1.getCellEditor()!=null)
jTable1.getCellEditor().stopCellEditing();
for(int i=0;i< jTable1.getRowCount();i++){
for (int j=0;j<jTable1.getColumnCount();j++){
//skip subtotal
if(j!=3){
if(jTable1.getValueAt(i,j)!=null){
String om=jTable1.getValueAt(i,j).toString();
if(om.trim().length()!=0){
items[i][j]=om;
//System.out.print(""+i+",j "+j+": "+items[i][j]);
}
}
}
}
}
return items;
}
edit: the table is editable and i need to get all the data from rows so i have to go over it and identify the number of not null rows so i can use them in a loop and store the data in the database
Kind of too late but for anyone who is having problems with NULL rows, .getValueAt is throwing a null exception so do not try to use it for checking null rows.
I have a JTable and when I use jTable1.getModel()).getDataVector() objects from different columns have different types (they should be all Strings for my case)
My table has five columns:
number (it's String actually, but I have no problem with parsing it)
String chosen from combobox attached to cell, but when I want to take value from it it appears to be Vector, not a String
again number (like in 0 column - no problem here)
again String with combobox, but gives me String value, so no problem
number (hidden in String) but again this should be String but is Vector
how I create comboboxes in cells (column 1 and 3):
TableColumn column = jTable1.getColumnModel().getColumn(3);
JComboBox cb = new JComboBox(ServerIntf.DataModificationType.getStringList().toArray());
column.setCellEditor(new DefaultCellEditor(cb));
how I create table model:
public TableModel getTableModel() {
Vector<Vector<String>> data = task.getDataSet(dataName);
ServerIntf.SimpleDataType sdt = Manager.manager.getSimpleDataType(task, dataName);
rowsMin = sdt.getRowMin();
rowsMax = sdt.getRowMax();
columnsMin = sdt.getColMin();
columnsMax = sdt.getColMax();
if (data != null) {
//nothing important here, it doeasn't come into this part anyway
}
int cmax = 5;
int rmax = 1;
Vector columns = new Vector(cmax);
columns.addAll(Arrays.asList(new String[]{"ID", "PrzeksztaĆcenie", "Ile razy", "Co z danymi", "Co dalej"}));
return new DefaultTableModel(columns, rmax);
}
And this is how I read data:
public AlgorythmTable(List get) {
steps = new Vector<Step>();
for (List<String> row : (List<List<String>>) get) {
steps.add(new Step(row));
}
//boring here
}
//...
public Step(List list) {
id = Integer.parseInt((String) list.get(0));
matrix = ((String) ((Vector) list.get(1)).get(0)).isEmpty() ? null : ((String) ((Vector) list.get(1)).get(0));
count = ((String) list.get(2))==null || ((String) list.get(2)).isEmpty() ? 0 : Integer.parseInt((String) list.get(2));
after = ((String) list.get(3)).isEmpty() ? null : ServerIntf.DataModificationType.getByName((String) list.get(3));
nextStep = ((String) list.get(4))==null || ((String) list.get(4)).isEmpty() ? -1 : Integer.parseInt(((String) list.get(1)));
}
I get CastException at the last line (nextStep = ...)
Exception occurred during event dispatching:
java.lang.ClassCastException: java.util.Vector cannot be cast to java.lang.String
I was getting the same exception at the line (matrix = ...) but as you can see I casted it differently and now it seems to work fine.
I thought at first that the problem was connected to the comboboxes in cells, but it also occurs in column where there is simple String inserted by the user.
Well I can simply do another casting but it bothers me, because it should work without it (didn't have that problem in different kinds of JTables), this makes code hard to read and if will ever want to expand this table with more columns (which might happen in near future) and I will struggle again with the same problem.
Does anyone have any idea, why does it work like that and if there's any way of forcing jtable (or model) to give me values of cells in unified format?
Instead of coercing your List<List<String>> into a DefaultTableModel, it may be more maintainable to extend AbstractTableModel, as suggested in Creating a Table Model. In particular, you override getColumnClass() to specify the precise type of each datum. This has the direct benefit of enabling several default Editors and Renderers. There's a related example here.