AutosizeColumns on SXSSFWorkbook - java

Is it possible to autoSizeColumns on a streaming SXSSFWorkbook?
I implemented an export functionality to export a list of objects to excel. At first I used the XSSFWorkbook (not streaming) and after all the cells were created, I autosized all the columns, resulting in a nice excel file.
For performance issues we wanted to change the workbook to the streaming version, but this resulted in a NullPointer at org.apache.poi.ss.util.SheetUtil.getCellWidth.
Is it possible to call autoSizeColumns for a SXSSFWorkbook?
Im using poi-ooxml 3.9, but I have the same issue in 3.8.

You need to make sure every cell has a value.
We use the following code to set a string value to a cell:
Cell c = row.createCell(i);
c.setCellValue(text == null ? "" : text );
** Cell should never be null values else it throws NullPointerException. Hence set the value as shown above.
Thanks a lot, this helped!!

Use sheet.isColumnTrackedForAutoSizing(0);
for first and subsequently used for other column, i have faced exception whenever code executed autoSizeColumn(0) get executed. by using above code i have resolved the issue and it's good to expand the column width too based on the text.

sheet.trackAllColumnsForAutoSizing();

sheet.trackAllColumnsForAutoSizing(), this works but it is truly not a good approach. Because if you are working with lakhs of data, it delays the process of writing to file by huge margin

Error: NullPointerException on org.apache.poi.ss.util.SheetUtil.getCellWidth(SheetUtil.java:122)
Fix: Always set value to Cell as shown below, it throws NullPointerException when there is null in Cell, so set the value as:
Cell c = row.createCell(i);
c.setCellValue(text == null ? "" : text );

Related

Apache Poi cell not returning the correct value

I have a excel file with a cell that generates the number 3.69 (based on calculations from proceeding numbers)
However when pulling that number in java using
if (brightCell.getNumericCellValue()) > 0 )
{
double brightness = brightCell.getNumericCellValue();
return brightness;
}
I've also tried:
if (Double.parseDouble(brightCell.getStringCellValue()) > 0 )
{
double brightness = Double.parseDouble(brightCell.getStringCellValue());
return brightness;
}
brightCell is instantiated with :
brightCell = spreadsheet.getRow(new CellReference(brightString).getRow()).getCell(new CellReference(brightString).getCol());
brightString is String brightString = "BV29"
But with both solutions, brightness receives the value, 3.2133....
So thanks to #Igor I managed to figure it out but it led to more issues.
So the solution was creating an evaluator
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
evaluator.setIgnoreMissingWorkbooks(true); //if you need it
when you finish setting the required cells and want to evaluate.
evaluator.EvaluateAll();
The problem for me is I'm doing this multiple times and my 1st resut is correct but upon the second iteration it becomes skewed, and more skewed.
What I'm doing is setting various cells (via java) then before I retrieve the value for a cell (that contains a formula) I run EvaluateAll. Now, I'm not sure if I should be evaluating after EVERY change or after I make all my changes to the excel sheet (via java).
I can't evaluate a specific cell at a time because there's over 38 sheets with multitudes of formulas. So EvaluateAll is the best option for me
EDIT 26/10/2018*
So the issue was not clearing the cache after making inputs. The solution was after each input as specified in the javaDoc that:
Should be called whenever there are changes to input cells in the evaluated workbook.
Failure to call this method after changing cell values will cause incorrect behaviour
of the evaluate~ methods of this class
therefore after making an input on a cell you should call evaluator.clearAllCachedResultValues();

docx4j / xlsx4j : create simple spreadsheet

