Why am I getting IllegalArgumentException while using copyRowFrom(...) in XSSFRow? - java

I'm trying to use copyRowFrom(...); to copy the first row of a spreadsheet to the first row of a new XSSFSheet but something is not working right.
You can find the XSSFRow class and the method here:
https://github.com/apache/poi/blob/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java#L581
I'm getting IllegalArgumentException("amountToMove must not be zero") from FormulaShifter.java:
https://github.com/apache/poi/blob/trunk/src/java/org/apache/poi/ss/formula/FormulaShifter.java#L80
It seems that the problem is at line 623 of XSSFRow where FormulaShifter.createForRowCopy(...) is invoked with parameter rowDifference = 0 because source row is 0 and destination row is 0:
https://github.com/apache/poi/blob/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java#L623
I don't know, maybe it's an error, but when reaching line 80 in FormulaShifter.java the parameter rowDifference corresponds to amountToMove which is 0 so it throws IllegalArgumentException.
Am I missing something or is this a bug of copyRowFrom(...); method in XSSFRow?

You are correct. This is a bug in XSSFRow since it calls FormulaShifter.createForRowCopy even if there is nothing to shift because destination row number is the same as source row number. You could file this as a bug to apache poi.
But then there is a need for creating a test case which can be provided there. I have done that for you. The code also provides a workaround. This is first copying to a wrong destination row where row number differs from source row number. Then it copies the wrong first destination row to the really needed destination row.
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
class ExcelCopyRowFrom {
public static void main(String[] args) throws Exception {
XSSFWorkbook workbook = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("SAMPLE.xlsx"));
XSSFSheet srcSheet = workbook.getSheetAt(0);
XSSFRow srcRow = srcSheet.getRow(0);
XSSFSheet destSheet = workbook.createSheet();
//XSSFRow destRow = destSheet.createRow(0); //this fails because destination row number is the same as source row number
XSSFRow destRow = destSheet.createRow(1); //this works
destRow.copyRowFrom(srcRow, new CellCopyPolicy());
//workaround copy wrong first destination row to really needed destination row
XSSFRow destRowNeeded = destSheet.createRow(0);
destRowNeeded.copyRowFrom(destRow, new CellCopyPolicy());
//the remove wrong first destination row
destSheet.removeRow(destRow);
FileOutputStream outputStream = new FileOutputStream("SAMPLENEW.xlsx");
workbook.write(outputStream);
outputStream.close();
workbook.close();
}
}

//fix amountToMove must not be zero:srcRows index base destStartRow+1,avoid the same
int indexAdd1 = 1;
for (Row row : failRowList) {
row.setRowNum(indexAdd1);
indexAdd1++;
}
failSheet.copyRows(failRowList,0, CellCopyPolicyFactory.newOnlyValue());

Related

Apache Poi setActiveCell() for multiple cells

