Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix row and column ranges after sheet rename #524

Draft
wants to merge 2 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
import org.apache.poi.xddf.usermodel.chart.XDDFChartData;
import org.apache.poi.xssf.XSSFITestDataProvider;
import org.apache.poi.xssf.model.StylesTable;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCalcPr;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTExternalLink;
Expand All @@ -79,6 +80,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.zip.CRC32;

import static org.apache.poi.hssf.HSSFTestDataSamples.openSampleFileStream;
Expand All @@ -100,6 +102,11 @@ public TestXSSFWorkbook() {
super(XSSFITestDataProvider.instance);
}

@BeforeAll
static void setup() {
Locale.setDefault(Locale.US);
}

/**
* Tests that we can save, and then re-load a new document
*/
Expand Down Expand Up @@ -1427,6 +1434,44 @@ void testGithub321() throws Exception {
}
}

@Test
public void changeSheetNameWithRowRanges() throws IOException {
String sampleFile = "multiple_sheets_with_row_and_column_ranges.xlsx";
try (Workbook wb = _testDataProvider.openSampleWorkbook(sampleFile)) {
String initialFormula = wb.getName("sheet2_rows_2_4").getRefersToFormula();

wb.setSheetName(0, "new_first_sheet_name");
String newFormula = wb.getName("sheet2_rows_2_4").getRefersToFormula();
assertEquals(initialFormula, newFormula);

wb.setSheetName(1, "new_second_sheet_name");
newFormula = wb.getName("sheet2_rows_2_4").getRefersToFormula();
assertEquals(initialFormula.replace("sheet2", "new_second_sheet_name"), newFormula);

Workbook reloaded = writeOutAndReadBack(wb);
newFormula = reloaded.getName("sheet2_rows_2_4").getRefersToFormula();
assertEquals(initialFormula.replace("sheet2", "new_second_sheet_name"), newFormula);
}
}
@Test
public void changeSheetNameWithRowRangesColumn() throws IOException {
String sampleFile = "multiple_sheets_with_row_and_column_ranges.xlsx";
try (Workbook wb = _testDataProvider.openSampleWorkbook(sampleFile)) {
String initialFormula = wb.getName("sheet2_columns_B_C").getRefersToFormula();

wb.setSheetName(0, "new_first_sheet_name");
String newFormula = wb.getName("sheet2_columns_B_C").getRefersToFormula();
assertEquals(initialFormula, newFormula);

wb.setSheetName(1, "new_second_sheet_name");
newFormula = wb.getName("sheet2_columns_B_C").getRefersToFormula();
assertEquals(initialFormula.replace("sheet2", "new_second_sheet_name"), newFormula);

Workbook reloaded = writeOutAndReadBack(wb);
newFormula = reloaded.getName("sheet2_columns_B_C").getRefersToFormula();
assertEquals(initialFormula.replace("sheet2", "new_second_sheet_name"), newFormula);
}
}
private static void expectFormattedContent(Cell cell, String value) {
assertEquals(value, new DataFormatter().formatCellValue(cell),
"Cell " + ref(cell) + " has wrong formatted content.");
Expand Down
44 changes: 29 additions & 15 deletions poi/src/main/java/org/apache/poi/ss/formula/ptg/AreaPtgBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI {
private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000);
private static final BitField colRelative = BitFieldFactory.getInstance(0x4000);
private static final BitField columnMask = BitFieldFactory.getInstance(0x3FFF);
private boolean firstCellIsRow;
private boolean firstCellIsColumn;
private boolean lastCellIsRow;
private boolean lastCellIsColumn;

/** zero based, unsigned 16 bit */
private int field_1_first_row;
Expand All @@ -55,15 +59,19 @@ protected AreaPtgBase(AreaPtgBase other) {
field_2_last_row = other.field_2_last_row;
field_3_first_column = other.field_3_first_column;
field_4_last_column = other.field_4_last_column;
firstCellIsRow = other.firstCellIsRow;
firstCellIsColumn = other.firstCellIsColumn;
lastCellIsRow = other.lastCellIsRow;
lastCellIsColumn = other.lastCellIsColumn;
}

protected AreaPtgBase(AreaReference ar) {
CellReference firstCell = ar.getFirstCell();
CellReference lastCell = ar.getLastCell();
setFirstRow(firstCell.getRow());
setFirstColumn(firstCell.getCol() == -1 ? 0 : firstCell.getCol());
setFirstColumn(firstCell.getCol());
setLastRow(lastCell.getRow());
setLastColumn(lastCell.getCol() == -1 ? 0xFF : lastCell.getCol());
setLastColumn(lastCell.getCol());
setFirstColRelative(!firstCell.isColAbsolute());
setLastColRelative(!lastCell.isColAbsolute());
setFirstRowRelative(!firstCell.isRowAbsolute());
Expand Down Expand Up @@ -144,36 +152,38 @@ protected final void writeCoordinates(LittleEndianOutput out) {
* @return the first row in the area
*/
public final int getFirstRow() {
return field_1_first_row;
return firstCellIsRow ? -1 : field_1_first_row;
}

/**
* sets the first row
* @param rowIx number (0-based)
*/
public final void setFirstRow(int rowIx) {
field_1_first_row = rowIx;
firstCellIsRow = rowIx == -1;
field_1_first_row = rowIx== -1 ? 0 : rowIx;
}

/**
* @return last row in the range (x2 in x1,y1-x2,y2)
*/
public final int getLastRow() {
return field_2_last_row;
return lastCellIsRow ? -1 : field_2_last_row;
}

/**
* @param rowIx last row number in the area
*/
public final void setLastRow(int rowIx) {
field_2_last_row = rowIx;
lastCellIsRow = rowIx == -1;
field_2_last_row = rowIx== -1 ? 0 : rowIx;
}

/**
* @return the first column number in the area.
*/
public final int getFirstColumn() {
return columnMask.getValue(field_3_first_column);
return firstCellIsColumn ? -1 : columnMask.getValue(field_3_first_column);
}

/**
Expand Down Expand Up @@ -216,7 +226,8 @@ public final void setFirstColRelative(boolean rel) {
* set the first column in the area
*/
public final void setFirstColumn(int colIx) {
field_3_first_column=columnMask.setValue(field_3_first_column, colIx);
firstCellIsColumn = colIx == -1;
field_3_first_column=columnMask.setValue(field_3_first_column, colIx == -1 ? 0 : colIx);
}

/**
Expand All @@ -230,7 +241,7 @@ public final void setFirstColumnRaw(int column) {
* @return lastcolumn in the area
*/
public final int getLastColumn() {
return columnMask.getValue(field_4_last_column);
return lastCellIsColumn ? -1 : columnMask.getValue(field_4_last_column);
}

/**
Expand Down Expand Up @@ -274,7 +285,8 @@ public final void setLastColRelative(boolean rel) {
* set the last column in the area
*/
public final void setLastColumn(int colIx) {
field_4_last_column=columnMask.setValue(field_4_last_column, colIx);
lastCellIsColumn = colIx == -1;
field_4_last_column=columnMask.setValue(field_4_last_column, colIx == -1 ? 0 : colIx);
}

/**
Expand All @@ -287,11 +299,13 @@ protected final String formatReferenceAsString() {
CellReference topLeft = new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative());
CellReference botRight = new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative());

if(AreaReference.isWholeColumnReference(SpreadsheetVersion.EXCEL97, topLeft, botRight)) {
return (new AreaReference(topLeft, botRight, SpreadsheetVersion.EXCEL97)).formatAsString();
}
if (AreaReference.isWholeColumnReference(SpreadsheetVersion.EXCEL2007, topLeft, botRight)){
return new AreaReference(topLeft, botRight,SpreadsheetVersion.EXCEL2007).formatAsString();
for (SpreadsheetVersion version : SpreadsheetVersion.values()) {
if (AreaReference.isWholeColumnReference(version, topLeft, botRight)) {
return (new AreaReference(topLeft, botRight, version)).formatAsString();
}
if (AreaReference.isWholeRowReference(version, topLeft, botRight)) {
return (new AreaReference(topLeft, botRight, version)).formatAsString();
}
}
return topLeft.formatAsString() + ":" + botRight.formatAsString();
}
Expand Down
49 changes: 42 additions & 7 deletions poi/src/main/java/org/apache/poi/ss/util/AreaReference.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public static AreaReference getWholeRow(SpreadsheetVersion version, String start
if (null == version) {
version = DEFAULT_SPREADSHEET_VERSION;
}
return new AreaReference("$A" + start + ":$" + version.getLastColumnName() + end, version);
return new AreaReference(start + ":" + end, version);
}

/**
Expand Down Expand Up @@ -221,6 +221,24 @@ public static boolean isWholeColumnReference(SpreadsheetVersion version, CellRef
&& botRight.isRowAbsolute());
}

/**
* Is the reference for a whole-row reference,
* such as 2:2 or 3:5 ?
*/
public static boolean isWholeRowReference(SpreadsheetVersion version, CellReference topLeft, CellReference botRight) {
if (null == version) {
version = DEFAULT_SPREADSHEET_VERSION; // how the code used to behave.
}

// These are represented as something like
// C$1:C$65535 or D$1:F$0
// i.e. absolute from 1st row to 0th one
return (topLeft.getCol() == 0
&& topLeft.isColAbsolute()
&& botRight.getCol() == version.getLastColumnIndex()
&& botRight.isColAbsolute());
}

/**
* Takes a non-contiguous area reference, and returns an array of contiguous area references
* @return an array of contiguous area references.
Expand Down Expand Up @@ -346,6 +364,10 @@ public boolean isWholeColumnReference() {
return isWholeColumnReference(_version, _firstCell, _lastCell);
}

public boolean isWholeRowReference() {
return isWholeRowReference(_version, _firstCell, _lastCell);
}

/**
* @return {@code false} if this area reference involves more than one cell
*/
Expand All @@ -358,7 +380,10 @@ public boolean isSingleCell() {
* left corner of the area (but this is not a requirement).
*/
public CellReference getFirstCell() {
return _firstCell;
if (_firstCell.getCol() == -1) {
return new CellReference(_firstCell.getRow(), 0);
}
return _firstCell;
}

/**
Expand All @@ -369,6 +394,9 @@ public CellReference getFirstCell() {
* of the area (but this is not a requirement).
*/
public CellReference getLastCell() {
if (_firstCell.getCol() == -1) {
return new CellReference(_lastCell.getRow(), _version.getLastColumnIndex());
}
return _lastCell;
}

Expand Down Expand Up @@ -414,11 +442,18 @@ public CellReference[] getAllReferencedCells() {
*/
public String formatAsString() {
// Special handling for whole-column references
if(isWholeColumnReference()) {
return
CellReference.convertNumToColString(_firstCell.getCol())
+ ":" +
CellReference.convertNumToColString(_lastCell.getCol());
if (isWholeColumnReference()) {
return _firstCell.formatAsColumnString()
+ ":"
+ _lastCell.formatAsColumnString();
}

if (isWholeRowReference()) {
StringBuilder sb = new StringBuilder();
_firstCell.appendRowReference(sb);
sb.append(":");
_lastCell.appendRowReference(sb);
return sb.toString();
}

StringBuilder sb = new StringBuilder(32);
Expand Down
15 changes: 15 additions & 0 deletions poi/src/main/java/org/apache/poi/ss/util/CellReference.java
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,13 @@ public String formatAsString() {
return formatAsString(true);
}

public String formatAsColumnString() {
StringBuilder sb = new StringBuilder();
appendColumnReference(sb);
return sb.toString();
}


/**
* Returns a text representation of this cell reference in R1C1 format.
* <p>
Expand Down Expand Up @@ -599,12 +606,20 @@ public String[] getCellRefParts() {
* Sheet name is not included.
*/
/* package */ void appendCellReference(StringBuilder sb) {
appendColumnReference(sb);
appendRowReference(sb);
}

public void appendColumnReference(StringBuilder sb) {
if (_colIndex != -1) {
if(_isColAbs) {
sb.append(ABSOLUTE_REFERENCE_MARKER);
}
sb.append( convertNumToColString(_colIndex));
}
}

public void appendRowReference(StringBuilder sb) {
if (_rowIndex != -1) {
if(_isRowAbs) {
sb.append(ABSOLUTE_REFERENCE_MARKER);
Expand Down
4 changes: 2 additions & 2 deletions poi/src/test/java/org/apache/poi/hssf/usermodel/TestBugs.java
Original file line number Diff line number Diff line change
Expand Up @@ -2191,8 +2191,8 @@ void bug52111() throws Exception {
try (Workbook wb = openSampleWorkbook("Intersection-52111.xls")) {
Sheet s = wb.getSheetAt(0);
assertFormula(wb, s.getRow(2).getCell(0), "(C2:D3 D3:E4)", "4.0");
assertFormula(wb, s.getRow(6).getCell(0), "Tabelle2!E:E Tabelle2!$A11:$IV11", "5.0");
assertFormula(wb, s.getRow(8).getCell(0), "Tabelle2!E:F Tabelle2!$A11:$IV12", null);
assertFormula(wb, s.getRow(6).getCell(0), "Tabelle2!E:E Tabelle2!11:11", "5.0");
assertFormula(wb, s.getRow(8).getCell(0), "Tabelle2!E:F Tabelle2!11:12", null);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ void testRepeatingRowsAndColumnsNames() throws Exception {
HSSFName nr1 = wb.getNameAt(0);

assertEquals("Print_Titles", nr1.getNameName());
// TODO - full column references not rendering properly, absolute markers not present either
// assertEquals("FirstSheet!$A:$A,FirstSheet!$1:$3", nr1.getRefersToFormula());
assertEquals("FirstSheet!A:A,FirstSheet!$A$1:$IV$3", nr1.getRefersToFormula());
assertEquals("FirstSheet!$A:$A,FirstSheet!$1:$3", nr1.getRefersToFormula());

// Save and re-open
HSSFWorkbook nwb = HSSFTestDataSamples.writeOutAndReadBack(wb);
Expand All @@ -84,7 +82,7 @@ void testRepeatingRowsAndColumnsNames() throws Exception {
nr1 = nwb.getNameAt(0);

assertEquals("Print_Titles", nr1.getNameName());
assertEquals("FirstSheet!A:A,FirstSheet!$A$1:$IV$3", nr1.getRefersToFormula());
assertEquals("FirstSheet!$A:$A,FirstSheet!$1:$3", nr1.getRefersToFormula());

// check that setting RR&C on a second sheet causes a new Print_Titles built-in
// name to be created
Expand All @@ -97,7 +95,7 @@ void testRepeatingRowsAndColumnsNames() throws Exception {
HSSFName nr2 = nwb.getNameAt(1);

assertEquals("Print_Titles", nr2.getNameName());
assertEquals("SecondSheet!B:C,SecondSheet!$A$1:$IV$1", nr2.getRefersToFormula());
assertEquals("SecondSheet!$B:$C,SecondSheet!$1:$1", nr2.getRefersToFormula());

nwb.close();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,8 +531,8 @@ void findBuiltInNameRecord() throws IOException {
NameRecord nr;
assertEquals(3, wb1.getWorkbook().getNumNames());
nr = wb1.getWorkbook().getNameRecord(2);
// TODO - render full row and full column refs properly
assertEquals("Sheet2!$A$1:$IV$1", HSSFFormulaParser.toFormulaString(wb1, nr.getNameDefinition())); // 1:1

assertEquals("Sheet2!$1:$1", HSSFFormulaParser.toFormulaString(wb1, nr.getNameDefinition())); // 1:1

try {
wb1.getSheetAt(3).setRepeatingRows(CellRangeAddress.valueOf("9:12"));
Expand All @@ -549,7 +549,7 @@ void findBuiltInNameRecord() throws IOException {
wb1.close();
assertEquals(3, wb2.getWorkbook().getNumNames());
nr = wb2.getWorkbook().getNameRecord(2);
assertEquals("Sheet2!E:F,Sheet2!$A$9:$IV$12", HSSFFormulaParser.toFormulaString(wb2, nr.getNameDefinition())); // E:F,9:12
assertEquals("Sheet2!$E:$F,Sheet2!$9:$12", HSSFFormulaParser.toFormulaString(wb2, nr.getNameDefinition())); // E:F,9:12
wb2.close();
}

Expand Down
Binary file not shown.