I want to create a simple spreadsheet in docx4j / xlsx4j. It shall contain only Strings, no formular is needed. The porpuse is basically switching from a CSV to XLSX
Therefore I tried the example here: https://github.com/plutext/docx4j/blob/master/src/samples/xlsx4j/org/xlsx4j/samples/CreateSimpleSpreadsheet.java
Unfortunetly it is not working. Even after removing the deprecated parts ( http://pastebin.com/bUnJWmFD ).
Excel reports unreadable content and suggest a repair. After that I get the error: "Entfernte Datensätze: Zellinformationen von /xl/worksheets/sheet1.xml-Part". It means something like "removed datasets: Cellinformation at /xl/worksheets/sheet1.xml-Part".
This error occures when createCell is called in line 58 (see. Github, not pastebin) or cell.setV is called with "Hello World" instead of "1234"
I think you are raising 2 issues here:
the resulting XLSX needing repair: this was the result of a typo in cell2.setR, fixed at https://github.com/plutext/docx4j/commit/7d04a65057ad61f5197fb9a98168fc654220f61f
calling setV with "Hello World", you shouldn't do that. Per http://webapp.docx4java.org/OnlineDemo/ecma376/SpreadsheetML/v.html
This element expresses the value contained in a cell. If the cell
contains a string, then this value is an index into the shared string
table, pointing to the actual string value. Otherwise, the value of
the cell is expressed directly in this element. .. For applications
not wanting to implement the shared string table, an 'inline string'
may be expressed in an <is> element under <c> (instead of a
<v> element under <c>),in the same way a string would be
expressed in the shared string table.
though I guess our setV method could detect misuse and either throw an exception or do one of those other things instead.
The CreateSimpleSpreadsheet sample as it stands shows you how to set an inline string, so you just need to test whether your input is a number or not.

POI: setCellType(Cell.CELL_TYPE_FORMULA) fails because of Cell.CELL_TYPE_ERROR

My Java application reads an xls file and presents it on a JTable. So far so good.
When I try to save my worksheet, I iterate over row,col in my JTable and:
String str = (String) Table.getValueAt(row, col);
HSSFRow thisrow = sheet.getRow(row);
HSSFCell thiscell = thisrow.getCell(col);
if(thiscell==null) thiscell = thisrow.createCell(col);
switch(inferType(str)) {
case "formula":
thiscell.setCellType(Cell.CELL_TYPE_FORMULA);
thiscell.setCellFormula(str.substring(1));
break;
case "numeric":
thiscell.setCellType(Cell.CELL_TYPE_NUMERIC);
thiscell.setCellValue(Double.parseDouble(str));
break;
case "text":
thiscell.setCellType(Cell.CELL_TYPE_STRING);
thiscell.setCellValue(str);
break;
}
But when I run over a cell which was originally a formula, say A1/B1, that is #DIV/0! at the moment, setCellType fails.
With much investigation I found out that when setCellType is called, it tries to convert the old content to the new type. BUT, this didn't seem a problem to me, since every table formula cell was already a formula in the xls. Hence, I am never actually changing types.
Even so, when I call setCellType(Cell.CELL_TYPE_FORMULA) on a cell that is already a formula, but it is evaluated to #DIV/0!, I get an conversion exception.
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Cannot get a numeric value from a error formula cell
at org.apache.poi.hssf.usermodel.HSSFCell.typeMismatch(HSSFCell.java:648)
at org.apache.poi.hssf.usermodel.HSSFCell.checkFormulaCachedValueType(HSSFCell.java:653)
at org.apache.poi.hssf.usermodel.HSSFCell.getNumericCellValue(HSSFCell.java:678)
at org.apache.poi.hssf.usermodel.HSSFCell.setCellType(HSSFCell.java:317)
at org.apache.poi.hssf.usermodel.HSSFCell.setCellType(HSSFCell.java:283)
Actually my only workaround is, before setCellType:
if(thiscell.getCachedFormulaResultType()==Cell.CELL_TYPE_ERROR)
thiscell = thisrow.createCell(col);
This IS working, but I lose the original layout of the cell, e.g. its colors.
How can I properly setCellType if the Cell is a formula with evaluation error?
I found this in the mailing list of poi-apache:
There are two possible scenarios when setting value for a formula
cell;
Update the pre-calculated value of the formula. If a cell contains formula then cell.setCellValue just updates the pre-calculated
(cached) formula value, the formula itself remains and the cell type
is not changed
Remove the formula and change the cell type to String or Number:
cell.setCellFormula(null); //Remove the formula
then cell.setCellValue("I changed! My type is CELL_TYPE_STRING now"");
or cell.setCellValue(200); //NA() is gone, the real value is 200
I think we can improve cell.setCellValue for the case (1). If the new
value conflicts with formula type then IllegalArgumentException should
be thrown.
Regards, Yegor
Still, it does feel like a workaround to me. But everything is now working.
cell.setCellFormula(null) before any setCellType should prevent conversion failure, because the first will discard the cached content.

apache poi - reading comments from blank and missing (null) cells

I'm trying to read comments from all Excel documents cell's (using Apache POI).
I have problem when empty (or missing) cells contains comments.
Currently only solutions that I found is to:
iterate every row to last not empty column
get all (even empty) cells
check if cell's comment is not empty
if true: handle comment
Some code:
if (row != null) {
cell = row.getCell(cellNum, Row.CREATE_NULL_AS_BLANK);
cellComment = cell.getCellComment();
if (cellComment != null)
...
}
Main problems is that I can't read comments from empty lines and comments which are after last not empty cell.
Increasing performance (comparing to reading all row cells) would be nice, but main point is to read ALL documents comments.
You can read the comments of Blank Cell or Null cell using missingCellPolicy, row.getCell(int cellnum, MissingCellPolicy policy) which allows you to deal with the cells which are blank or null.
For example in your sheet, say the 7th row is blank and its 5th col have some comment (say "Hello"), and you need to read that comment. just do the following:
Comment comment = sheet.getRow(7).getCell(5, Row.CREATE_NULL_AS_BLANK).getCellComment();
System.out.println(comment.getString());
will print "Hello".
To all martyrs who are using Apache POI and trying to do what OP want: use sheet.getCellComments() method - it retuns a TreeMap which keys are CellAddress'es and values are Comment instances, even if cells that handle them are null or missing.
To make that kind of cells visible for POI iterators, simply ask getCell method with MissingRowPolicy.CREATE_NULL_AS_BLANK.
e.g. (for Java 1.8+):
sheet.getCellComments().forEach((cellAddress, o) ->
sheet.getRow(cellAddress.getRow()).getCell(cellAddress.getColumn()
, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK));

Redefine Named Excel Range then Save using Apache POI

Using Apache POI, I'm able to find a named range:
XSSFName[] ranges = new XSSFName[workbook.getNumberOfNames()];
for (int i = 0; i < _wb.getNumberOfNames(); i++)
ranges[i] = workbook.getNameAt(i);
With that, I'm able to cell an AreaReference:
AreaReference area = new AreaReference(ranges[0].getRefersToFormula());
And then finally I can get all the cells within that range:
CellReference[] cells = area.getAllReferencedCells();
That all works just fine. Burt I have a use case where I have to redefine the area that the range covers. Is there a way to do that? I notice that the range.getRefersToFormula() method return a String, something like MySheet!$A$1:$B$8. There is a range.setRefersToFormula(String formula), but I've got to believe there's a way other than resorting to writing an excel range formula parser on my own. Is there no way to generate an AreaReference with a set to Cell references of something more type-safe? Do I actually have to generate a String to represent the new range? I would think there would be API somewhere to help me with this but I can't seem to find it.
Update
I found some API, but it doesn't seem to work, at least it doesn't save properly. Here's what I did.
AreaReference newArea = new AreaReference(firstCell, lastCell);
ranges[0].setRefersToFormula(newArea.formatAsString())
It seems to set the formula correctly, but when I stream the workbook back out to disk, the range is completely wrong.
you can update the existing Reference and set it as per your requirement.
Suppose the reference contains TestSheet!$A$1:$B$8and you want to change it to MySheet!$B$5:$C$12
For any cell, say "B5", at runtime,
cell.getReference();
will give you cell reference (like in example... it will return you "B5")
char startCellColRef = cell.getReference().toString().charAt(0);
will give you the Column Reference (will give you "B" if the current cell is B5). Now
int startCellRowRef = cell.getReference().toString().charAt(1);
will give you Row Index (will give you "5" if the current cell is B5).
By the same way you can get your start and end cell references (say B5 and C12).
Now comes how can I update the existing references. Just update its value with newly created reference string
Name reference = wb.getName("NameReferenceInExcelSheet");
referenceString = sheetName+"!$"+startCellColRef+"$"+startCellRowRef+":$"+endCellColRef+"$"+endCellRowRef;
reference.setRefersToFormula(referenceString);

Categories

Resources