Is there an efficient way to determine which cells have changed after a call to FormulaEvaluator::evaluateAll?
I can see there is an interface IEvaluationListener that could be useful, but it's not available externally (seems to be used for testing only).
I looked for how evaluateAll works and found BaseFormulaEvaluator.evaluateAllFormulaCells.
This code has access to all cells which are evaluated. And it simply can be used by copy/paste. Since it is the same as apache poi uses while evaluateAll it should be as performant as evaluateAll.
Before evaluation you can get the cell's old value and after the evaluation you can get the cell's new value. So you can determine if the evaluation has changed something. Of course those changings will influence the performance but there is no way to avoid this in my opinion.
import org.apache.poi.ss.usermodel.*;
import java.io.InputStream;
import java.io.FileInputStream;
class ExcelEvaluateAllFormulas {
public static void evaluateAllFormulaCells(Workbook wb) {
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
evaluateAllFormulaCells(wb, evaluator);
}
protected static void evaluateAllFormulaCells(Workbook wb, FormulaEvaluator evaluator) {
for(int i=0; i<wb.getNumberOfSheets(); i++) {
Sheet sheet = wb.getSheetAt(i);
for(Row r : sheet) {
for (Cell c : r) {
if (c.getCellTypeEnum() == CellType.FORMULA) {
CellType celltype = c.getCachedFormulaResultTypeEnum();
Object oldvalue = null;
if (celltype == CellType.NUMERIC) {
oldvalue = c.getNumericCellValue();
} else if (celltype == CellType.BOOLEAN) {
oldvalue = c.getBooleanCellValue();
} else if (celltype == CellType.STRING) {
oldvalue = c.getStringCellValue();
} else if (celltype == CellType.ERROR) {
oldvalue = "Err: " + c.getErrorCellValue();
}
if (oldvalue == null) oldvalue = "no value";
System.out.print(c.getSheet().getSheetName() + "!" + c.getAddress() + ":old value:" + oldvalue);
celltype = evaluator.evaluateFormulaCellEnum(c);
Object newvalue = null;
if (celltype == CellType.NUMERIC) {
newvalue = c.getNumericCellValue();
} else if (celltype == CellType.BOOLEAN) {
newvalue = c.getBooleanCellValue();
} else if (celltype == CellType.STRING) {
newvalue = c.getStringCellValue();
} else if (celltype == CellType.ERROR) {
newvalue = "Err: " + c.getErrorCellValue();
}
if (newvalue == null) newvalue = "no value";
System.out.println("->new value:" + newvalue);
if (oldvalue.equals(newvalue)) {
System.out.println("Value has not changed.");
} else {
System.out.println("Value has changed.");
}
}
}
}
}
}
public static void main(String[] args) throws Exception{
InputStream inp = new FileInputStream("ExcelWithFormulas.xlsx");
Workbook workbook = WorkbookFactory.create(inp);
workbook.getSheetAt(0).getRow(1).getCell(0).setCellValue(0.5);
evaluateAllFormulaCells(workbook);
workbook.close();
}
}
This example expects that the value of A2 in the first sheet is used as reference in some formulas. So changing this value leads to new values in formula cells while evaluation.
Related
How to read empty cell in excel file using POI?
When I upload Excel file, the empty cell is filled with previous cell.
like this:
this is the sample of excel file
this is the DB that the empty cell be filled with previous cell
POI VERSION
org.apache.poi
poi
4.1.2
org.apache.poi
poi-ooxml
4.1.2
code
#RequestMapping("/mng/mngSaupExcelUpload14.do")
public ModelAndView mngBizApplyExcelUpload14(MultipartHttpServletRequest request, HttpServletResponse response,
HttpSession session, INDEYearVO attach) throws IOException, ParseException {
Iterator<String> iterator = request.getFileNames();
int actResult = 0;
ModelAndView mav = new ModelAndView();
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
MultipartFile mFile = null;
while (iterator.hasNext()) {
String uploadFileName = iterator.next();
mFile = request.getFile(uploadFileName);
// String originFileName = mFile.getOriginalFilename();
// String saveFileName = originFileName;
}
XSSFWorkbook workbook = new XSSFWorkbook(mFile.getInputStream());
for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
XSSFSheet sheet = workbook.getSheetAt(sheetNum);
int rows = sheet.getPhysicalNumberOfRows();
int rowindex = 0;
int columnindex = 0;
if (sheetNum == 0) {
for (rowindex = 1; rowindex < rows; rowindex++) {
sheet = workbook.getSheetAt(0);
XSSFRow row = sheet.getRow(rowindex);
if (row != null) {
int cells = row.getPhysicalNumberOfCells();
for (columnindex = 0; columnindex <= cells; columnindex++) {
XSSFCell cell = row.getCell(columnindex);
String value = "";
if (cell == null) {
continue;
} else {
switch (cell.getCellType()) {
case FORMULA:
value = cell.getCellFormula();
break;
case NUMERIC:
//value = (int)cell.getNumericCellValue() + "";
double cellValue = cell.getNumericCellValue();
if (cellValue == Math.rint(cellValue)) {
value = String.valueOf((int) cellValue);
} else {
value = String.valueOf(cellValue);
}
break;
case STRING:
value = cell.getStringCellValue() + "";
break;
case BLANK:
value = null;
// value = cell.getBooleanCellValue() + "";
break;
case ERROR:
value = cell.getErrorCellValue() + "";
break;
default:
}
if (columnindex == 0) {
attach.setSn(Integer.parseInt(value));
}else if (columnindex == 1) {
attach.setYear(Integer.parseInt(value));
}else if (columnindex == 2) {
attach.setSigngu(value);
} else if (columnindex == 3) {
attach.setJan(Integer.parseInt(value));
} else if (columnindex == 4) {
attach.setFeb(Integer.parseInt(value));
} else if (columnindex == 5) {
attach.setMar(Integer.parseInt(value));
} else if (columnindex == 6) {
attach.setApr(Integer.parseInt(value));
} else if (columnindex == 7) {
attach.setMay(Integer.parseInt(value));
} else if (columnindex == 8) {
attach.setJne(Integer.parseInt(value));
} else if (columnindex == 9) {
attach.setJly(Integer.parseInt(value));
} else if (columnindex == 10) {
attach.setAug(Integer.parseInt(value));
} else if (columnindex == 11) {
attach.setSep(Integer.parseInt(value));
} else if (columnindex == 12) {
attach.setOct(Integer.parseInt(value));
} else if (columnindex == 13) {
attach.setNov(Integer.parseInt(value));
} else if (columnindex == 14) {
if ("false".equals(value) || "".equals(value) || "0".equals(value) || value == null ) {
attach.setDec(Integer.parseInt(""));
} else {
attach.setDec(Integer.parseInt(value));
}
}
}
}
actResult = attachFileService.fileInsertActForExcel14(attach);
}
}
}
}
if (actResult > 0) {
out.println("<script>");
out.println("alert('등록에 성공 하였습니다.');");
out.println("location.replace('" + request.getContextPath() + "attachFile.do');");
out.println("</script>");
out.flush();
out.close();
return null;
} else {
out.println("<script>");
out.println("alert('등록 중 오류가 발생했습니다.');");
out.println("location.replace('" + request.getContextPath() + "attachFile.do');");
out.println("</script>");
out.flush();
out.close();
return null;
}
}
I don't really know why empty cell is filled..
I don't think it's a problem with poi. Also check the db insert code.
And I'm developing a library that might be helpful, so I'd appreciate it if you could watch it.
https://github.com/scndry/jackson-dataformat-spreadsheet
Problem
How to add some part of data from source excel sheet to the destination excel sheet using Apache POI(XSSF Format)?Excel sheet contains merged columns.
Requirement:
Requirement is not only to copy the row but also to put the data into desired Excel cell(desired column) of the destination sheet.
Note
-Copying row to desired row location in destination excel sheet is achievable. Problem is to first merge the cell as per source sheet in destination sheet and then put data into desired merged excel cell.
- Merged Columns could vary in a row.
Here is the source code, half referred and half written.
public static void copyNodeFrmtSrcToDest(XSSFSheet srcSheet, XSSFSheet destSheet, XSSFRow srcRowStart,XSSFRow srcRowEnd
,XSSFRow destRowStart, int destCellStart, Map<Integer, XSSFCellStyle> styleMap){
/*Check if there is only one row to be pasted*/
int noOfRows = srcRowEnd.getRowNum() - srcRowStart.getRowNum();
/*Check if there is only one row to be pasted*/
if(noOfRows == 0)
{
/*Copy a single row*/
copyRow(srcSheet,destSheet,srcRowStart,destRowStart,destCellStart,styleMap);
return;
}
for (int i = 0;i <= noOfRows ;i++)//For every row
{
/*Get rows from source sheet and increment it*/
XSSFRow srcIntermediateRow = srcSheet.getRow(srcRowStart.getRowNum() + i);
if(destRowStart == null)
{
try {
throw new RowNotFoundError("Row has not been found in the sheet.Kindly create a row.");
} catch (RowNotFoundError e) {
e.printStackTrace();
System.out.println(e.toString());
}
}
if(i!=0)//Assuming destRowStart has been created by user of the API
{
/*Create a new row*/
destRowStart = destSheet.createRow(destRowStart.getRowNum()+1);
}
copyRow(srcSheet,destSheet,srcIntermediateRow,destRowStart,destCellStart,styleMap);
}
}
public static void copyRow(XSSFSheet srcSheet, XSSFSheet destSheet, XSSFRow srcRow, XSSFRow destRow, int destCellStart,
Map<Integer, XSSFCellStyle> styleMap) {
int count = 1;
Set<CellRangeAddress> mergedRegions = new HashSet<CellRangeAddress>();
CellRangeAddress previousMergedRegion =null;
destRow.setHeight(srcRow.getHeight());
for (int j = srcRow.getFirstCellNum(); j <= srcRow.getLastCellNum(); j++) {
int mergedDiff;
XSSFCell oldCell = srcRow.getCell(j);
XSSFCell newCell;
if(j == srcRow.getFirstCellNum()){
newCell = destRow.getCell(destCellStart);}
else
{
newCell = destRow.getCell(destCellStart + count);
}
if (oldCell != null) {
if (newCell == null) {
if(j == srcRow.getFirstCellNum()){
newCell = destRow.createCell(destCellStart);//Keeping the new cell as the first one.
copyCell(oldCell, newCell, styleMap);
}
else{
newCell = destRow.createCell(destCellStart + count);
count = count + 1;
copyCell(oldCell, newCell, styleMap);}
}
CellRangeAddress mergedRegion = getMergedRegion(srcSheet, srcRow.getRowNum(),oldCell.getColumnIndex());
if(previousMergedRegion != null && mergedRegion != null)
{
mergedDiff = mergedRegion.getLastColumn() - mergedRegion.getFirstColumn();
if(!previousMergedRegion.equals(mergedRegion))
{
destCellStart = destCellStart + mergedDiff + 1;
count = 1;
}
}
if (mergedRegion != null) {
previousMergedRegion = mergedRegion.copy();
mergedDiff = mergedRegion.getLastColumn() - mergedRegion.getFirstColumn();
CellRangeAddress newMergedRegion = new CellRangeAddress(destRow.getRowNum(),destRow.getRowNum()
,destCellStart,destCellStart + mergedDiff);
if (isNewMergedRegion(newMergedRegion, mergedRegions))
{
mergedRegions.add(newMergedRegion);
destSheet.addMergedRegion(newMergedRegion);
}
}
}
}
}
public static void copyCell(XSSFCell oldCell, XSSFCell newCell, Map<Integer, XSSFCellStyle> styleMap) {
if(styleMap != null) {
if(oldCell.getSheet().getWorkbook() == newCell.getSheet().getWorkbook()){
newCell.setCellStyle(oldCell.getCellStyle());
} else{
int stHashCode = oldCell.getCellStyle().hashCode();
XSSFCellStyle newCellStyle = styleMap.get(stHashCode);
if(newCellStyle == null){
newCellStyle = newCell.getSheet().getWorkbook().createCellStyle();
newCellStyle.cloneStyleFrom(oldCell.getCellStyle());
styleMap.put(stHashCode, newCellStyle);
}
newCell.setCellStyle(newCellStyle);
}
}
switch(oldCell.getCellTypeEnum()) {
case STRING:
newCell.setCellValue(oldCell.getStringCellValue());
break;
case NUMERIC:
newCell.setCellValue(oldCell.getNumericCellValue());
break;
case BLANK:
newCell.setCellValue(oldCell.getStringCellValue());
break;
case BOOLEAN:
newCell.setCellValue(oldCell.getBooleanCellValue());
break;
case ERROR:
newCell.setCellErrorValue(oldCell.getErrorCellValue());
break;
case FORMULA:
newCell.setCellFormula(oldCell.getCellFormula());
break;
default:
break;
}
}
public static CellRangeAddress getMergedRegion(XSSFSheet sheet, int rowNum, int cellNum) {
for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
CellRangeAddress merged = sheet.getMergedRegion(i);
if (merged.isInRange(rowNum, cellNum)) {
return merged;
}
}
return null;
}
private static boolean isNewMergedRegion(CellRangeAddress newMergedRegion, Set<CellRangeAddress> mergedRegions)
{
if(mergedRegions.isEmpty())
{
return true;
}
return !mergedRegions.contains(newMergedRegion);
}
}
It is working fine for some testcases but not for all.
I need to convert text file to Excel file. I found articles regarding this but my requirement is little different so I am not getting any idea.
I have text file including rows in this format
Jun 13 07:35:08 mail dovecot: pop3-login: Login: user=<veena,.patel#test.com>, method=PLAIN, rip=102.201.122.131, lip=103.123.113.83, mpid=33178, session=<Wfdfdfcxvc>
I want to create Excel file having four columns:-
first column includes "Jun 13 07:35:08" of above row
second column includes "pop3" of above row
third column includes "veena,.patel#test.com"
and fourth column includes "102.201.122.131"
All other data is not required in Excel. How can I do this? I know this is not what I wanted. I should have put some code about what I have tried first, but really I'm not getting any idea.
private void button2_Click(object sender, EventArgs e)
{
try
{
if (!String.IsNullOrWhiteSpace(fileName))
{
if (System.IO.File.Exists(fileName))
{
//string fileContant = System.IO.File.ReadAllText(fileName);
System.Text.StringBuilder sb = new StringBuilder();
List<Country> countries = new List<Country>();
Country country = null;
string line;
string[] arrLine;
System.IO.StreamReader file = new System.IO.StreamReader(fileName);
while ((line = file.ReadLine()) != null)
{
if (!string.IsNullOrWhiteSpace(line))
{
if (line.Contains("rip="))
{
arrLine = line.Split(new string[] { "mail" }, StringSplitOptions.None);
if (arrLine.Length > 1)
{
sb.Append(arrLine[0] + ";");
if (line.Contains("pop3-login:"))
{
sb.Append("pop3;");
}
else if (line.Contains("imap-login:"))
{
sb.Append("imap;");
}
else
{
sb.Append(";");
}
arrLine = line.Split(new string[] { "user=<" }, StringSplitOptions.None);
if (arrLine.Length > 1)
{
arrLine = arrLine[1].Split(new string[] { ">," }, StringSplitOptions.None);
if (arrLine.Length > 1)
{
sb.Append(arrLine[0] + ";");
}
else
{
sb.Append(";");
}
}
else
{
sb.Append(";");
}
arrLine = line.Split(new string[] { "rip=" }, StringSplitOptions.None);
if (arrLine.Length > 1)
{
arrLine = arrLine[1].Split(new string[] { "," }, StringSplitOptions.None);
if (arrLine.Length > 1)
{
sb.Append(arrLine[0] + ";");
country = countries.FirstOrDefault(a => a.IP == arrLine[0]);
if (country != null && !string.IsNullOrWhiteSpace(country.IP))
{
sb.Append(country.Name + ";");
}
else
{
sb.Append(GetCountryByIP(arrLine[0],ref countries) + ";");
}
}
else
{
sb.Append(";;");
}
}
else
{
sb.Append(";;");
}
sb.Append(System.Environment.NewLine);
}
}
}
}
file.Close();
DialogResult dialogResult = saveFileDialog1.ShowDialog();
string saveFileName=Application.StartupPath + #"\data.csv";
if (dialogResult == DialogResult.OK)
{
saveFileName = saveFileDialog1.FileName;
}
System.IO.File.WriteAllText(saveFileName, sb.ToString());
MessageBox.Show("File Save at " + saveFileName);
fileName = string.Empty;
textBox1.Text = string.Empty;
}
else
{
MessageBox.Show("File Not Found");
}
}
else
{
MessageBox.Show("Select File");
}
}
catch (Exception ex)
{
MessageBox.Show("Message:" + ex.Message + " InnerException:" + ex.InnerException);
}
}
If you are up to VBA -after you have watched some tutorials- you need this approach.
1. Get the text file by an EOF method.
2. A UDF for each of your desired criteria using REGEX would be my approach -hint: you can check your regex logic here-.
Here's an example for extracting the email:
Function UserInString(StringToAnalyze As String) As String
Dim regex As Object: Set regex = CreateObject("VBScript.RegExp")
Dim Regexmatches As Variant
Dim ItemMatch As Variant
With regex
.Pattern = "[a-z]{1,99}[,][.][A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}"
.Global = True
End With
If regex.Test(StringToAnalyze) = True Then 'there's a user in the string! ' 1. If regex.Test(StringToAnalyze) = True
Set Regexmatches = regex.Execute(StringToAnalyze)
For Each ItemMatch In Regexmatches
UserInString = IIf(UserInString = "", ItemMatch, ItemMatch & "," & UserInString)
Next ItemMatch
Else ' 1. If regex.Test(StringToAnalyze) = True
UserInString = "There's no user in the string!"
End If ' 1. If regex.Test(StringToAnalyze) = True
End Function
how to get last row of excel in POI and append blank row and write the next record after the blank row
Below is my code snippet
public class ResultSetToExcel {
private HSSFWorkbook workbook;
private HSSFSheet sheet;
private HSSFFont boldFont;
private HSSFDataFormat format;
private ResultSet resultSet;
private FormatType[] formatTypes;
public ResultSetToExcel(ResultSet resultSet, FormatType[] formatTypes, String sheetName) {
workbook = new HSSFWorkbook();
this.resultSet = resultSet;
sheet = workbook.createSheet(sheetName);
boldFont = workbook.createFont();
boldFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
format = workbook.createDataFormat();
this.formatTypes = formatTypes;
}
public ResultSetToExcel(ResultSet resultSet, String sheetName) {
this(resultSet, null, sheetName);
}
private FormatType getFormatType(Class _class) {
if (_class == Integer.class || _class == Long.class) {
return FormatType.INTEGER;
} else if (_class == Float.class || _class == Double.class) {
return FormatType.FLOAT;
} else if (_class == Timestamp.class || _class == java.sql.Date.class) {
return FormatType.DATE;
} else {
return FormatType.TEXT;
}
}
public void generate(OutputStream outputStream) throws Exception {
try {
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
if (formatTypes != null && formatTypes.length != resultSetMetaData.getColumnCount()) {
throw new IllegalStateException("Number of types is not identical to number of resultset columns. "
+ "Number of types: " + formatTypes.length + ". Number of columns: "
+ resultSetMetaData.getColumnCount());
}
int currentRow = 0;
HSSFRow row = sheet.createRow(currentRow);
int numCols = resultSetMetaData.getColumnCount();
boolean isAutoDecideFormatTypes;
if (isAutoDecideFormatTypes = (formatTypes == null)) {
formatTypes = new FormatType[numCols];
}
for (int i = 0; i < numCols; i++) {
String title = resultSetMetaData.getColumnName(i + 1);
writeCell(row, i, title, FormatType.TEXT, boldFont);
if (isAutoDecideFormatTypes) {
Class _class = Class.forName(resultSetMetaData.getColumnClassName(i + 1));
formatTypes[i] = getFormatType(_class);
}
}
currentRow++; // Write report rows
while (resultSet.next()) {
row = sheet.createRow(currentRow++);
for (int i = 0; i < numCols; i++) {
Object value = resultSet.getObject(i + 1);
writeCell(row, i, value, formatTypes[i]);
}
}
// Autosize columns
for (int i = 0; i < numCols; i++) {
sheet.autoSizeColumn((short) i);
}
workbook.write(outputStream);
} finally {
outputStream.close();
}
}
public void generate(File file) throws Exception {
generate(new FileOutputStream(file));
}
private void writeCell(HSSFRow row, int col, Object value, FormatType formatType) throws NestableException {
writeCell(row, col, value, formatType, null, null);
}
private void writeCell(HSSFRow row, int col, Object value, FormatType formatType, HSSFFont font)
throws NestableException {
writeCell(row, col, value, formatType, null, font);
}
private void writeCell(HSSFRow row, int col, Object value, FormatType formatType, Short bgColor, HSSFFont font)
throws NestableException {
HSSFCell cell = HSSFCellUtil.createCell(row, col, null);
if (value == null) {
return;
}
if (font != null) {
HSSFCellStyle style = workbook.createCellStyle();
style.setFont(font);
cell.setCellStyle(style);
}
switch (formatType) {
case TEXT:
cell.setCellValue(value.toString());
break;
case INTEGER:
cell.setCellValue(((Number) value).intValue());
HSSFCellUtil.setCellStyleProperty(cell, workbook, HSSFCellUtil.DATA_FORMAT, HSSFDataFormat
.getBuiltinFormat(("#,##0")));
break;
case FLOAT:
cell.setCellValue(((Number) value).doubleValue());
HSSFCellUtil.setCellStyleProperty(cell, workbook, HSSFCellUtil.DATA_FORMAT, HSSFDataFormat
.getBuiltinFormat(("#,##0.00")));
break;
case DATE:
cell.setCellValue((Timestamp) value);
HSSFCellUtil.setCellStyleProperty(cell, workbook, HSSFCellUtil.DATA_FORMAT, HSSFDataFormat
.getBuiltinFormat(("m/d/yy")));
break;
case MONEY:
cell.setCellValue(((Number) value).intValue());
HSSFCellUtil.setCellStyleProperty(cell, workbook, HSSFCellUtil.DATA_FORMAT, format
.getFormat("($#,##0.00);($#,##0.00)"));
break;
case PERCENTAGE:
cell.setCellValue(((Number) value).doubleValue());
HSSFCellUtil.setCellStyleProperty(cell, workbook, HSSFCellUtil.DATA_FORMAT, HSSFDataFormat
.getBuiltinFormat("0.00%"));
}
if (bgColor != null) {
HSSFCellUtil.setCellStyleProperty(cell, workbook, HSSFCellUtil.FILL_FOREGROUND_COLOR, bgColor);
HSSFCellUtil
.setCellStyleProperty(cell, workbook, HSSFCellUtil.FILL_PATTERN, HSSFCellStyle.SOLID_FOREGROUND);
}
}
public enum FormatType {
TEXT, INTEGER, FLOAT, DATE, MONEY, PERCENTAGE
}
}
And class implementing the above code
ResultSetToExcel resultSetToExcel = new ResultSetToExcel(iResultSet, csv_file_name);
int fileCount = 1;
while (true) {
boolean done = resultSetToExcel.generate(new File(csv_file_path+ csv_file_name));
if (done) break;
csv_file_name = csv_file_name + "_" + fileCount + ".xls";
fileCount++;
}
Edit:
I used below snippet
InputStream myxls = new FileInputStream("test.xls");
Workbook book = new HSSFWorkbook(myxls);
Sheet sheet = book.getSheetAt(0);
System.out.println(sheet.getLastRowNum());
But I get this error
java.io.IOException: Unable to read entire header; 0 bytes read; expected 512 bytes
The key bit is the exception is the number 0:
java.io.IOException: Unable to read entire header; 0 bytes read; expected 512 bytes
POI went to read the 512 byte header that should be at the start of the file, but found 0 bytes in the file - your file is empty.
Make sure you're passing in the correct filename to load. (You mention struts in your tag, you might not be in the directory you expected to be when running...)
Also, I'd suggest a slight improvement to your code. You have a file, and you're using the common interfaces, so you should change how you load the workbook. Instead of your current:
InputStream myxls = new FileInputStream("test.xls");
Workbook book = new HSSFWorkbook(myxls);
You'd be much better off with
Workbook book = WorkbookFactory.create(new File("test.xls"));
Loading directly from a file, rather than going via an InputStream, will give better performance and a lower memory footprint. By using the WorkbookFactory, your code can work for both XLS and XLSX files.
I am using POI XSSF API and I would like to transpose a sheet.
how can I do that?
Thanks.
Transpose, as in swap A2 with B1 and A3 with C1 (so columns become rows)?
If so, there's nothing built in, so you'd need to do a little bit of coding yourself. You'd likely want to grab a pair of cells, save the contents of one (value and style), copy the second to the first, then overwrite the second.
See the quick guide if you're not sure on all the reading/writing parts.
I was looking for the same answer and had to code it myself. I've attached my solution which is quite simple:
Determine the number of rows
Determine the maximal number of columns used
Iterator over every row, and every column
Save the Cell from that row/column into a simple list as a 'CellModel' type
Once done, iterate over all CellModels
Switch column and row index and save the CellModel into the sheet
The code I've used is:
public static void transpose(Workbook wb, int sheetNum, boolean replaceOriginalSheet) {
Sheet sheet = wb.getSheetAt(sheetNum);
Pair<Integer, Integer> lastRowColumn = getLastRowAndLastColumn(sheet);
int lastRow = lastRowColumn.getFirst();
int lastColumn = lastRowColumn.getSecond();
LOG.debug("Sheet {} has {} rows and {} columns, transposing ...", new Object[] {sheet.getSheetName(), 1+lastRow, lastColumn});
List<CellModel> allCells = new ArrayList<CellModel>();
for (int rowNum = 0; rowNum <= lastRow; rowNum++) {
Row row = sheet.getRow(rowNum);
if (row == null) {
continue;
}
for (int columnNum = 0; columnNum < lastColumn; columnNum++) {
Cell cell = row.getCell(columnNum);
allCells.add(new CellModel(cell));
}
}
LOG.debug("Read {} cells ... transposing them", allCells.size());
Sheet tSheet = wb.createSheet(sheet.getSheetName() + "_transposed");
for (CellModel cm : allCells) {
if (cm.isBlank()) {
continue;
}
int tRow = cm.getColNum();
int tColumn = cm.getRowNum();
Row row = tSheet.getRow(tRow);
if (row == null) {
row = tSheet.createRow(tRow);
}
Cell cell = row.createCell(tColumn);
cm.insertInto(cell);
}
lastRowColumn = getLastRowAndLastColumn(sheet);
lastRow = lastRowColumn.getFirst();
lastColumn = lastRowColumn.getSecond();
LOG.debug("Transposing done. {} now has {} rows and {} columns.", new Object[] {tSheet.getSheetName(), 1+lastRow, lastColumn});
if (replaceOriginalSheet) {
int pos = wb.getSheetIndex(sheet);
wb.removeSheetAt(pos);
wb.setSheetOrder(tSheet.getSheetName(), pos);
}
}
private static Pair<Integer, Integer> getLastRowAndLastColumn(Sheet sheet) {
int lastRow = sheet.getLastRowNum();
int lastColumn = 0;
for (Row row : sheet) {
if (lastColumn < row.getLastCellNum()) {
lastColumn = row.getLastCellNum();
}
}
return new Pair<Integer, Integer>(lastRow, lastColumn);
}
Whereby the CellModel is a wrapper which holds the data a Cell contained (you can add more attributes if you like e.g., comments, ...):
static class CellModel {
private int rowNum = -1;
private int colNum = -1;
private CellStyle cellStyle;
private int cellType = -1;
private Object cellValue;
public CellModel(Cell cell) {
if (cell != null) {
this.rowNum = cell.getRowIndex();
this.colNum = cell.getColumnIndex();
this.cellStyle = cell.getCellStyle();
this.cellType = cell.getCellType();
switch (this.cellType) {
case Cell.CELL_TYPE_BLANK:
break;
case Cell.CELL_TYPE_BOOLEAN:
cellValue = cell.getBooleanCellValue();
break;
case Cell.CELL_TYPE_ERROR:
cellValue = cell.getErrorCellValue();
break;
case Cell.CELL_TYPE_FORMULA:
cellValue = cell.getCellFormula();
break;
case Cell.CELL_TYPE_NUMERIC:
cellValue = cell.getNumericCellValue();
break;
case Cell.CELL_TYPE_STRING:
cellValue = cell.getRichStringCellValue();
break;
}
}
}
public boolean isBlank() {
return this.cellType == -1 && this.rowNum == -1 && this.colNum == -1;
}
public void insertInto(Cell cell) {
if (isBlank()) {
return;
}
cell.setCellStyle(this.cellStyle);
cell.setCellType(this.cellType);
switch (this.cellType) {
case Cell.CELL_TYPE_BLANK:
break;
case Cell.CELL_TYPE_BOOLEAN:
cell.setCellValue((boolean) this.cellValue);
break;
case Cell.CELL_TYPE_ERROR:
cell.setCellErrorValue((byte) this.cellValue);
break;
case Cell.CELL_TYPE_FORMULA:
cell.setCellFormula((String) this.cellValue);
break;
case Cell.CELL_TYPE_NUMERIC:
cell.setCellValue((double) this.cellValue);
break;
case Cell.CELL_TYPE_STRING:
cell.setCellValue((RichTextString) this.cellValue);
break;
}
}
public CellStyle getCellStyle() {
return cellStyle;
}
public int getCellType() {
return cellType;
}
public Object getCellValue() {
return cellValue;
}
public int getRowNum() {
return rowNum;
}
public int getColNum() {
return colNum;
}
}