blob: 3afec95478333d614a8f750a6c051cbce6442819 [file] [log] [blame]
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.ss.usermodel;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.ITestDataProvider;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.LocaleUtil;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* Common superclass for testing automatic sizing of sheet columns
*
* @author Yegor Kozlov
*/
public abstract class BaseTestSheetAutosizeColumn {
private final ITestDataProvider _testDataProvider;
private static Locale userLocale;
@BeforeAll
public static void initLocale() {
userLocale = LocaleUtil.getUserLocale();
LocaleUtil.setUserLocale(Locale.ROOT);
}
@AfterAll
public static void resetLocale() {
LocaleUtil.setUserLocale(userLocale);
}
protected BaseTestSheetAutosizeColumn(ITestDataProvider testDataProvider) {
_testDataProvider = testDataProvider;
}
protected void trackColumnsForAutoSizingIfSXSSF(Sheet sheet) {
// do nothing for Sheet base class. This will be overridden for SXSSFSheets.
}
@Test
void numericCells() throws Exception {
Workbook workbook = _testDataProvider.createWorkbook();
DataFormat df = workbook.getCreationHelper().createDataFormat();
Sheet sheet = workbook.createSheet();
trackColumnsForAutoSizingIfSXSSF(sheet);
Row row = sheet.createRow(0);
row.createCell(0).setCellValue(0); // getCachedFormulaResult() returns 0 for not evaluated formula cells
row.createCell(1).setCellValue(10);
row.createCell(2).setCellValue("10");
row.createCell(3).setCellFormula("(A1+B1)*1.0"); // a formula that returns '10'
Cell cell4 = row.createCell(4); // numeric cell with a custom style
CellStyle style4 = workbook.createCellStyle();
style4.setDataFormat(df.getFormat("0.0000"));
cell4.setCellStyle(style4);
cell4.setCellValue(10); // formatted as '10.0000'
row.createCell(5).setCellValue("10.0000");
// autosize not-evaluated cells, formula cells are sized as if the result is 0
for (int i = 0; i < 6; i++) sheet.autoSizeColumn(i);
assertTrue(sheet.getColumnWidth(0) < sheet.getColumnWidth(1)); // width of '0' is less then width of '10'
assertEquals(sheet.getColumnWidth(1), sheet.getColumnWidth(2)); // 10 and '10' should be sized equally
assertEquals(sheet.getColumnWidth(3), sheet.getColumnWidth(0)); // formula result is unknown, the width is calculated for '0'
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(5)); // 10.0000 and '10.0000'
// evaluate formulas and re-autosize
evaluateWorkbook(workbook);
for (int i = 0; i < 6; i++) sheet.autoSizeColumn(i);
assertTrue(sheet.getColumnWidth(0) < sheet.getColumnWidth(1)); // width of '0' is less then width of '10'
assertEquals(sheet.getColumnWidth(1), sheet.getColumnWidth(2)); // columns 1, 2 and 3 should have the same width
assertEquals(sheet.getColumnWidth(2), sheet.getColumnWidth(3)); // columns 1, 2 and 3 should have the same width
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(5)); // 10.0000 and '10.0000'
workbook.close();
}
@Test
void booleanCells() throws Exception {
Workbook workbook = _testDataProvider.createWorkbook();
Sheet sheet = workbook.createSheet();
trackColumnsForAutoSizingIfSXSSF(sheet);
Row row = sheet.createRow(0);
row.createCell(0).setCellValue(0); // getCachedFormulaResult() returns 0 for not evaluated formula cells
row.createCell(1).setCellValue(true);
row.createCell(2).setCellValue("TRUE");
row.createCell(3).setCellFormula("1 > 0"); // a formula that returns true
// autosize not-evaluated cells, formula cells are sized as if the result is 0
for (int i = 0; i < 4; i++) sheet.autoSizeColumn(i);
assertTrue(sheet.getColumnWidth(1) > sheet.getColumnWidth(0)); // 'true' is wider than '0'
assertEquals(sheet.getColumnWidth(1), sheet.getColumnWidth(2)); // 10 and '10' should be sized equally
assertEquals(sheet.getColumnWidth(3), sheet.getColumnWidth(0)); // formula result is unknown, the width is calculated for '0'
// evaluate formulas and re-autosize
evaluateWorkbook(workbook);
for (int i = 0; i < 4; i++) sheet.autoSizeColumn(i);
assertTrue(sheet.getColumnWidth(1) > sheet.getColumnWidth(0)); // 'true' is wider than '0'
assertEquals(sheet.getColumnWidth(1), sheet.getColumnWidth(2)); // columns 1, 2 and 3 should have the same width
assertEquals(sheet.getColumnWidth(2), sheet.getColumnWidth(3)); // columns 1, 2 and 3 should have the same width
workbook.close();
}
@Test
void dateCells() throws Exception {
Workbook workbook = _testDataProvider.createWorkbook();
Sheet sheet = workbook.createSheet();
trackColumnsForAutoSizingIfSXSSF(sheet);
DataFormat df = workbook.getCreationHelper().createDataFormat();
CellStyle style1 = workbook.createCellStyle();
style1.setDataFormat(df.getFormat("m"));
CellStyle style3 = workbook.createCellStyle();
style3.setDataFormat(df.getFormat("mmm"));
CellStyle style5 = workbook.createCellStyle(); //rotated text
style5.setDataFormat(df.getFormat("mmm/dd/yyyy"));
Calendar calendar = LocaleUtil.getLocaleCalendar(2010, 0, 1); // Jan 1 2010
Row row = sheet.createRow(0);
row.createCell(0).setCellValue(DateUtil.getJavaDate(0)); //default date
Cell cell1 = row.createCell(1);
cell1.setCellValue(calendar);
cell1.setCellStyle(style1);
row.createCell(2).setCellValue("1"); // column 1 should be sized as '1'
Cell cell3 = row.createCell(3);
cell3.setCellValue(calendar);
cell3.setCellStyle(style3);
row.createCell(4).setCellValue("Jan");
Cell cell5 = row.createCell(5);
cell5.setCellValue(calendar);
cell5.setCellStyle(style5);
row.createCell(6).setCellValue("Jan/01/2010");
Cell cell7 = row.createCell(7);
cell7.setCellFormula("DATE(2010,1,1)");
cell7.setCellStyle(style3); // should be sized as 'Jan'
// autosize not-evaluated cells, formula cells are sized as if the result is 0
for (int i = 0; i < 8; i++) sheet.autoSizeColumn(i);
assertEquals(sheet.getColumnWidth(2), sheet.getColumnWidth(1)); // date formatted as 'm'
assertTrue(sheet.getColumnWidth(3) > sheet.getColumnWidth(1)); // 'mmm' is wider than 'm'
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(3)); // date formatted as 'mmm'
assertTrue(sheet.getColumnWidth(5) > sheet.getColumnWidth(3)); // 'mmm/dd/yyyy' is wider than 'mmm'
assertEquals(sheet.getColumnWidth(6), sheet.getColumnWidth(5)); // date formatted as 'mmm/dd/yyyy'
// YK: width of not-evaluated formulas that return data is not determined
// POI seems to conevert '0' to Excel date which is the beginng of the Excel's date system
// evaluate formulas and re-autosize
evaluateWorkbook(workbook);
for (int i = 0; i < 8; i++) sheet.autoSizeColumn(i);
assertEquals(sheet.getColumnWidth(2), sheet.getColumnWidth(1)); // date formatted as 'm'
assertTrue(sheet.getColumnWidth(3) > sheet.getColumnWidth(1)); // 'mmm' is wider than 'm'
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(3)); // date formatted as 'mmm'
assertTrue(sheet.getColumnWidth(5) > sheet.getColumnWidth(3)); // 'mmm/dd/yyyy' is wider than 'mmm'
assertEquals(sheet.getColumnWidth(6), sheet.getColumnWidth(5)); // date formatted as 'mmm/dd/yyyy'
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(7)); // date formula formatted as 'mmm'
workbook.close();
}
@Test
void stringCells() throws Exception {
Workbook workbook = _testDataProvider.createWorkbook();
Sheet sheet = workbook.createSheet();
trackColumnsForAutoSizingIfSXSSF(sheet);
Row row = sheet.createRow(0);
Font defaultFont = workbook.getFontAt(0);
CellStyle style1 = workbook.createCellStyle();
Font font1 = workbook.createFont();
font1.setFontHeight((short)(2*defaultFont.getFontHeight()));
style1.setFont(font1);
row.createCell(0).setCellValue("x");
row.createCell(1).setCellValue("xxxx");
row.createCell(2).setCellValue("xxxxxxxxxxxx");
row.createCell(3).setCellValue("Apache\nSoftware Foundation"); // the text is splitted into two lines
row.createCell(4).setCellValue("Software Foundation");
Cell cell5 = row.createCell(5);
cell5.setCellValue("Software Foundation");
cell5.setCellStyle(style1); // same as in column 4 but the font is twice larger than the default font
for (int i = 0; i < 10; i++) sheet.autoSizeColumn(i);
assertTrue(2*sheet.getColumnWidth(0) < sheet.getColumnWidth(1)); // width is roughly proportional to the number of characters
assertTrue(2*sheet.getColumnWidth(1) < sheet.getColumnWidth(2));
assertEquals(sheet.getColumnWidth(4), sheet.getColumnWidth(3));
assertTrue(sheet.getColumnWidth(5) > sheet.getColumnWidth(4)); //larger font results in a wider column width
workbook.close();
}
@Test
void rotatedText() throws Exception {
Workbook workbook = _testDataProvider.createWorkbook();
Sheet sheet = workbook.createSheet();
trackColumnsForAutoSizingIfSXSSF(sheet);
Row row = sheet.createRow(0);
CellStyle style1 = workbook.createCellStyle();
style1.setRotation((short)90);
Cell cell0 = row.createCell(0);
cell0.setCellValue("Apache Software Foundation");
cell0.setCellStyle(style1);
Cell cell1 = row.createCell(1);
cell1.setCellValue("Apache Software Foundation");
for (int i = 0; i < 2; i++) sheet.autoSizeColumn(i);
int w0 = sheet.getColumnWidth(0);
int w1 = sheet.getColumnWidth(1);
assertTrue(w0*5 < w1); // rotated text occupies at least five times less horizontal space than normal text
workbook.close();
}
@Test
void mergedCells() throws Exception {
Workbook workbook = _testDataProvider.createWorkbook();
Sheet sheet = workbook.createSheet();
trackColumnsForAutoSizingIfSXSSF(sheet);
Row row = sheet.createRow(0);
assertEquals(0, sheet.addMergedRegion(CellRangeAddress.valueOf("A1:B1")));
Cell cell0 = row.createCell(0);
cell0.setCellValue("Apache Software Foundation");
int defaulWidth = sheet.getColumnWidth(0);
sheet.autoSizeColumn(0);
// column is unchanged if merged regions are ignored (Excel like behavior)
assertEquals(defaulWidth, sheet.getColumnWidth(0));
sheet.autoSizeColumn(0, true);
assertTrue(sheet.getColumnWidth(0) > defaulWidth);
workbook.close();
}
/**
* Auto-Sizing a column needs to work when we have rows
* passed the 32767 boundary. See bug #48079
*/
@Test
void largeRowNumbers() throws Exception {
try (Workbook workbook = _testDataProvider.createWorkbook()) {
boolean isHssf = workbook instanceof HSSFWorkbook;
Sheet sheet = workbook.createSheet();
trackColumnsForAutoSizingIfSXSSF(sheet);
Row r0 = sheet.createRow(0);
r0.createCell(0).setCellValue("I am ROW 0");
Row r200 = sheet.createRow(200);
r200.createCell(0).setCellValue("I am ROW 200");
// This should work fine
sheet.autoSizeColumn(0);
int colWidth1 = sheet.getColumnWidth(0);
// Get close to 32767
Row r32765 = sheet.createRow(32765);
r32765.createCell(0).setCellValue("Nearly there...");
sheet.autoSizeColumn(0);
int colWidth2 = sheet.getColumnWidth(0);
assertTrue(colWidth1 <= colWidth2);
colWidth1 = colWidth2;
// To it
Row r32767 = sheet.createRow(32767);
r32767.createCell(0).setCellValue("At the boundary");
sheet.autoSizeColumn(0);
colWidth2 = sheet.getColumnWidth(0);
assertTrue(colWidth1 <= colWidth2);
colWidth1 = colWidth2;
// And passed it
Row r32768 = sheet.createRow(32768);
r32768.createCell(0).setCellValue("Passed");
Row r32769 = sheet.createRow(32769);
r32769.createCell(0).setCellValue("More Passed");
sheet.autoSizeColumn(0);
colWidth2 = sheet.getColumnWidth(0);
assertTrue(colWidth1 <= colWidth2);
colWidth1 = colWidth2;
// Long way passed
Row r60708 = sheet.createRow(60708);
r60708.createCell(0).setCellValue("Near the end");
sheet.autoSizeColumn(0);
colWidth2 = sheet.getColumnWidth(0);
assertTrue(colWidth1 <= colWidth2);
}
}
// TODO should we have this stuff in the FormulaEvaluator?
private void evaluateWorkbook(Workbook workbook){
FormulaEvaluator eval = workbook.getCreationHelper().createFormulaEvaluator();
for(int i=0; i < workbook.getNumberOfSheets(); i++) {
Sheet sheet = workbook.getSheetAt(i);
for (Row r : sheet) {
for (Cell c : r) {
if (c.getCellType() == CellType.FORMULA){
eval.evaluateFormulaCell(c);
}
}
}
}
}
@Test
void testExcelExporter() throws IOException {
try (final Workbook wb = _testDataProvider.createWorkbook()) {
boolean isHssf = wb instanceof HSSFWorkbook;
final Sheet sheet = wb.createSheet("test");
trackColumnsForAutoSizingIfSXSSF(sheet);
final Row row = sheet.createRow(0);
final Cell cell = row.createCell(0);
CellStyle csDateTime = wb.createCellStyle();
csDateTime.setAlignment(HorizontalAlignment.LEFT);
cell.setCellValue(new Date(Long.parseLong("1439800763994")));
cell.setCellStyle(csDateTime);
sheet.autoSizeColumn(0);
assertTrue(sheet.getColumnWidth(0) > 2500);
}
}
}