Created attachment 31338 [details] Template file to reproduce the issue I'm trying to add rows to an Excel 2010 table that has no visible header rows. However, POI-3.10 throws an exception on safe (see below). Given: ====== an Excel 2010 file that contains a table with no visible header rows Description: ============ New rows are added to the table and the table is expanded before safe. POI tries to update the table headers in org.apache.poi.xssf.usermodel.XSSFTable#updateHeaders but fails to take into account that there are no visible table headers. If I use the debugger to set the value of row in XSSFTable line 295 to null, the exception can be avoided and POI produces a file that opens in Excel without errors. I guess adding a condition that checks for number of visible header rows should help. Please note: This exception does not occur if the table has headers. It also does not occur if there is no other content in the file (thus the x in column C). Demo program to reproduce (please adjust paths before running): =============================================================== public class Demo { public static void main(String[] args) throws IOException, InvalidFormatException { final Workbook workbook = WorkbookFactory.create(new File("Tabelle.xlsx")); final XSSFSheet sheet = (XSSFSheet) workbook.getSheetAt(0); // add some contents to table so that the table will need expansion Row row = sheet.getRow(0); Cell cell = row.createCell(0); cell.setCellValue("demo1"); cell = row.createCell(1); cell.setCellValue("demo2"); cell = row.createCell(2); cell.setCellValue("demo3"); row = sheet.getRow(1); cell = row.createCell(0); cell.setCellValue("demo1"); cell = row.createCell(1); cell.setCellValue("demo2"); cell = row.createCell(2); cell.setCellValue("demo3"); // expand table XSSFTable table = sheet.getTables().get(0); final CellReference startRef = table.getStartCellReference(); final CellReference endRef = table.getEndCellReference(); table.getCTTable().setRef(new CellRangeAddress(startRef.getRow(), 1, startRef.getCol(), endRef.getCol()).formatAsString()); FileOutputStream stream = new FileOutputStream("e:\\output.xlsx"); workbook.write(stream); stream.close(); } } Excpetion: ========== Exception in thread "main" org.apache.xmlbeans.impl.values.XmlValueDisconnectedException at org.apache.xmlbeans.impl.values.XmlObjectBase.check_orphaned(XmlObjectBase.java:1213) at org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCellImpl.getF(Unknown Source) at org.apache.poi.xssf.usermodel.XSSFCell.getCellType(XSSFCell.java:529) at org.apache.poi.xssf.usermodel.XSSFCell.getRichStringCellValue(XSSFCell.java:264) at org.apache.poi.xssf.usermodel.XSSFCell.getStringCellValue(XSSFCell.java:251) at org.apache.poi.xssf.usermodel.XSSFTable.updateHeaders(XSSFTable.java:301) at org.apache.poi.xssf.usermodel.XSSFTable.writeTo(XSSFTable.java:84) at org.apache.poi.xssf.usermodel.XSSFTable.commit(XSSFTable.java:95) at org.apache.poi.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:322) at org.apache.poi.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:326) at org.apache.poi.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:326) at org.apache.poi.POIXMLDocument.write(POIXMLDocument.java:173) at Demo.main(PoiBug.java:49) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
I've found the problem in XSSFTable.updateHeaders() while fixing bug #56274, and found that adding row validation solves it, i.e. changing if (row != null) to if (row != null && row.getCTRow().validate()) I've not included a patch, since I'm not sure it's ok to count on validate() to check this - can invalid rows still be appropriate for being a table header row? In addition, validate() might be too heavy - but I didn't find any other way of identifying what's wrong.
The actual point where the Cell contents becomes invalid is here: CTRowImpl(XmlComplexContentImpl).arraySetterHelper(XmlObject[], QName) line: 1149 CTRowImpl.setCArray(CTCell[]) line: not available XSSFRow.onDocumentWrite() line: 466 XSSFSheet.write(OutputStream) line: 2761 XSSFSheet.commit() line: 2725 XSSFSheet(POIXMLDocumentPart).onSave(Set<PackagePart>) line: 322 XSSFWorkbook(POIXMLDocumentPart).onSave(Set<PackagePart>) line: 326 XSSFWorkbook(POIXMLDocument).write(OutputStream) line: 173 XSSFTestDataSamples.writeOutAndReadBack(R) line: 68 TestXSSFCell.test56170() line: 299 Unfortunatly the XMLBeans framework is very picky about objects being used in more than one place, sometimes setting objects to "invalid" if they are removed from some collection, while we are still using it in other places. Your change basically just checks if the row or any cell inside it became invalid by some prior action, so it is not a full fix, but just does not do anything any more with the cell as soon as it becomes invalid.
I did some more investigation, this is likely caused at XSSFRow.onDocumentWrite() when we call _row.setCArray(cArray); We mostly pass in objects which were in this array before, but XMLBeans does not handle this case when the old array is larger than the new one. The call gets to XmlComplexContentImpl.arraySetterHelper() where XMLBeans will release any objects that it removes from the previous array and thus objects will be disconnected if they are still referenced there afterwards, despite them still being in the new array that we set! So it is mostly caused by how XMLBeans is handling setting these arrays, my first approach in fixing locally in POI is to keep the array of CTCells in XSSFRow._row in sync with _cells, so these cases do not appear at all.
I tried to fix this in r1595659, I could not find any other way than to copy the CTCell in this case, which is not nice and may cause a slight increase in save-time.