I'm trying to use the method sheet.setActiveCell(CellAddress addr) to set a range of multiple cells active at the same time. I've tryed with multiple versions of Apache poi-ooxml library and now i'm using 3.16 which also supports the method sheet.setActiveCell(String addr)(I know 3.16 is old but the issue stays the same also with the latest version).
Following the suggestions on this question: Is it possible to set the active range with Apache POI XSSF?
I've managed to get it to work, both with the custom CellAddress and the String in the format "A1:B5".
The problem is that every time I try to open an xlsx in which a range of cells has been set to active using apache poi, I get an error message from Excel saying that the file is damaged and need to be recovered. If I do, the recovery completes correctly, but this error is annoying since I have to open a great number of these files each day.
Is there a way to avoid this error from excel (maybe modifying the creation of the xlsx or changing some setting in Excel)?
Only one cell can be the active cell. And Sheet.setActiveCell only sets that one active cell. So sheet.setActiveCell("A1:B5") will work if setActiveCell(String addr) is available but it leads to a corrupted sheet. That's why it was removed.
Multiple cells can be selected. But there are no methods to set the selected cells in apache poi's high level classes. So the underlying low level classes needs to be used. Doing this one needs differentiate between XSSF and HSSF because different low level classes needs to be used.
Following complete example sets active cell to B2. This also sets sheet view having selection and active cell to that one given cell B2. Then it uses low level methods of XSSF and HSSF to set the selection to B2:E5.
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
class CreateExcelSelectMultipleCells {
public static void main(String[] args) throws Exception {
try (Workbook workbook = new XSSFWorkbook(); FileOutputStream out = new FileOutputStream("Excel.xlsx") ) {
//try (Workbook workbook = new HSSFWorkbook(); FileOutputStream out = new FileOutputStream("Excel.xls") ) {
Sheet sheet = workbook.createSheet();
Row row;
Cell cell;
for (int r = 0; r < 6; r++) {
row = sheet.createRow(r);
for (int c = 0; c < 6; c++) {
cell = row.createCell(c);
cell.setCellValue("R" + (r+1) + "C" + (c+1));
}
}
// set active cell; this also sets sheet view having selection and active cell to one given cell
sheet.setActiveCell(new CellAddress("B2"));
// set selected cells
if (sheet instanceof XSSFSheet) {
XSSFSheet xssfSheet = (XSSFSheet) sheet;
xssfSheet.getCTWorksheet().getSheetViews().getSheetViewArray(0).getSelectionArray(0).setSqref(
java.util.Arrays.asList("B2:E5"));
} else if (sheet instanceof HSSFSheet) {
HSSFSheet hssfSheet = (HSSFSheet) sheet;
org.apache.poi.hssf.record.SelectionRecord selectionRecord = hssfSheet.getSheet().getSelection();
java.lang.reflect.Field field_6_refs = org.apache.poi.hssf.record.SelectionRecord.class.getDeclaredField("field_6_refs");
field_6_refs.setAccessible(true);
field_6_refs.set(
selectionRecord,
new org.apache.poi.hssf.util.CellRangeAddress8Bit[] { new org.apache.poi.hssf.util.CellRangeAddress8Bit(1,4,1,4) }
);
}
workbook.write(out);
}
}
}

XSSFSheet Apache POI - is it possible to lock everything but allowing update and insert rows?

I try to lock the whole sheet but some columns should be unlocked (I want to add values in some columns). I want to copy some row, add new row and paste values from copied row. Is it possible?
public ByteArrayResource getQuestionnaireTemplate(List<QuestionnaireTemplateDto> questionnaireTemplateInitialData) throws IOException {
XSSFWorkbook workbook = excelExportService.createExcelWorkBook();
String frameworkName = questionnaireTemplateInitialData.stream().map(QuestionnaireTemplateDto::getFramework).findFirst().orElse("Framework Name");
XSSFSheet sheet = workbook.createSheet(frameworkName);
// sheet.lockInsertColumns(true);
// sheet.lockInsertRows(false);
sheet.enableLocking();
// sheet.lo
// CellStyle unlockedCellStyle = workbook.createCellStyle();
// unlockedCellStyle.setLocked(false);
CTSheetProtection sheetProtection = sheet.getCTWorksheet().getSheetProtection();
sheetProtection.setSelectLockedCells(false);
sheetProtection.setSelectUnlockedCells(false);
sheetProtection.setFormatCells(false);
sheetProtection.setFormatColumns(false);
sheetProtection.setFormatRows(false);
sheetProtection.setInsertColumns(false);
sheetProtection.setInsertRows(false);
sheetProtection.setInsertHyperlinks(false);
sheetProtection.setDeleteColumns(false);
sheetProtection.setDeleteRows(false);
sheetProtection.setSort(false);
sheetProtection.setAutoFilter(false);
sheetProtection.setPivotTables(false);
sheetProtection.setObjects(false);
sheetProtection.setScenarios(false);
Amd then for some row I can set some cell as editable (it works):
private void addFieldRow(XSSFSheet sheet, XSSFCellStyle fieldRowStyle, QuestionnaireTemplateDto questionnaireTemplateDto) {
XSSFRow row = excelExportService.createRow(sheet,
sheet.getLastRowNum() + 1,
Arrays.asList(questionnaireTemplateDto.getFrameworkFieldId().toString(), questionnaireTemplateDto.getFramework(), questionnaireTemplateDto.getFieldName(), questionnaireTemplateDto.getYear().toString()),
fieldRowStyle);
CellStyle unlockedStyle = sheet.getWorkbook().createCellStyle();
unlockedStyle.setLocked(false);
XSSFCell cell = row.createCell(4);
cell.setCellStyle(unlockedStyle);
}
My generated sheet works perfectly except for one small detail - I can't insert new row and copy some other row to it...
I think I tried all solutions from Stack Overflow...
You should use XSSFSheet methods instead of low level CTSheetProtection methods for specifying sheet protection. In current apache poi 4.1.2 there are all possibilities also useable using XSSFSheet.lock...- methods.
But what you want is not completely possible. Sheet protection mainly protects cells from changing. So if you allow deleting rows and the row contains protected cells, then deleting the row contradicts the cell protection. The same is for copying a row into another. If that other row contains protected cells, then copying also contradicts the cell protection.
Following complete example creates a workbook having one sheet where all cells are protected except columns A, C and E. The sheet protection allows formatting rows, inserting rows and deleting rows. But deleting rows contradicts the cell protection of all cells in that row except those in columns A, C and E. So deleting rows is allowed but not possible.
So the resulting sheet allows only changing cells in columns A, C and E, formatting rows (row height) and inserting rows.
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
public class CreateExcelXSSFProtectedSheet {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook();
CellStyle notLocked = workbook.createCellStyle();
notLocked.setLocked(false);
Sheet sheet = workbook.createSheet();
sheet.setDefaultColumnStyle(0, notLocked); // column A is not locked
sheet.setDefaultColumnStyle(2, notLocked); // column C is not locked
sheet.setDefaultColumnStyle(4, notLocked); // column E is not locked
((XSSFSheet)sheet).lockFormatRows(false); // formatting rows is allowed
((XSSFSheet)sheet).lockInsertRows(false); // inserting rows is allowed
((XSSFSheet)sheet).lockDeleteRows(false); // deleting rows is allowed but may contradict cell protection
sheet.protectSheet("");
FileOutputStream out = new FileOutputStream("CreateExcelXSSFProtectedSheet.xlsx");
workbook.write(out);
out.close();
workbook.close();
}
}

