git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1822639 13f79535-47bb-0310-9956-ffa450edef68tags/REL_4_0_0_FINAL
@@ -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; | |||
} | |||
} |
@@ -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); |
@@ -65,4 +65,4 @@ public final class HSSFColumnShifter extends ColumnShifter { | |||
throw new NotImplementedException("updateHyperlinks"); | |||
} | |||
} | |||
} |
@@ -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; |
@@ -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() + ")"); | |||
} | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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> |
@@ -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 | |||
} | |||
} |
@@ -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. | |||
* |
@@ -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"); | |||
} | |||
} | |||
@@ -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"); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
/** |
@@ -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; |
@@ -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 | |||
* |
@@ -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. | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 {} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |