Browse Source

[github-81] Formula adjusting in context of column shifting. Thanks to Dragan Jovanović. This closes #81

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1822639 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_4_0_0_FINAL
PJ Fanning 6 years ago
parent
commit
445ca472e1
24 changed files with 1083 additions and 116 deletions
  1. 56
    0
      src/java/org/apache/poi/hssf/usermodel/HSSFRow.java
  2. 35
    5
      src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
  3. 1
    1
      src/java/org/apache/poi/hssf/usermodel/helpers/HSSFColumnShifter.java
  4. 2
    0
      src/java/org/apache/poi/hssf/usermodel/helpers/HSSFRowShifter.java
  5. 1
    3
      src/java/org/apache/poi/ss/formula/FormulaShifter.java
  6. 3
    0
      src/java/org/apache/poi/ss/usermodel/Row.java
  7. 11
    0
      src/java/org/apache/poi/ss/usermodel/Sheet.java
  8. 15
    0
      src/java/org/apache/poi/ss/usermodel/helpers/ColumnShifter.java
  9. 5
    5
      src/java/org/apache/poi/ss/usermodel/helpers/RowShifter.java
  10. 12
    0
      src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFRow.java
  11. 6
    0
      src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
  12. 19
    0
      src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java
  13. 68
    18
      src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java
  14. 184
    81
      src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
  15. 1
    1
      src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFVMLDrawing.java
  16. 1
    2
      src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFRowShifter.java
  17. 14
    0
      src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFRow.java
  18. 24
    0
      src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheetShiftColumns.java
  19. 18
    0
      src/ooxml/testcases/org/apache/poi/xssf/usermodel/helpers/TestXSSFColumnShifting.java
  20. 16
    0
      src/testcases/org/apache/poi/hssf/usermodel/TestHSSFColumnShifting.java
  21. 69
    0
      src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheetShiftColumns.java
  22. 69
    0
      src/testcases/org/apache/poi/ss/usermodel/BaseTestColumnShifting.java
  23. 65
    0
      src/testcases/org/apache/poi/ss/usermodel/BaseTestRow.java
  24. 388
    0
      src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetShiftColumns.java

+ 56
- 0
src/java/org/apache/poi/hssf/usermodel/HSSFRow.java View File

@@ -24,11 +24,13 @@ import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.ExtendedFormatRecord;
import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.util.Configurator;
import org.apache.poi.util.NotImplemented;

/**
* High level representation of a row of a spreadsheet.
@@ -715,4 +717,58 @@ public final class HSSFRow implements Row, Comparable<HSSFRow> {
public int hashCode() {
return row.hashCode();
}
/**
* Shifts column range [firstShiftColumnIndex-lastShiftColumnIndex] step places to the right.
* @param startColumn the column to start shifting
* @param endColumn the column to end shifting
* @param step length of the shifting step
*/
@Override
public void shiftCellsRight(int firstShiftColumnIndex, int lastShiftColumnIndex, int step){
if(step < 0)
throw new IllegalArgumentException("Shifting step may not be negative ");
if(firstShiftColumnIndex > lastShiftColumnIndex)
throw new IllegalArgumentException(String.format("Incorrect shifting range : %d-%d", firstShiftColumnIndex, lastShiftColumnIndex));
if(lastShiftColumnIndex + step + 1> cells.length)
extend(lastShiftColumnIndex + step + 1);
for (int columnIndex = lastShiftColumnIndex; columnIndex >= firstShiftColumnIndex; columnIndex--){ // process cells backwards, because of shifting
HSSFCell cell = getCell(columnIndex);
cells[columnIndex+step] = null;
if(cell != null)
moveCell(cell, (short)(columnIndex+step));
}
for (int columnIndex = firstShiftColumnIndex; columnIndex <= firstShiftColumnIndex+step-1; columnIndex++)
cells[columnIndex] = null;
}
private void extend(int newLenght){
HSSFCell[] temp = cells.clone();
cells = new HSSFCell[newLenght];
System.arraycopy(temp, 0, cells, 0, temp.length);
}
/**
* Shifts column range [firstShiftColumnIndex-lastShiftColumnIndex] step places to the left.
* @param startColumn the column to start shifting
* @param endColumn the column to end shifting
* @param step length of the shifting step
*/
@Override
public void shiftCellsLeft(int firstShiftColumnIndex, int lastShiftColumnIndex, int step){
if(step < 0)
throw new IllegalArgumentException("Shifting step may not be negative ");
if(firstShiftColumnIndex > lastShiftColumnIndex)
throw new IllegalArgumentException(String.format("Incorrect shifting range : %d-%d", firstShiftColumnIndex, lastShiftColumnIndex));
if(firstShiftColumnIndex - step < 0)
throw new IllegalStateException("Column index less than zero : " + (Integer.valueOf(firstShiftColumnIndex + step)).toString());
for (int columnIndex = firstShiftColumnIndex; columnIndex <= lastShiftColumnIndex; columnIndex++){
HSSFCell cell = getCell(columnIndex);
if(cell != null){
cells[columnIndex-step] = null;
moveCell(cell, (short)(columnIndex-step));
}
else cells[columnIndex-step] = null;
}
for (int columnIndex = lastShiftColumnIndex-step+1; columnIndex <= lastShiftColumnIndex; columnIndex++)
cells[columnIndex] = null;
}
}

+ 35
- 5
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java View File

@@ -50,6 +50,8 @@ import org.apache.poi.hssf.record.aggregates.DataValidityTable;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
import org.apache.poi.hssf.record.aggregates.WorksheetProtectionBlock;
import org.apache.poi.hssf.usermodel.helpers.HSSFColumnShifter;

import org.apache.poi.hssf.usermodel.helpers.HSSFRowShifter;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.FormulaShifter;
@@ -73,6 +75,7 @@ import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.PaneInformation;
import org.apache.poi.ss.util.SSCellRange;
import org.apache.poi.ss.util.SheetUtil;
import org.apache.poi.util.Beta;
import org.apache.poi.util.Configurator;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@@ -1647,16 +1650,18 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
// Re-compute the first and last rows of the sheet as needed
recomputeFirstAndLastRowsForRowShift(startRow, endRow, n);

int sheetIndex = _workbook.getSheetIndex(this);
short externSheetIndex = _book.checkExternSheet(sheetIndex);
String sheetName = _workbook.getSheetName(sheetIndex);
FormulaShifter formulaShifter = FormulaShifter.createForRowShift(
externSheetIndex, sheetName, startRow, endRow, n, SpreadsheetVersion.EXCEL97);
// Update formulas that refer to rows that have been moved
updateFormulasForRowShift(startRow, endRow, n);
updateFormulasForShift(formulaShifter);
}

private void updateFormulasForRowShift(int startRow, int endRow, int n) {
private void updateFormulasForShift(FormulaShifter formulaShifter) {
int sheetIndex = _workbook.getSheetIndex(this);
String sheetName = _workbook.getSheetName(sheetIndex);
short externSheetIndex = _book.checkExternSheet(sheetIndex);
FormulaShifter formulaShifter = FormulaShifter.createForRowShift(
externSheetIndex, sheetName, startRow, endRow, n, SpreadsheetVersion.EXCEL97);
// update formulas on this sheet that point to rows which have been moved
_sheet.updateFormulasAfterCellShift(formulaShifter, externSheetIndex);

@@ -1737,6 +1742,31 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
}
}
}
/**
* Shifts columns in range [startColumn, endColumn] for n places to the right.
* For n < 0, it will shift columns left.
* Additionally adjusts formulas.
* Probably should also process other features (hyperlinks, comments...) in the way analog to shiftRows method
* @param startRow the row to start shifting
* @param endRow the row to end shifting
* @param n the number of rows to shift
*/

@Beta
@Override
public void shiftColumns(int startColumn, int endColumn, int n){
HSSFColumnShifter columnShifter = new HSSFColumnShifter(this);
columnShifter.shiftColumns(startColumn, endColumn, n);
int sheetIndex = _workbook.getSheetIndex(this);
short externSheetIndex = _book.checkExternSheet(sheetIndex);
String sheetName = _workbook.getSheetName(sheetIndex);
FormulaShifter formulaShifter = FormulaShifter.createForColumnShift(
externSheetIndex, sheetName, startColumn, endColumn, n, SpreadsheetVersion.EXCEL97);
updateFormulasForShift(formulaShifter);
// add logic for hyperlinks etc, like in shiftRows()
}

protected void insertChartRecords(List<Record> records) {
int window2Loc = _sheet.findFirstRecordLocBySid(WindowTwoRecord.sid);

+ 1
- 1
src/java/org/apache/poi/hssf/usermodel/helpers/HSSFColumnShifter.java View File

@@ -65,4 +65,4 @@ public final class HSSFColumnShifter extends ColumnShifter {
throw new NotImplementedException("updateHyperlinks");
}

}
}

+ 2
- 0
src/java/org/apache/poi/hssf/usermodel/helpers/HSSFRowShifter.java View File

@@ -20,6 +20,8 @@ package org.apache.poi.hssf.usermodel.helpers;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.ss.formula.FormulaShifter;
import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.helpers.RowShifter;
import org.apache.poi.util.NotImplemented;
import org.apache.poi.util.POILogFactory;

+ 1
- 3
src/java/org/apache/poi/ss/formula/FormulaShifter.java View File

@@ -5,9 +5,7 @@
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.
@@ -838,4 +836,4 @@ public final class FormulaShifter {

throw new IllegalArgumentException("Unexpected ref ptg class (" + ptg.getClass().getName() + ")");
}
}
}

+ 3
- 0
src/java/org/apache/poi/ss/usermodel/Row.java View File

@@ -234,4 +234,7 @@ public interface Row extends Iterable<Cell> {
* you take it out of them.
*/
public int getOutlineLevel();
public void shiftCellsRight(int firstShiftColumnIndex, int lastShiftColumnIndex, int step);
public void shiftCellsLeft(int firstShiftColumnIndex, int lastShiftColumnIndex, int step);
}

+ 11
- 0
src/java/org/apache/poi/ss/usermodel/Sheet.java View File

@@ -703,6 +703,17 @@ public interface Sheet extends Iterable<Row> {
*/
void shiftRows(int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight);

/**
* Shifts columns between startColumn and endColumn, n number of columns.
* If you use a negative number, it will shift columns left.
* Code ensures that columns don't wrap around
*
* @param startColumn the column to start shifting
* @param endColumn the column to end shifting
* @param n the number of columns to shift
*/
void shiftColumns(int startColumn, int endColumn, int n);

/**
* Creates a split (freezepane). Any existing freezepane or split pane is overwritten.
* <p>

+ 15
- 0
src/java/org/apache/poi/ss/usermodel/helpers/ColumnShifter.java View File

@@ -22,6 +22,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.Beta;
@@ -119,4 +120,18 @@ public abstract class ColumnShifter extends BaseRowColShifter {
// if the merged-region and the overwritten area intersect, we need to remove it
return merged.intersects(overwrite);
}

public void shiftColumns(int firstShiftColumnIndex, int lastShiftColumnIndex, int step){
if(step > 0){
for (Row row : sheet)
if(row != null)
row.shiftCellsRight(firstShiftColumnIndex, lastShiftColumnIndex, step);
}
else if(step < 0){
for (Row row : sheet)
if(row != null)
row.shiftCellsLeft(firstShiftColumnIndex, lastShiftColumnIndex, -step);
}
//else step == 0 => nothing to shift
}
}

+ 5
- 5
src/java/org/apache/poi/ss/usermodel/helpers/RowShifter.java View File

@@ -35,11 +35,11 @@ import org.apache.poi.ss.util.CellRangeAddress;
public abstract class RowShifter extends BaseRowColShifter {
protected final Sheet sheet;

public RowShifter(Sheet sh) {
sheet = sh;
}
/**
public RowShifter(Sheet sh) {
sheet = sh;
}
/**
* Shifts, grows, or shrinks the merged regions due to a row shift.
* Merged regions that are completely overlaid by shifting will be deleted.
*

+ 12
- 0
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFRow.java View File

@@ -24,12 +24,14 @@ import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.util.Internal;
import org.apache.poi.util.NotImplemented;

/**
* Streaming version of XSSFRow implementing the "BigGridDemo" strategy.
@@ -545,6 +547,16 @@ public class SXSSFRow implements Row, Comparable<SXSSFRow>
return _cells.hashCode();
}

@Override
@NotImplemented
public void shiftCellsRight(int firstShiftColumnIndex, int lastShiftColumnIndex, int step){
throw new NotImplementedException("shiftCellsRight");
}
@Override
@NotImplemented
public void shiftCellsLeft(int firstShiftColumnIndex, int lastShiftColumnIndex, int step){
throw new NotImplementedException("shiftCellsLeft");
}

}


+ 6
- 0
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFSheet.java View File

@@ -2119,4 +2119,10 @@ public class SXSSFSheet implements Sheet
color.setIndexed(colorIndex);
pr.setTabColor(color);
}
@NotImplemented
@Override
public void shiftColumns(int startColumn, int endColumn, int n){
throw new UnsupportedOperationException("NotImplemented");
}
}

+ 19
- 0
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java View File

@@ -49,6 +49,7 @@ import org.apache.poi.util.LocaleUtil;
import org.apache.poi.util.Removal;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.model.CalculationChain;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType;
@@ -1324,4 +1325,22 @@ public final class XSSFCell implements Cell {
"You cannot change part of an array.";
notifyArrayFormulaChanging(msg);
}
//Moved from XSSFRow.shift(). Not sure what is purpose.
public void updateCellReferencesForShifting(String msg){
if(isPartOfArrayFormulaGroup())
notifyArrayFormulaChanging(msg);
CalculationChain calcChain = getSheet().getWorkbook().getCalculationChain();
int sheetId = (int)getSheet().sheet.getSheetId();
//remove the reference in the calculation chain
if(calcChain != null) calcChain.removeItem(sheetId, getReference());
CTCell ctCell = getCTCell();
String r = new CellReference(getRowIndex(), getColumnIndex()).formatAsString();
ctCell.setR(r);
}
}

+ 68
- 18
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java View File

@@ -30,10 +30,8 @@ import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.Beta;
import org.apache.poi.util.Internal;
import org.apache.poi.xssf.model.CalculationChain;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;
@@ -227,7 +225,6 @@ public class XSSFRow implements Row, Comparable<XSSFRow> {
_cells.put(colI, xcell);
return xcell;
}

/**
* Returns the cell at the given (0 based) index,
* with the {@link org.apache.poi.ss.usermodel.Row.MissingCellPolicy} from the parent Workbook.
@@ -554,23 +551,11 @@ public class XSSFRow implements Row, Comparable<XSSFRow> {
*/
protected void shift(int n) {
int rownum = getRowNum() + n;
CalculationChain calcChain = _sheet.getWorkbook().getCalculationChain();
int sheetId = (int)_sheet.sheet.getSheetId();
String msg = "Row[rownum="+getRowNum()+"] contains cell(s) included in a multi-cell array formula. " +
"You cannot change part of an array.";
for(Cell c : this){
XSSFCell cell = (XSSFCell)c;
if(cell.isPartOfArrayFormulaGroup()){
cell.notifyArrayFormulaChanging(msg);
}

//remove the reference in the calculation chain
if(calcChain != null) calcChain.removeItem(sheetId, cell.getReference());

CTCell ctCell = cell.getCTCell();
String r = new CellReference(rownum, cell.getColumnIndex()).formatAsString();
ctCell.setR(r);
}
((XSSFCell)c).updateCellReferencesForShifting(msg);
}
setRowNum(rownum);
}
@@ -620,13 +605,14 @@ public class XSSFRow implements Row, Comparable<XSSFRow> {
destCell.copyCellFrom(srcCell, policy);
}

final XSSFRowShifter rowShifter = new XSSFRowShifter(_sheet);
final int sheetIndex = _sheet.getWorkbook().getSheetIndex(_sheet);
final String sheetName = _sheet.getWorkbook().getSheetName(sheetIndex);
final int srcRowNum = srcRow.getRowNum();
final int destRowNum = getRowNum();
final int rowDifference = destRowNum - srcRowNum;
final FormulaShifter formulaShifter = FormulaShifter.createForRowCopy(sheetIndex, sheetName, srcRowNum, srcRowNum, rowDifference, SpreadsheetVersion.EXCEL2007);
final XSSFRowShifter rowShifter = new XSSFRowShifter(_sheet);
rowShifter.updateRowFormulas(this, formulaShifter);

// Copy merged regions that are fully contained on the row
@@ -652,4 +638,68 @@ public class XSSFRow implements Row, Comparable<XSSFRow> {
public int getOutlineLevel() {
return _row.getOutlineLevel();
}
/**
* Shifts column range [firstShiftColumnIndex-lastShiftColumnIndex] step places to the right.
* @param startColumn the column to start shifting
* @param endColumn the column to end shifting
* @param step length of the shifting step
*/
@Override
public void shiftCellsRight(int firstShiftColumnIndex, int lastShiftColumnIndex, int step){
if(step < 0)
throw new IllegalArgumentException("Shifting step may not be negative ");
if(firstShiftColumnIndex > lastShiftColumnIndex)
throw new IllegalArgumentException(String.format("Incorrect shifting range : %d-%d", firstShiftColumnIndex, lastShiftColumnIndex));
for (int columnIndex = lastShiftColumnIndex; columnIndex >= firstShiftColumnIndex; columnIndex--){ // process cells backwards, because of shifting
shiftCell(columnIndex, step);
}
for (int columnIndex = firstShiftColumnIndex; columnIndex <= firstShiftColumnIndex+step-1; columnIndex++)
{
_cells.remove(columnIndex);
XSSFCell targetCell = getCell(columnIndex);
if(targetCell != null)
targetCell.getCTCell().set(CTCell.Factory.newInstance());
}
}
/**
* Shifts column range [firstShiftColumnIndex-lastShiftColumnIndex] step places to the left.
* @param startColumn the column to start shifting
* @param endColumn the column to end shifting
* @param step length of the shifting step
*/
@Override
public void shiftCellsLeft(int firstShiftColumnIndex, int lastShiftColumnIndex, int step){
if(step < 0)
throw new IllegalArgumentException("Shifting step may not be negative ");
if(firstShiftColumnIndex > lastShiftColumnIndex)
throw new IllegalArgumentException(String.format("Incorrect shifting range : %d-%d", firstShiftColumnIndex, lastShiftColumnIndex));
if(firstShiftColumnIndex - step < 0)
throw new IllegalStateException("Column index less than zero : " + (Integer.valueOf(firstShiftColumnIndex + step)).toString());
for (int columnIndex = firstShiftColumnIndex; columnIndex <= lastShiftColumnIndex; columnIndex++){
shiftCell(columnIndex, -step);
}
for (int columnIndex = lastShiftColumnIndex-step+1; columnIndex <= lastShiftColumnIndex; columnIndex++){
_cells.remove(columnIndex);
XSSFCell targetCell = getCell(columnIndex);
if(targetCell != null)
targetCell.getCTCell().set(CTCell.Factory.newInstance());
}
}
private void shiftCell(int columnIndex, int step/*pass negative value for left shift*/){
if(columnIndex + step < 0) // only for shifting left
throw new IllegalStateException("Column index less than zero : " + (Integer.valueOf(columnIndex + step)).toString());
XSSFCell currentCell = getCell(columnIndex);
if(currentCell != null){
currentCell.setCellNum(columnIndex+step);
_cells.put(columnIndex+step, currentCell);
}
else {
_cells.remove(columnIndex+step);
XSSFCell targetCell = getCell(columnIndex+step);
if(targetCell != null)
targetCell.getCTCell().set(CTCell.Factory.newInstance());
}
}
}

+ 184
- 81
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java View File

@@ -84,6 +84,7 @@ import org.apache.poi.util.Units;
import org.apache.poi.xssf.model.CommentsTable;
import org.apache.poi.xssf.usermodel.XSSFPivotTable.PivotTableReferenceConfigurator;
import org.apache.poi.xssf.usermodel.helpers.ColumnHelper;
import org.apache.poi.xssf.usermodel.helpers.XSSFColumnShifter;
import org.apache.poi.xssf.usermodel.helpers.XSSFIgnoredErrorHelper;
import org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter;
import org.apache.xmlbeans.XmlCursor;
@@ -2985,7 +2986,65 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
public void shiftRows(int startRow, int endRow, final int n, boolean copyRowHeight, boolean resetOriginalRowHeight) {
XSSFVMLDrawing vml = getVMLDrawing(false);

// first remove all rows which will be overwritten
int sheetIndex = getWorkbook().getSheetIndex(this);
String sheetName = getWorkbook().getSheetName(sheetIndex);
FormulaShifter formulaShifter = FormulaShifter.createForRowShift(
sheetIndex, sheetName, startRow, endRow, n, SpreadsheetVersion.EXCEL2007);
removeOverwritten(vml, startRow, endRow, n);
shiftCommentsAndRows(vml, startRow, endRow, n);
XSSFRowShifter rowShifter = new XSSFRowShifter(this);
rowShifter.shiftMergedRegions(startRow, endRow, n);
rowShifter.updateNamedRanges(formulaShifter);
rowShifter.updateFormulas(formulaShifter);
rowShifter.updateConditionalFormatting(formulaShifter);
rowShifter.updateHyperlinks(formulaShifter);

//rebuild the _rows map
Map<Integer, XSSFRow> map = new HashMap<>();
for(XSSFRow r : _rows.values()) {
// Performance optimization: explicit boxing is slightly faster than auto-unboxing, though may use more memory
final Integer rownumI = new Integer(r.getRowNum()); // NOSONAR
map.put(rownumI, r);
}
_rows.clear();
_rows.putAll(map);
}
/**
* Shifts columns between startColumn and endColumn n number of columns.
* If you use a negative number, it will shift columns left.
* Code ensures that columns don't wrap around
*
* @param startColumn the column to start shifting
* @param endColumn the column to end shifting
* @param n length of the shifting step
*/
@Override
public void shiftColumns(int startColumn, int endColumn, final int n) {
XSSFVMLDrawing vml = getVMLDrawing(false);
shiftCommentsForColumns(vml, startColumn, endColumn, n);
FormulaShifter formulaShifter = FormulaShifter.createForColumnShift(this.getWorkbook().getSheetIndex(this), this.getSheetName(), startColumn, endColumn, n, SpreadsheetVersion.EXCEL2007);
XSSFColumnShifter columnShifter = new XSSFColumnShifter(this);
columnShifter.shiftColumns(startColumn, endColumn, n);
columnShifter.shiftMergedRegions(startColumn, startColumn, n);
columnShifter.updateFormulas(formulaShifter);
columnShifter.updateConditionalFormatting(formulaShifter);
columnShifter.updateHyperlinks(formulaShifter);
columnShifter.updateNamedRanges(formulaShifter);

//rebuild the _rows map
Map<Integer, XSSFRow> map = new HashMap<>();
for(XSSFRow r : _rows.values()) {
final Integer rownumI = new Integer(r.getRowNum()); // NOSONAR
map.put(rownumI, r);
}
_rows.clear();
_rows.putAll(map);
}
// remove all rows which will be overwritten
private void removeOverwritten(XSSFVMLDrawing vml, int startRow, int endRow, final int n){
for (Iterator<Row> it = rowIterator() ; it.hasNext() ; ) {
XSSFRow row = (XSSFRow)it.next();
int rownum = row.getRowNum();
@@ -3029,16 +3088,123 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
}
}

}

private void shiftCommentsAndRows(XSSFVMLDrawing vml, int startRow, int endRow, final int n){
// then do the actual moving and also adjust comments/rowHeight
// we need to sort it in a way so the shifting does not mess up the structures,
// i.e. when shifting down, start from down and go up, when shifting up, vice-versa
SortedMap<XSSFComment, Integer> commentsToShift = new TreeMap<>(new Comparator<XSSFComment>() {
@Override
public int compare(XSSFComment o1, XSSFComment o2) {
int row1 = o1.getRow();
int row2 = o2.getRow();

if (row1 == row2) {
// ordering is not important when row is equal, but don't return zero to still
// get multiple comments per row into the map
return o1.hashCode() - o2.hashCode();
}

// when shifting down, sort higher row-values first
if (n > 0) {
return row1 < row2 ? 1 : -1;
} else {
// sort lower-row values first when shifting up
return row1 > row2 ? 1 : -1;
}
}
});

for (Iterator<Row> it = rowIterator() ; it.hasNext() ; ) {
XSSFRow row = (XSSFRow)it.next();
int rownum = row.getRowNum();

if(sheetComments != null){
// calculate the new rownum
int newrownum = shiftedRowNum(startRow, endRow, n, rownum);
// is there a change necessary for the current row?
if(newrownum != rownum) {
CTCommentList lst = sheetComments.getCTComments().getCommentList();
for (CTComment comment : lst.getCommentArray()) {
String oldRef = comment.getRef();
CellReference ref = new CellReference(oldRef);
// is this comment part of the current row?
if(ref.getRow() == rownum) {
XSSFComment xssfComment = new XSSFComment(sheetComments, comment,
vml == null ? null : vml.findCommentShape(rownum, ref.getCol()));
// we should not perform the shifting right here as we would then find
// already shifted comments and would shift them again...
commentsToShift.put(xssfComment, newrownum);
}
}
}
}

if(rownum < startRow || rownum > endRow) {
continue;
}
row.shift(n);
}
// adjust all the affected comment-structures now
// the Map is sorted and thus provides them in the order that we need here,
// i.e. from down to up if shifting down, vice-versa otherwise
for(Map.Entry<XSSFComment, Integer> entry : commentsToShift.entrySet()) {
entry.getKey().setRow(entry.getValue());
}
//rebuild the _rows map
Map<Integer, XSSFRow> map = new HashMap<>();
for(XSSFRow r : _rows.values()) {
// Performance optimization: explicit boxing is slightly faster than auto-unboxing, though may use more memory
final Integer rownumI = Integer.valueOf(r.getRowNum()); // NOSONAR
map.put(rownumI, r);
}
_rows.clear();
_rows.putAll(map);
}
private int shiftedRowNum(int startRow, int endRow, int n, int rownum) {
// no change if before any affected row
if(rownum < startRow && (n > 0 || (startRow - rownum) > n)) {
return rownum;
}
// no change if after any affected row
if(rownum > endRow && (n < 0 || (rownum - endRow) > n)) {
return rownum;
}
// row before and things are moved up
if(rownum < startRow) {
// row is moved down by the shifting
return rownum + (endRow - startRow);
}
// row is after and things are moved down
if(rownum > endRow) {
// row is moved up by the shifting
return rownum - (endRow - startRow);
}
// row is part of the shifted block
return rownum + n;
}
private void shiftCommentsForColumns(XSSFVMLDrawing vml, int startColumnIndex, int endColumnIndex, final int n){
// then do the actual moving and also adjust comments/rowHeight
// we need to sort it in a way so the shifting does not mess up the structures,
// i.e. when shifting down, start from down and go up, when shifting up, vice-versa
SortedMap<XSSFComment, Integer> commentsToShift = new TreeMap<>(new Comparator<XSSFComment>() {
@Override
public int compare(XSSFComment o1, XSSFComment o2) {
int row1 = o1.getRow();
int row2 = o2.getRow();
int column1 = o1.getColumn();
int column2 = o2.getColumn();

if (row1 == row2) {
if (column1 == column2) {
// ordering is not important when row is equal, but don't return zero to still
// get multiple comments per row into the map
return o1.hashCode() - o2.hashCode();
@@ -3046,74 +3212,37 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {

// when shifting down, sort higher row-values first
if (n > 0) {
return row1 < row2 ? 1 : -1;
return column1 < column2 ? 1 : -1;
} else {
// sort lower-row values first when shifting up
return row1 > row2 ? 1 : -1;
return column1 > column2 ? 1 : -1;
}
}
});

for (Iterator<Row> it = rowIterator() ; it.hasNext() ; ) {
XSSFRow row = (XSSFRow)it.next();
int rownum = row.getRowNum();

if(sheetComments != null){
// calculate the new rownum
int newrownum = shiftedRowNum(startRow, endRow, n, rownum);
if(sheetComments != null){
CTCommentList lst = sheetComments.getCTComments().getCommentList();
for (CTComment comment : lst.getCommentArray()) {
String oldRef = comment.getRef();
CellReference ref = new CellReference(oldRef);
// is there a change necessary for the current row?
if(newrownum != rownum) {
CTCommentList lst = sheetComments.getCTComments().getCommentList();
for (CTComment comment : lst.getCommentArray()) {
String oldRef = comment.getRef();
CellReference ref = new CellReference(oldRef);
// is this comment part of the current row?
if(ref.getRow() == rownum) {
XSSFComment xssfComment = new XSSFComment(sheetComments, comment,
vml == null ? null : vml.findCommentShape(rownum, ref.getCol()));
// we should not perform the shifting right here as we would then find
// already shifted comments and would shift them again...
commentsToShift.put(xssfComment, newrownum);
}
}
int columnIndex =ref.getCol();
int newColumnIndex = shiftedRowNum(startColumnIndex, endColumnIndex, n, columnIndex);
if(newColumnIndex != columnIndex){
XSSFComment xssfComment = new XSSFComment(sheetComments, comment,
vml == null ? null : vml.findCommentShape(ref.getRow(), columnIndex));
commentsToShift.put(xssfComment, newColumnIndex);
}
}

if(rownum < startRow || rownum > endRow) {
continue;
}

if (!copyRowHeight) {
row.setHeight((short)-1);
}

row.shift(n);
}
// adjust all the affected comment-structures now
// the Map is sorted and thus provides them in the order that we need here,
// i.e. from down to up if shifting down, vice-versa otherwise
for(Map.Entry<XSSFComment, Integer> entry : commentsToShift.entrySet()) {
entry.getKey().setRow(entry.getValue());
entry.getKey().setColumn(entry.getValue());
}
XSSFRowShifter rowShifter = new XSSFRowShifter(this);

int sheetIndex = getWorkbook().getSheetIndex(this);
String sheetName = getWorkbook().getSheetName(sheetIndex);
FormulaShifter shifter = FormulaShifter.createForRowShift(
sheetIndex, sheetName, startRow, endRow, n, SpreadsheetVersion.EXCEL2007);

rowShifter.updateNamedRanges(shifter);
rowShifter.updateFormulas(shifter);
rowShifter.shiftMergedRegions(startRow, endRow, n);
rowShifter.updateConditionalFormatting(shifter);
rowShifter.updateHyperlinks(shifter);

//rebuild the _rows map
Map<Integer, XSSFRow> map = new HashMap<>();
for(XSSFRow r : _rows.values()) {
@@ -3123,33 +3252,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
}
_rows.clear();
_rows.putAll(map);
}

private int shiftedRowNum(int startRow, int endRow, int n, int rownum) {
// no change if before any affected row
if(rownum < startRow && (n > 0 || (startRow - rownum) > n)) {
return rownum;
}
// no change if after any affected row
if(rownum > endRow && (n < 0 || (rownum - endRow) > n)) {
return rownum;
}
// row before and things are moved up
if(rownum < startRow) {
// row is moved down by the shifting
return rownum + (endRow - startRow);
}
// row is after and things are moved down
if(rownum > endRow) {
// row is moved up by the shifting
return rownum - (endRow - startRow);
}
// row is part of the shifted block
return rownum + n;
}

/**

+ 1
- 1
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFVMLDrawing.java View File

@@ -264,7 +264,7 @@ public final class XSSFVMLDrawing extends POIXMLDocumentPart {
*
* @return the comment shape or <code>null</code>
*/
protected CTShape findCommentShape(int row, int col){
public CTShape findCommentShape(int row, int col){
for(XmlObject itm : _items){
if(itm instanceof CTShape){
CTShape sh = (CTShape)itm;

+ 1
- 2
src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFRowShifter.java View File

@@ -36,7 +36,6 @@ public final class XSSFRowShifter extends RowShifter {
public XSSFRowShifter(XSSFSheet sh) {
super(sh);
}

@Override
public void updateNamedRanges(FormulaShifter formulaShifter) {
XSSFRowColShifter.updateNamedRanges(sheet, formulaShifter);
@@ -46,7 +45,7 @@ public final class XSSFRowShifter extends RowShifter {
public void updateFormulas(FormulaShifter formulaShifter) {
XSSFRowColShifter.updateFormulas(sheet, formulaShifter);
}
/**
* Update the formulas in specified row using the formula shifting policy specified by shifter
*

+ 14
- 0
src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFRow.java View File

@@ -22,6 +22,8 @@ package org.apache.poi.xssf.streaming;
import org.apache.poi.ss.usermodel.BaseTestXRow;
import org.apache.poi.xssf.SXSSFITestDataProvider;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;

/**
* Tests for XSSFRow
@@ -38,4 +40,16 @@ public final class TestSXSSFRow extends BaseTestXRow {
((SXSSFITestDataProvider) _testDataProvider).cleanup();
}

@Override
@Ignore ("see <https://bz.apache.org/bugzilla/show_bug.cgi?id=62030#c1>") @Test
public void testCellShiftingRight(){
// Remove when SXSSFRow.shiftCellsRight() is implemented.
}
@Override
@Ignore ("see <https://bz.apache.org/bugzilla/show_bug.cgi?id=62030#c1>") @Test
public void testCellShiftingLeft(){
// Remove when SXSSFRow.shiftCellsLeft() is implemented.
}

}

+ 24
- 0
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheetShiftColumns.java View File

@@ -0,0 +1,24 @@
package org.apache.poi.xssf.usermodel;

import java.io.IOException;

import org.apache.poi.ss.usermodel.BaseTestSheetShiftColumns;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.XSSFITestDataProvider;
import org.apache.poi.xssf.XSSFTestDataSamples;

public class TestXSSFSheetShiftColumns extends BaseTestSheetShiftColumns {
public TestXSSFSheetShiftColumns(){
super();
workbook = new XSSFWorkbook();
_testDataProvider = XSSFITestDataProvider.instance;
}

protected Workbook openWorkbook(String spreadsheetFileName) throws IOException{
return XSSFTestDataSamples.openSampleWorkbook(spreadsheetFileName);
}
protected Workbook getReadBackWorkbook(Workbook wb){
return XSSFTestDataSamples.writeOutAndReadBack(wb);
}

}

+ 18
- 0
src/ooxml/testcases/org/apache/poi/xssf/usermodel/helpers/TestXSSFColumnShifting.java View File

@@ -0,0 +1,18 @@
package org.apache.poi.xssf.usermodel.helpers;

import org.apache.poi.ss.usermodel.BaseTestColumnShifting;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class TestXSSFColumnShifting extends BaseTestColumnShifting{
public TestXSSFColumnShifting(){
super();
wb = new XSSFWorkbook();
}
@Override
protected void initColumnShifter(){
columnShifter = new XSSFColumnShifter((XSSFSheet)sheet1);
}


}

+ 16
- 0
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFColumnShifting.java View File

@@ -0,0 +1,16 @@
package org.apache.poi.hssf.usermodel;

import org.apache.poi.hssf.usermodel.helpers.HSSFColumnShifter;
import org.apache.poi.ss.usermodel.BaseTestColumnShifting;

public class TestHSSFColumnShifting extends BaseTestColumnShifting {
public TestHSSFColumnShifting(){
super();
wb = new HSSFWorkbook();
}
@Override
protected void initColumnShifter(){
columnShifter = new HSSFColumnShifter((HSSFSheet)sheet1);
}

}

+ 69
- 0
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheetShiftColumns.java View File

@@ -0,0 +1,69 @@
package org.apache.poi.hssf.usermodel;

import java.io.IOException;

import org.apache.poi.hssf.HSSFITestDataProvider;
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.ss.usermodel.BaseTestSheetShiftColumns;
import org.apache.poi.ss.usermodel.Workbook;
import org.junit.Ignore;
import org.junit.Test;

public class TestHSSFSheetShiftColumns extends BaseTestSheetShiftColumns {
public TestHSSFSheetShiftColumns(){
super();
workbook = new HSSFWorkbook();
_testDataProvider = HSSFITestDataProvider.instance;
}

protected Workbook openWorkbook(String spreadsheetFileName)
throws IOException {
return HSSFTestDataSamples.openSampleWorkbook(spreadsheetFileName);
}

protected Workbook getReadBackWorkbook(Workbook wb) throws IOException {
return HSSFTestDataSamples.writeOutAndReadBack((HSSFWorkbook)wb);
}
@Override
@Ignore("see <https://bz.apache.org/bugzilla/show_bug.cgi?id=62030>")
@Test
public void shiftMergedColumnsToMergedColumnsLeft() throws IOException {
// This override is used only in order to test failing for HSSF. Please remove method after code is fixed on hssf,
// so that original method from BaseTestSheetShiftColumns can be executed.
}
@Override
@Ignore("see <https://bz.apache.org/bugzilla/show_bug.cgi?id=62030>")
@Test
public void shiftMergedColumnsToMergedColumnsRight() throws IOException {
// This override is used only in order to test failing for HSSF. Please remove method after code is fixed on hssf,
// so that original method from BaseTestSheetShiftColumns can be executed.
}
@Override
@Ignore("see <https://bz.apache.org/bugzilla/show_bug.cgi?id=62030>")
@Test
public void testBug54524() throws IOException {
// This override is used only in order to test failing for HSSF. Please remove method after code is fixed on hssf,
// so that original method from BaseTestSheetShiftColumns can be executed.
}
@Override
@Ignore("see <https://bz.apache.org/bugzilla/show_bug.cgi?id=62030>")
@Test
public void testCommentsShifting() throws IOException {
// This override is used only in order to test failing for HSSF. Please remove method after code is fixed on hssf,
// so that original method from BaseTestSheetShiftColumns can be executed.
}
@Override
@Ignore("see <https://bz.apache.org/bugzilla/show_bug.cgi?id=62030>")
@Test
public void testShiftWithMergedRegions() throws IOException {
// This override is used only in order to test failing for HSSF. Please remove method after code is fixed on hssf,
// so that original method from BaseTestSheetShiftColumns can be executed.
// After removing, you can re-add 'final' keyword to specification of original method.
}

@Override
@Ignore("see <https://bz.apache.org/bugzilla/show_bug.cgi?id=62030>")
@Test
public void testShiftHyperlinks() throws IOException {}
}

+ 69
- 0
src/testcases/org/apache/poi/ss/usermodel/BaseTestColumnShifting.java View File

@@ -0,0 +1,69 @@
package org.apache.poi.ss.usermodel;

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import org.apache.poi.ss.usermodel.helpers.ColumnShifter;

public class BaseTestColumnShifting {
protected Workbook wb;
protected Sheet sheet1;
protected ColumnShifter columnShifter;

@Before
public void init() {
int rowIndex = 0;
sheet1 = wb.createSheet("sheet1");
Row row = sheet1.createRow(rowIndex++);
row.createCell(0, CellType.NUMERIC).setCellValue(0);
row.createCell(3, CellType.NUMERIC).setCellValue(3);
row.createCell(4, CellType.NUMERIC).setCellValue(4);

row = sheet1.createRow(rowIndex++);
row.createCell(0, CellType.NUMERIC).setCellValue(0.1);
row.createCell(1, CellType.NUMERIC).setCellValue(1.1);
row.createCell(2, CellType.NUMERIC).setCellValue(2.1);
row.createCell(3, CellType.NUMERIC).setCellValue(3.1);
row.createCell(4, CellType.NUMERIC).setCellValue(4.1);
row.createCell(5, CellType.NUMERIC).setCellValue(5.1);
row.createCell(6, CellType.NUMERIC).setCellValue(6.1);
row.createCell(7, CellType.NUMERIC).setCellValue(7.1);
row = sheet1.createRow(rowIndex++);
row.createCell(3, CellType.NUMERIC).setCellValue(3.2);
row.createCell(5, CellType.NUMERIC).setCellValue(5.2);
row.createCell(7, CellType.NUMERIC).setCellValue(7.2);

initColumnShifter();
}
protected void initColumnShifter(){
}

@Test
public void testShift3ColumnsRight() {
columnShifter.shiftColumns(1, 2, 3);
Cell cell = sheet1.getRow(0).getCell(4);
assertNull(cell);
cell = sheet1.getRow(1).getCell(4);
assertEquals(1.1, cell.getNumericCellValue(), 0.01);
cell = sheet1.getRow(1).getCell(5);
assertEquals(2.1, cell.getNumericCellValue(), 0.01);
cell = sheet1.getRow(2).getCell(4);
assertNull(cell);
}

@Test
public void testShiftLeft() {
try {
columnShifter.shiftColumns(1, 2, -3);
assertTrue("Shift to negative indices should throw exception", false);
}
catch(IllegalStateException e){
assertTrue(true);
}
}
}

+ 65
- 0
src/testcases/org/apache/poi/ss/usermodel/BaseTestRow.java View File

@@ -466,4 +466,69 @@ public abstract class BaseTestRow {
wb2.close();
}
@Test
public void testCellShiftingRight() {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet("sheet1");
Row row = sheet.createRow(0);
row.createCell(0, CellType.NUMERIC).setCellValue(0);
row.createCell(1, CellType.NUMERIC).setCellValue(1);
row.createCell(2, CellType.NUMERIC).setCellValue(2);//C
row.createCell(3, CellType.NUMERIC).setCellValue(3);//D
row.createCell(4, CellType.NUMERIC).setCellValue(4);//E
row.createCell(5, CellType.NUMERIC).setCellValue(5);//F
row.createCell(6, CellType.NUMERIC).setCellValue(6);//G
try {
row.shiftCellsLeft(6, 4, 2); // range [6-4] is illegal
fail("expected shiftLeft to fail");
} catch (IllegalArgumentException e){
row.shiftCellsRight(2, 4, 1);
//should be [0.0, 1.0, null, 2.0, 3.0, 4.0, 6.0, null]
Cell h1 = row.getCell(7);
assertNull(h1);
Cell g1 = row.getCell(6);
assertEquals(6, g1.getNumericCellValue(), 0.01);
Cell f1 = row.getCell(5);
assertEquals(4, f1.getNumericCellValue(), 0.01);
Cell e1 = row.getCell(4);
assertEquals(3, e1.getNumericCellValue(), 0.01);
Cell d1 = row.getCell(3);
assertEquals(2, d1.getNumericCellValue(), 0.01);
Cell c1 = row.getCell(2);
assertNull(c1);
}
}
@Test
public void testCellShiftingLeft() {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet("sheet1");
Row row = sheet.createRow(0);
row.createCell(0, CellType.NUMERIC).setCellValue(0);
row.createCell(1, CellType.NUMERIC).setCellValue(1);
row.createCell(2, CellType.NUMERIC).setCellValue(2);//C
row.createCell(3, CellType.NUMERIC).setCellValue(3);//D
row.createCell(4, CellType.NUMERIC).setCellValue(4);//E
row.createCell(5, CellType.NUMERIC).setCellValue(5);//F
row.createCell(6, CellType.NUMERIC).setCellValue(6);//G
try {
row.shiftCellsLeft(4, 6, -2); // step = -1 is illegal
fail("expected shiftLeft to fail");
} catch (IllegalArgumentException e){
row.shiftCellsLeft(4, 6, 2);
//should be [0.0, 1.0, 4.0, 5.0, 6.0, null, null, null]
Cell b1 = row.getCell(1);
assertEquals(1, b1.getNumericCellValue(), 0.01);
Cell c1 = row.getCell(2);
assertEquals(4, c1.getNumericCellValue(), 0.01);
Cell d1 = row.getCell(3);
assertEquals(5, d1.getNumericCellValue(), 0.01);
Cell e1 = row.getCell(4);
assertEquals(6, e1.getNumericCellValue(), 0.01);
Cell f1 = row.getCell(5);
assertNull(f1);
}
}
}

+ 388
- 0
src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetShiftColumns.java View File

@@ -0,0 +1,388 @@
package org.apache.poi.ss.usermodel;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.io.IOException;

import org.apache.poi.common.usermodel.HyperlinkType;
import org.apache.poi.ss.ITestDataProvider;
import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellUtil;
import org.junit.Before;
import org.junit.Test;

public abstract class BaseTestSheetShiftColumns {
protected Sheet sheet1;
protected Sheet sheet2;
protected Workbook workbook;

protected ITestDataProvider _testDataProvider;

public BaseTestSheetShiftColumns(){
}

@Before
public void init() {
int rowIndex = 0;
sheet1 = workbook.createSheet("sheet1");
Row row = sheet1.createRow(rowIndex++);
row.createCell(0, CellType.NUMERIC).setCellValue(0);
row.createCell(1, CellType.NUMERIC).setCellValue(1);
row.createCell(2, CellType.NUMERIC).setCellValue(2);

row = sheet1.createRow(rowIndex++);
row.createCell(0, CellType.NUMERIC).setCellValue(0.1);
row.createCell(1, CellType.NUMERIC).setCellValue(1.1);
row.createCell(2, CellType.NUMERIC).setCellValue(2.1);
row = sheet1.createRow(rowIndex++);
row.createCell(0, CellType.NUMERIC).setCellValue(0.2);
row.createCell(1, CellType.NUMERIC).setCellValue(1.2);
row.createCell(2, CellType.NUMERIC).setCellValue(2.2);
row = sheet1.createRow(rowIndex++);
row.createCell(0, CellType.FORMULA).setCellFormula("A2*B3");
row.createCell(1, CellType.NUMERIC).setCellValue(1.3);
row.createCell(2, CellType.FORMULA).setCellFormula("B1-B3");
row = sheet1.createRow(rowIndex++);
row.createCell(0, CellType.FORMULA).setCellFormula("SUM(C1:C4)");
row.createCell(1, CellType.FORMULA).setCellFormula("SUM(A3:C3)");
row.createCell(2, CellType.FORMULA).setCellFormula("$C1+C$2");
row = sheet1.createRow(rowIndex++);
row.createCell(1, CellType.NUMERIC).setCellValue(1.5);
row = sheet1.createRow(rowIndex);
row.createCell(1, CellType.BOOLEAN).setCellValue(false);
Cell textCell = row.createCell(2, CellType.STRING);
textCell.setCellValue("TEXT");
textCell.setCellStyle(newCenterBottomStyle());

sheet2 = workbook.createSheet("sheet2");
row = sheet2.createRow(0); row.createCell(0, CellType.NUMERIC).setCellValue(10);
row.createCell(1, CellType.NUMERIC).setCellValue(11);
row.createCell(2, CellType.FORMULA).setCellFormula("SUM(sheet1!B3:C3)");
row = sheet2.createRow(1);
row.createCell(0, CellType.NUMERIC).setCellValue(21);
row.createCell(1, CellType.NUMERIC).setCellValue(22);
row.createCell(2, CellType.NUMERIC).setCellValue(23);
row = sheet2.createRow(2);
row.createCell(0, CellType.FORMULA).setCellFormula("sheet1!A4+sheet1!C2+A2");
row.createCell(1, CellType.FORMULA).setCellFormula("SUM(sheet1!A3:$C3)");
row = sheet2.createRow(3);
row.createCell(0, CellType.STRING).setCellValue("dummy");
}

private CellStyle newCenterBottomStyle(){
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.BOTTOM);
return style;
}

@Test
public void testShiftOneColumnRight() {
sheet1.shiftColumns(1, 2, 1);
double c1Value = sheet1.getRow(0).getCell(2).getNumericCellValue();
assertEquals(1d, c1Value, 0.01);
String formulaA4 = sheet1.getRow(3).getCell(0).getCellFormula();
assertEquals("A2*C3", formulaA4);
String formulaC4 = sheet1.getRow(3).getCell(3).getCellFormula();
assertEquals("C1-C3", formulaC4);
String formulaB5 = sheet1.getRow(4).getCell(2).getCellFormula();
assertEquals("SUM(A3:D3)", formulaB5);
String formulaD5 = sheet1.getRow(4).getCell(3).getCellFormula(); // $C1+C$2
assertEquals("$D1+D$2", formulaD5);

Cell newb5Null = sheet1.getRow(4).getCell(1);
assertEquals(newb5Null, null);
boolean logicalValue = sheet1.getRow(6).getCell(2).getBooleanCellValue();
assertEquals(logicalValue, false);
Cell textCell = sheet1.getRow(6).getCell(3);
assertEquals(textCell.getStringCellValue(), "TEXT");
assertEquals(textCell.getCellStyle().getAlignment(), HorizontalAlignment.CENTER);
// other sheet
String formulaC1 = sheet2.getRow(0).getCell(2).getCellFormula(); // SUM(sheet1!B3:C3)
assertEquals("SUM(sheet1!C3:D3)", formulaC1);
String formulaA3 = sheet2.getRow(2).getCell(0).getCellFormula(); // sheet1!A4+sheet1!C2+A2
assertEquals("sheet1!A4+sheet1!D2+A2", formulaA3);
}

@Test
public void testShiftTwoColumnsRight() {
sheet1.shiftColumns(1, 2, 2);
String formulaA4 = sheet1.getRow(3).getCell(0).getCellFormula();
assertEquals("A2*D3", formulaA4);
String formulaD4 = sheet1.getRow(3).getCell(4).getCellFormula();
assertEquals("D1-D3", formulaD4);
String formulaD5 = sheet1.getRow(4).getCell(3).getCellFormula();
assertEquals("SUM(A3:E3)", formulaD5);

Cell b5Null = sheet1.getRow(4).getCell(1);
assertEquals(b5Null, null);
Object c6Null = sheet1.getRow(5).getCell(2); // null cell A5 is shifted
// for 2 columns, so now
// c5 should be null
assertEquals(c6Null, null);
}

@Test
public void testShiftOneColumnLeft() {
sheet1.shiftColumns(1, 2, -1);

String formulaA5 = sheet1.getRow(4).getCell(0).getCellFormula();
assertEquals("SUM(A3:B3)", formulaA5);
String formulaB4 = sheet1.getRow(3).getCell(1).getCellFormula();
assertEquals("A1-A3", formulaB4);
String formulaB5 = sheet1.getRow(4).getCell(1).getCellFormula();
assertEquals("$B1+B$2", formulaB5);
Cell newb6Null = sheet1.getRow(5).getCell(1);
assertEquals(newb6Null, null);
}
@Test(expected = IllegalStateException.class)
public void testShiftTwoColumnsLeft() {
sheet1.shiftColumns(1, 2, -2);
}

@Test
public void testShiftHyperlinks() throws IOException {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet("test");
Row row = sheet.createRow(0);

// How to create hyperlinks
// https://poi.apache.org/spreadsheet/quick-guide.html#Hyperlinks
CreationHelper helper = wb.getCreationHelper();
CellStyle hlinkStyle = wb.createCellStyle();
Font hlinkFont = wb.createFont();
hlinkFont.setUnderline(Font.U_SINGLE);
hlinkFont.setColor(IndexedColors.BLUE.getIndex());
hlinkStyle.setFont(hlinkFont);

// 3D relative document link
// CellAddress=A1, shifted to A4
Cell cell = row.createCell(0);
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, HyperlinkType.DOCUMENT, "test!E1");

// URL
cell = row.createCell(1);
// CellAddress=B1, shifted to B4
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, HyperlinkType.URL, "http://poi.apache.org/");

// row0 will be shifted on top of row1, so this URL should be removed
// from the workbook
Row overwrittenRow = sheet.createRow(3);
cell = overwrittenRow.createCell(2);
// CellAddress=C4, will be overwritten (deleted)
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, HyperlinkType.EMAIL, "mailto:poi@apache.org");

Row unaffectedRow = sheet.createRow(20);
cell = unaffectedRow.createCell(3);
// CellAddress=D21, will be unaffected
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, HyperlinkType.FILE, "54524.xlsx");

cell = wb.createSheet("other").createRow(0).createCell(0);
// CellAddress=Other!A1, will be unaffected
cell.setCellStyle(hlinkStyle);
createHyperlink(helper, cell, HyperlinkType.URL, "http://apache.org/");

int startRow = 0;
int endRow = 4;
int n = 3;
sheet.shiftColumns(startRow, endRow, n);

Workbook read = _testDataProvider.writeOutAndReadBack(wb);
wb.close();

Sheet sh = read.getSheet("test");

Row shiftedRow = sh.getRow(0);

// document link anchored on a shifted cell should be moved
// Note that hyperlinks do not track what they point to, so this
// hyperlink should still refer to test!E1
verifyHyperlink(shiftedRow.getCell(3), HyperlinkType.DOCUMENT, "test!E1");

// URL, EMAIL, and FILE links anchored on a shifted cell should be moved
verifyHyperlink(shiftedRow.getCell(4), HyperlinkType.URL, "http://poi.apache.org/");

// Make sure hyperlinks were moved and not copied
assertNull("Document hyperlink should be moved, not copied", sh.getHyperlink(0, 0));
assertNull("URL hyperlink should be moved, not copied", sh.getHyperlink(1, 0));

assertEquals(4, sh.getHyperlinkList().size());
read.close();
}

private void createHyperlink(CreationHelper helper, Cell cell, HyperlinkType linkType, String ref) {
cell.setCellValue(ref);
Hyperlink link = helper.createHyperlink(linkType);
link.setAddress(ref);
cell.setHyperlink(link);
}

private void verifyHyperlink(Cell cell, HyperlinkType linkType, String ref) {
assertTrue(cellHasHyperlink(cell));
if (cell != null) {
Hyperlink link = cell.getHyperlink();
assertEquals(linkType, link.getType());
assertEquals(ref, link.getAddress());
}
}

private boolean cellHasHyperlink(Cell cell) {
return (cell != null) && (cell.getHyperlink() != null);
}

@Test
public void shiftMergedColumnsToMergedColumnsRight() throws IOException {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet("test");

// populate sheet cells
populateSheetCells(sheet);
CellRangeAddress A1_A5 = new CellRangeAddress(0, 4, 0, 0); // NOSONAR, it's more readable this way
CellRangeAddress B1_B3 = new CellRangeAddress(0, 2, 1, 1); // NOSONAR, it's more readable this way

sheet.addMergedRegion(B1_B3);
sheet.addMergedRegion(A1_A5);

// A1:A5 should be moved to B1:B5
// B1:B3 will be removed
sheet.shiftColumns(0, 0, 1);

assertEquals(1, sheet.getNumMergedRegions());
assertEquals(CellRangeAddress.valueOf("B1:B5"), sheet.getMergedRegion(0));

wb.close();
}

@Test
public void shiftMergedColumnsToMergedColumnsLeft() throws IOException {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet("test");
populateSheetCells(sheet);

CellRangeAddress A1_A5 = new CellRangeAddress(0, 4, 0, 0); // NOSONAR, it's more readable this way
CellRangeAddress B1_B3 = new CellRangeAddress(0, 2, 1, 1); // NOSONAR, it's more readable this way

sheet.addMergedRegion(A1_A5);
sheet.addMergedRegion(B1_B3);

// A1:E1 should be removed
// B1:B3 will be A1:A3
sheet.shiftColumns(1, 5, -1);

assertEquals(1, sheet.getNumMergedRegions());
assertEquals(CellRangeAddress.valueOf("A1:A3"), sheet.getMergedRegion(0));

wb.close();
}

private void populateSheetCells(Sheet sheet) {
// populate sheet cells
for (int i = 0; i < 2; i++) {
Row row = sheet.createRow(i);
for (int j = 0; j < 5; j++) {
Cell cell = row.createCell(j);
cell.setCellValue(i + "x" + j);
}
}
}

@Test
public void testShiftWithMergedRegions() throws IOException {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet();
Row row = sheet.createRow(0);
row.createCell(0).setCellValue(1.1);
row = sheet.createRow(1);
row.createCell(0).setCellValue(2.2);
CellRangeAddress region = new CellRangeAddress(0, 2, 0, 0);
assertEquals("A1:A3", region.formatAsString());

sheet.addMergedRegion(region);

sheet.shiftColumns(0, 1, 2);
region = sheet.getMergedRegion(0);
assertEquals("C1:C3", region.formatAsString());
wb.close();
}

protected abstract Workbook openWorkbook(String spreadsheetFileName) throws IOException;
protected abstract Workbook getReadBackWorkbook(Workbook wb) throws IOException;
protected static final String AMDOCS = "Amdocs";
protected static final String AMDOCS_TEST = "Amdocs:\ntest\n";

@Test
public void testCommentsShifting() throws IOException {
Workbook inputWb = openWorkbook("56017.xlsx");

Sheet sheet = inputWb.getSheetAt(0);
Comment comment = sheet.getCellComment(new CellAddress(0, 0));
assertNotNull(comment);
assertEquals(AMDOCS, comment.getAuthor());
assertEquals(AMDOCS_TEST, comment.getString().getString());
sheet.shiftColumns(0, 1, 1);

// comment in column 0 is gone
comment = sheet.getCellComment(new CellAddress(0, 0));
assertNull(comment);

// comment is column in column 1
comment = sheet.getCellComment(new CellAddress(0, 1));
assertNotNull(comment);
assertEquals(AMDOCS, comment.getAuthor());
assertEquals(AMDOCS_TEST, comment.getString().getString());

Workbook wbBack = getReadBackWorkbook(inputWb);
inputWb.close();
assertNotNull(wbBack);

Sheet sheetBack = wbBack.getSheetAt(0);

// comment in column 0 is gone
comment = sheetBack.getCellComment(new CellAddress(0, 0));
assertNull(comment);

// comment is now in column 1
comment = sheetBack.getCellComment(new CellAddress(0, 1));
assertNotNull(comment);
assertEquals(AMDOCS, comment.getAuthor());
assertEquals(AMDOCS_TEST, comment.getString().getString());
wbBack.close();
}
// transposed version of TestXSSFSheetShiftRows.testBug54524()
@Test
public void testBug54524() throws IOException {
Workbook wb = _testDataProvider.createWorkbook();
Sheet sheet = wb.createSheet();
Row firstRow = sheet.createRow(0);
firstRow.createCell(0).setCellValue("");
firstRow.createCell(1).setCellValue(1);
firstRow.createCell(2).setCellValue(2);
firstRow.createCell(3).setCellFormula("SUM(B1:C1)");
firstRow.createCell(4).setCellValue("X");
sheet.shiftColumns(3, 5, -1);

Cell cell = CellUtil.getCell(sheet.getRow(0), 1);
assertEquals(1.0, cell.getNumericCellValue(), 0);
cell = CellUtil.getCell(sheet.getRow(0), 2);
assertEquals("SUM(B1:B1)", cell.getCellFormula());
cell = CellUtil.getCell(sheet.getRow(0), 3);
assertEquals("X", cell.getStringCellValue());
wb.close();
}


}

Loading…
Cancel
Save