Apache poi - Remove first row in excel and save it to the same file

Hello i need help with this, i tried about 30 tutorials last few hours and i dont know how to solve it:
Open Excel File
Delete and remove row "A" ( to be replaced in for later by excel row B,C,D,...)
Rewrite opened Excel File ( because if program crash for high usage i need to have stored last value, and start program again without searching and deleting which was used ... )
OPCPackage fileInputStream = OPCPackage.open(new File("input.xlsx"));
XSSFWorkbook workbook = new XSSFWorkbook(fileInputStream);
XSSFSheet worksheet = workbook.getSheetAt(0);
worksheet.shiftRows(0, 0, 1);
workbook.write(new FileOutputStream("input.xlsx"));
This code dont remove row a and dont save file to the same location ...
Could anybody help me please?
Thank you FJ
First problem in your code:
The worksheet.shiftRows(0, 0, 1); shifts first row one row downwards. If the need is removing first row, then second row up to last row should be shifted one row upwards. This would be worksheet.shiftRows(1, worksheet.getLastRowNum(), -1);.
Second problem in your code:
If a File is used for creating a Workbook then the workbook cannot be written into the same file. This is because the File used stays opened until the workbook will be closed. So we should not using a File here for opening the workbook but a FileInputStream instead.
Working example:
import org.apache.poi.ss.usermodel.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
class ReadExcelRemoveRowAndWrite {
public static void main(String[] args) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("input.xlsx"));
Sheet worksheet = workbook.getSheetAt(0);
worksheet.shiftRows(1, worksheet.getLastRowNum(), -1);
workbook.write(new FileOutputStream("input.xlsx"));
workbook.close();
}
}

Apache POI Pivot table error when same index is used for both column and row label

I am trying to create a pivot table to do cohort analysis
pivotTable.addColumnLabel(DataConsolidateFunction.COUNT, 1);
pivotTable.addRowLabel(1);
this is giving me an error while opening the file that the file is corrupt do you want to still open the file, when I say yes and open it, the result looks fine, the only issue is the error.
I did a workaround to have a duplicate column data with different name
for ex:
say column 1 is email added a duplicate column 36 with name dup email and did as shown below, it works fine
pivotTable.addColumnLabel(DataConsolidateFunction.COUNT, 1);
pivotTable.addRowLabel(35);
why in the first place it failed when I give both column and row label as 1.
Any help is greatly appreciated
If you set pivotTable.addRowLabel(1) using apache poi, then apache poi sets pivot field 1 only to be axisRow but it needs to be dataField too if you also want to pivotTable.addColumnLabel(DataConsolidateFunction.COUNT, 1). So we neeed to correct this.
Example:
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import java.io.*;
class PivotTableTest5 {
private static void setCellData(Sheet sheet) {
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellValue("Name");
cell = row.createCell(1);
cell.setCellValue("City");
for (int r = 1; r < 15; r++) {
row = sheet.createRow(r);
cell = row.createCell(0);
cell.setCellValue("Name " + ((r-1) % 4 + 1));
cell = row.createCell(1);
cell.setCellValue("City " + (int)((new java.util.Random().nextDouble() * 3)+1) );
}
}
public static void main(String[] args) {
try {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet();
//Create some data to build the pivot table on
setCellData(sheet);
XSSFPivotTable pivotTable = sheet.createPivotTable(
new AreaReference(new CellReference("A1"), new CellReference("B15")), new CellReference("H5"));
//Count the second column. This needs to be second column a data field.
pivotTable.addColumnLabel(DataConsolidateFunction.COUNT, 1);
//Use second column as row label
pivotTable.addRowLabel(1);
//Apache poi sets pivot field 1 (second column) only to be axisRow but it needs to be dataField too.
pivotTable.getCTPivotTableDefinition().getPivotFields().getPivotFieldArray(1).setDataField(true);
FileOutputStream fileOut = new FileOutputStream("PivotTableTest5.xlsx");
wb.write(fileOut);
fileOut.close();
wb.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

What is the use of CELL_TYPE_ERROR?

I want to understand the use of cell type CELL_TYPE_ERROR in apache poi. I tried the following code, I see no error.
Workbook wb = new XSSFWorkbook();
Row row = sheet1.createRow(0);
Cell cell = row.createCell(0);
cell.setCellType(Cell.CELL_TYPE_ERROR);
cell.setCellValue(234);
System.out.println("error cell value-"+ cell.getNumericCellValue()); //this prints 234.0
Also, I want to understand if the cell can be of type error if we don't manually set its type.
See the comments in the code.
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
class CellTypeErrorTest {
public static void main(String[] args) {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
//The following works, but it makes no sense, because the cell will have no real content.
//If you wants to see, how this will be shown into the Workbook, then comment out the
//following code that overwrites the Cell with numeric content.
cell.setCellType(Cell.CELL_TYPE_ERROR);
cell.setCellErrorValue(FormulaError.DIV0.getCode());
System.out.println("error cell value-"+ FormulaError.forInt(cell.getErrorCellValue()).getString());
//If you put real content in the cell, then the CELL_TYPE_ERROR goes away, if the content
//not produces ERROR.
cell.setCellValue(234);
System.out.println(cell.getCellType()); //0 == CELL_TYPE_NUMERIC
//If you put a Formula in the Cell, it will not be evaluated automatically.
//So there is no error, even the formula will produce error if it will be evaluated.
cell = row.createCell(1);
cell.setCellFormula("1/0");
System.out.println(cell.getCellType()); //2 == CELL_TYPE_FORMULA
//It you need to check if a formula produces error, then you have to evaluate it.
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
CellValue cellValue = evaluator.evaluate(cell);
System.out.println(cellValue.getCellType()); //5 == CELL_TYPE_ERROR
if (cellValue.getCellType() == Cell.CELL_TYPE_ERROR) {
System.out.println("error cell value-"+ FormulaError.forInt(cellValue.getErrorValue()).getString());
}
try {
FileOutputStream fileOut = new FileOutputStream("workbook.xlsx");
wb.write(fileOut);
fileOut.close();
} catch (FileNotFoundException fnfex) {
} catch (IOException ioex) {
}
}
}
Conclusion:
The Cell.CELL_TYPE_ERROR is necessary to detect if a cell content produces an error. It mostly makes no sense, to set it manually.
It can be setted manually to cells without real content with cell.setCellErrorValue. But this mostly makes no sense, because if the cell gets real content and this don't produces an error, then the CellType changes automatically to another type.
POI do not evaluate the cells formulas automatically. CellTypes of cells with formulas are ever Cell.CELL_TYPE_FORMULA. Therefore, to check whether a cell formula produces error, we have to evaluate manually and then to check the CellType of the evaluated CellValue. See: http://poi.apache.org/spreadsheet/eval.html
Greetings
Axel

Categories

Resources