git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1711885 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_14_BETA1
package org.apache.poi.ss.usermodel; | |||||
import org.apache.poi.util.Beta; | |||||
@Beta | |||||
public class CellCopyPolicy implements Cloneable { | |||||
public static final boolean DEFAULT_COPY_CELL_VALUE_POLICY = true; | |||||
public static final boolean DEFAULT_COPY_CELL_STYLE_POLICY = true; | |||||
public static final boolean DEFAULT_COPY_CELL_FORMULA_POLICY = true; | |||||
public static final boolean DEFAULT_COPY_MERGED_REGIONS_POLICY = true; | |||||
public static final boolean DEFAULT_COPY_ROW_HEIGHT_POLICY = true; | |||||
public static final boolean DEFAULT_CONDENSE_ROWS_POLICY = false; | |||||
private boolean copyCellValue = DEFAULT_COPY_CELL_VALUE_POLICY; | |||||
private boolean copyCellStyle = DEFAULT_COPY_CELL_STYLE_POLICY; | |||||
private boolean copyCellFormula = DEFAULT_COPY_CELL_FORMULA_POLICY; | |||||
private boolean copyMergedRegions = DEFAULT_COPY_MERGED_REGIONS_POLICY; | |||||
private boolean copyRowHeight = DEFAULT_COPY_ROW_HEIGHT_POLICY; | |||||
private boolean condenseRows = DEFAULT_CONDENSE_ROWS_POLICY; | |||||
/** | |||||
* Default CellCopyPolicy, uses default policy | |||||
* For custom CellCopyPolicy, use {@link #Builder} class | |||||
*/ | |||||
public CellCopyPolicy() { } | |||||
// should builder be replaced with CellCopyPolicy setters that return the object | |||||
// to allow setters to be chained together? | |||||
// policy.setCopyCellValue(true).setCopyCellStyle(true) | |||||
private CellCopyPolicy(Builder builder) { | |||||
copyCellValue = builder.copyCellValue; | |||||
copyCellStyle = builder.copyCellStyle; | |||||
copyCellFormula = builder.copyCellFormula; | |||||
copyMergedRegions = builder.copyMergedRegions; | |||||
copyRowHeight = builder.copyRowHeight; | |||||
condenseRows = builder.condenseRows; | |||||
} | |||||
public static class Builder { | |||||
private boolean copyCellValue = DEFAULT_COPY_CELL_VALUE_POLICY; | |||||
private boolean copyCellStyle = DEFAULT_COPY_CELL_STYLE_POLICY; | |||||
private boolean copyCellFormula = DEFAULT_COPY_CELL_FORMULA_POLICY; | |||||
private boolean copyMergedRegions = DEFAULT_COPY_MERGED_REGIONS_POLICY; | |||||
private boolean copyRowHeight = DEFAULT_COPY_ROW_HEIGHT_POLICY; | |||||
private boolean condenseRows = DEFAULT_CONDENSE_ROWS_POLICY; | |||||
/** | |||||
* Builder class for CellCopyPolicy | |||||
*/ | |||||
public Builder() { | |||||
} | |||||
public Builder cellValue(boolean copyCellValue) { | |||||
this.copyCellValue = copyCellValue; | |||||
return this; | |||||
} | |||||
public Builder cellStyle(boolean copyCellStyle) { | |||||
this.copyCellStyle = copyCellStyle; | |||||
return this; | |||||
} | |||||
public Builder cellFormula(boolean copyCellFormula) { | |||||
this.copyCellFormula = copyCellFormula; | |||||
return this; | |||||
} | |||||
public Builder mergedRegions(boolean copyMergedRegions) { | |||||
this.copyMergedRegions = copyMergedRegions; | |||||
return this; | |||||
} | |||||
public Builder rowHeight(boolean copyRowHeight) { | |||||
this.copyRowHeight = copyRowHeight; | |||||
return this; | |||||
} | |||||
public Builder condenseRows(boolean condenseRows) { | |||||
this.condenseRows = condenseRows; | |||||
return this; | |||||
} | |||||
public CellCopyPolicy build() { | |||||
return new CellCopyPolicy(this); | |||||
} | |||||
} | |||||
private Builder createBuilder() { | |||||
final Builder builder = new Builder() | |||||
.cellValue(copyCellValue) | |||||
.cellStyle(copyCellStyle) | |||||
.cellFormula(copyCellFormula) | |||||
.mergedRegions(copyMergedRegions) | |||||
.rowHeight(copyRowHeight) | |||||
.condenseRows(condenseRows); | |||||
return builder; | |||||
} | |||||
@Override | |||||
public CellCopyPolicy clone() { | |||||
return createBuilder().build(); | |||||
} | |||||
/** | |||||
* @return the copyCellValue | |||||
*/ | |||||
public boolean isCopyCellValue() { | |||||
return copyCellValue; | |||||
} | |||||
/** | |||||
* @param copyCellValue the copyCellValue to set | |||||
*/ | |||||
public void setCopyCellValue(boolean copyCellValue) { | |||||
this.copyCellValue = copyCellValue; | |||||
} | |||||
/** | |||||
* @return the copyCellStyle | |||||
*/ | |||||
public boolean isCopyCellStyle() { | |||||
return copyCellStyle; | |||||
} | |||||
/** | |||||
* @param copyCellStyle the copyCellStyle to set | |||||
*/ | |||||
public void setCopyCellStyle(boolean copyCellStyle) { | |||||
this.copyCellStyle = copyCellStyle; | |||||
} | |||||
/** | |||||
* @return the copyCellFormula | |||||
*/ | |||||
public boolean isCopyCellFormula() { | |||||
return copyCellFormula; | |||||
} | |||||
/** | |||||
* @param copyCellFormula the copyCellFormula to set | |||||
*/ | |||||
public void setCopyCellFormula(boolean copyCellFormula) { | |||||
this.copyCellFormula = copyCellFormula; | |||||
} | |||||
/** | |||||
* @return the copyMergedRegions | |||||
*/ | |||||
public boolean isCopyMergedRegions() { | |||||
return copyMergedRegions; | |||||
} | |||||
/** | |||||
* @param copyMergedRegions the copyMergedRegions to set | |||||
*/ | |||||
public void setCopyMergedRegions(boolean copyMergedRegions) { | |||||
this.copyMergedRegions = copyMergedRegions; | |||||
} | |||||
/** | |||||
* @return the copyRowHeight | |||||
*/ | |||||
public boolean isCopyRowHeight() { | |||||
return copyRowHeight; | |||||
} | |||||
/** | |||||
* @param copyRowHeight the copyRowHeight to set | |||||
*/ | |||||
public void setCopyRowHeight(boolean copyRowHeight) { | |||||
this.copyRowHeight = copyRowHeight; | |||||
} | |||||
/** | |||||
* If condenseRows is true, a discontinuities in srcRows will be removed when copied to destination | |||||
* For example: | |||||
* Sheet.copyRows({Row(1), Row(2), Row(5)}, 11, policy) results in rows 1, 2, and 5 | |||||
* being copied to rows 11, 12, and 13 if condenseRows is True, or rows 11, 11, 15 if condenseRows is false | |||||
* @return the condenseRows | |||||
*/ | |||||
public boolean isCondenseRows() { | |||||
return condenseRows; | |||||
} | |||||
/** | |||||
* @param condenseRows the condenseRows to set | |||||
*/ | |||||
public void setCondenseRows(boolean condenseRows) { | |||||
this.condenseRows = condenseRows; | |||||
} | |||||
} |
import org.apache.poi.ss.formula.eval.ErrorEval; | import org.apache.poi.ss.formula.eval.ErrorEval; | ||||
import org.apache.poi.ss.formula.ptg.Ptg; | import org.apache.poi.ss.formula.ptg.Ptg; | ||||
import org.apache.poi.ss.usermodel.Cell; | import org.apache.poi.ss.usermodel.Cell; | ||||
import org.apache.poi.ss.usermodel.CellCopyPolicy; | |||||
import org.apache.poi.ss.usermodel.CellStyle; | import org.apache.poi.ss.usermodel.CellStyle; | ||||
import org.apache.poi.ss.usermodel.Comment; | import org.apache.poi.ss.usermodel.Comment; | ||||
import org.apache.poi.ss.usermodel.DataFormatter; | import org.apache.poi.ss.usermodel.DataFormatter; | ||||
import org.apache.poi.ss.usermodel.Row; | import org.apache.poi.ss.usermodel.Row; | ||||
import org.apache.poi.ss.util.CellRangeAddress; | import org.apache.poi.ss.util.CellRangeAddress; | ||||
import org.apache.poi.ss.util.CellReference; | import org.apache.poi.ss.util.CellReference; | ||||
import org.apache.poi.util.Beta; | |||||
import org.apache.poi.util.Internal; | import org.apache.poi.util.Internal; | ||||
import org.apache.poi.util.LocaleUtil; | import org.apache.poi.util.LocaleUtil; | ||||
import org.apache.poi.xssf.model.SharedStringsTable; | import org.apache.poi.xssf.model.SharedStringsTable; | ||||
_sharedStringSource = row.getSheet().getWorkbook().getSharedStringSource(); | _sharedStringSource = row.getSheet().getWorkbook().getSharedStringSource(); | ||||
_stylesSource = row.getSheet().getWorkbook().getStylesSource(); | _stylesSource = row.getSheet().getWorkbook().getStylesSource(); | ||||
} | } | ||||
/** | |||||
* Copy cell value, formula, and style, from srcCell per cell copy policy | |||||
* If srcCell is null, clears the cell value and cell style per cell copy policy | |||||
* @param srcCell | |||||
* @param policy | |||||
* @throws IllegalArgumentException if copy cell style and srcCell is from a different workbook | |||||
*/ | |||||
@Beta | |||||
@Internal | |||||
public void copyCellFrom(Cell srcCell, CellCopyPolicy policy) { | |||||
// Copy cell value (cell type is updated implicitly) | |||||
if (policy.isCopyCellValue()) { | |||||
if (srcCell != null) { | |||||
int copyCellType = srcCell.getCellType(); | |||||
if (copyCellType == Cell.CELL_TYPE_FORMULA && !policy.isCopyCellFormula()) { | |||||
// Copy formula result as value | |||||
// FIXME: Cached value may be stale | |||||
copyCellType = srcCell.getCachedFormulaResultType(); | |||||
} | |||||
switch (copyCellType) { | |||||
case Cell.CELL_TYPE_BOOLEAN: | |||||
setCellValue(srcCell.getBooleanCellValue()); | |||||
break; | |||||
case Cell.CELL_TYPE_ERROR: | |||||
setCellErrorValue(srcCell.getErrorCellValue()); | |||||
break; | |||||
case Cell.CELL_TYPE_FORMULA: | |||||
setCellFormula(srcCell.getCellFormula()); | |||||
break; | |||||
case Cell.CELL_TYPE_NUMERIC: | |||||
// DataFormat is not copied unless policy.isCopyCellStyle is true | |||||
if (DateUtil.isCellDateFormatted(srcCell)) { | |||||
setCellValue(srcCell.getDateCellValue()); | |||||
} | |||||
else { | |||||
setCellValue(srcCell.getNumericCellValue()); | |||||
} | |||||
break; | |||||
case Cell.CELL_TYPE_STRING: | |||||
setCellValue(srcCell.getStringCellValue()); | |||||
break; | |||||
case Cell.CELL_TYPE_BLANK: | |||||
setBlank(); | |||||
break; | |||||
default: | |||||
throw new IllegalArgumentException("Invalid cell type " + srcCell.getCellType()); | |||||
} | |||||
} else { //srcCell is null | |||||
setBlank(); | |||||
} | |||||
} | |||||
// Copy CellStyle | |||||
if (policy.isCopyCellStyle()) { | |||||
if (srcCell != null) { | |||||
setCellStyle(srcCell.getCellStyle()); | |||||
} | |||||
else { | |||||
// clear cell style | |||||
setCellStyle(null); | |||||
} | |||||
} | |||||
} | |||||
/** | /** | ||||
* @return table of strings shared across this workbook | * @return table of strings shared across this workbook | ||||
* | * | ||||
* @param style reference contained in the workbook. | * @param style reference contained in the workbook. | ||||
* If the value is null then the style information is removed causing the cell to used the default workbook style. | * If the value is null then the style information is removed causing the cell to used the default workbook style. | ||||
* | |||||
* @throws IllegalArgumentException if style belongs to a different styles source (most likely because style is from a different workbook) | |||||
* @throws IllegalArgumentException if style belongs to a different styles source (most likely because style is from a different Workbook) | |||||
*/ | */ | ||||
@Override | @Override | ||||
public void setCellStyle(CellStyle style) { | public void setCellStyle(CellStyle style) { |
package org.apache.poi.xssf.usermodel; | package org.apache.poi.xssf.usermodel; | ||||
import java.util.HashSet; | |||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.Set; | |||||
import java.util.TreeMap; | import java.util.TreeMap; | ||||
import org.apache.poi.ss.formula.FormulaShifter; | |||||
import org.apache.poi.ss.SpreadsheetVersion; | import org.apache.poi.ss.SpreadsheetVersion; | ||||
import org.apache.poi.ss.usermodel.Cell; | import org.apache.poi.ss.usermodel.Cell; | ||||
import org.apache.poi.ss.usermodel.CellCopyPolicy; | |||||
import org.apache.poi.ss.usermodel.CellStyle; | import org.apache.poi.ss.usermodel.CellStyle; | ||||
import org.apache.poi.ss.usermodel.Row; | 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.ss.util.CellReference; | ||||
import org.apache.poi.util.Beta; | |||||
import org.apache.poi.util.Internal; | import org.apache.poi.util.Internal; | ||||
import org.apache.poi.xssf.model.CalculationChain; | import org.apache.poi.xssf.model.CalculationChain; | ||||
import org.apache.poi.xssf.model.StylesTable; | import org.apache.poi.xssf.model.StylesTable; | ||||
import org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter; | |||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; | import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; | ||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow; | import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow; | ||||
} | } | ||||
setRowNum(rownum); | setRowNum(rownum); | ||||
} | } | ||||
/** | |||||
* Copy the cells from srcRow to this row | |||||
* If this row is not a blank row, this will merge the two rows, overwriting | |||||
* the cells in this row with the cells in srcRow | |||||
* If srcRow is null, overwrite cells in destination row with blank values, styles, etc per cell copy policy | |||||
* srcRow may be from a different sheet in the same workbook | |||||
* @param srcRow the rows to copy from | |||||
* @param policy the policy to determine what gets copied | |||||
*/ | |||||
@Beta | |||||
public void copyRowFrom(Row srcRow, CellCopyPolicy policy) { | |||||
if (srcRow == null) { | |||||
// srcRow is blank. Overwrite cells with blank values, blank styles, etc per cell copy policy | |||||
for (Cell destCell : this) { | |||||
final XSSFCell srcCell = null; | |||||
// FIXME: remove type casting when copyCellFrom(Cell, CellCopyPolicy) is added to Cell interface | |||||
((XSSFCell)destCell).copyCellFrom(srcCell, policy); | |||||
} | |||||
if (policy.isCopyMergedRegions()) { | |||||
// Remove MergedRegions in dest row | |||||
final int destRowNum = getRowNum(); | |||||
int index = 0; | |||||
final Set<Integer> indices = new HashSet<Integer>(); | |||||
for (CellRangeAddress destRegion : getSheet().getMergedRegions()) { | |||||
if (destRowNum == destRegion.getFirstRow() && destRowNum == destRegion.getLastRow()) { | |||||
indices.add(index); | |||||
} | |||||
index++; | |||||
} | |||||
getSheet().removeMergedRegions(indices); | |||||
} | |||||
if (policy.isCopyRowHeight()) { | |||||
// clear row height | |||||
setHeight((short)-1); | |||||
} | |||||
} | |||||
else { | |||||
for (final Cell c : srcRow){ | |||||
final XSSFCell srcCell = (XSSFCell)c; | |||||
final XSSFCell destCell = createCell(srcCell.getColumnIndex(), srcCell.getCellType()); | |||||
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 shifter = FormulaShifter.createForRowCopy(sheetIndex, sheetName, srcRowNum, srcRowNum, rowDifference, SpreadsheetVersion.EXCEL2007); | |||||
rowShifter.updateRowFormulas(this, shifter); | |||||
// Copy merged regions that are fully contained on the row | |||||
// FIXME: is this something that rowShifter could be doing? | |||||
if (policy.isCopyMergedRegions()) { | |||||
for (CellRangeAddress srcRegion : srcRow.getSheet().getMergedRegions()) { | |||||
if (srcRowNum == srcRegion.getFirstRow() && srcRowNum == srcRegion.getLastRow()) { | |||||
CellRangeAddress destRegion = srcRegion.copy(); | |||||
destRegion.setFirstRow(destRowNum); | |||||
destRegion.setLastRow(destRowNum); | |||||
getSheet().addMergedRegion(destRegion); | |||||
} | |||||
} | |||||
} | |||||
if (policy.isCopyRowHeight()) { | |||||
setHeight(srcRow.getHeight()); | |||||
} | |||||
} | |||||
} | |||||
public int getOutlineLevel() { | public int getOutlineLevel() { | ||||
return _row.getOutlineLevel(); | return _row.getOutlineLevel(); |
import org.apache.poi.ss.formula.FormulaShifter; | import org.apache.poi.ss.formula.FormulaShifter; | ||||
import org.apache.poi.ss.formula.SheetNameFormatter; | import org.apache.poi.ss.formula.SheetNameFormatter; | ||||
import org.apache.poi.ss.usermodel.Cell; | import org.apache.poi.ss.usermodel.Cell; | ||||
import org.apache.poi.ss.usermodel.CellCopyPolicy; | |||||
import org.apache.poi.ss.usermodel.CellRange; | import org.apache.poi.ss.usermodel.CellRange; | ||||
import org.apache.poi.ss.usermodel.CellStyle; | import org.apache.poi.ss.usermodel.CellStyle; | ||||
import org.apache.poi.ss.usermodel.DataValidation; | import org.apache.poi.ss.usermodel.DataValidation; | ||||
public XSSFRow getRow(int rownum) { | public XSSFRow getRow(int rownum) { | ||||
return _rows.get(rownum); | return _rows.get(rownum); | ||||
} | } | ||||
/** | |||||
* returns all rows between startRow and endRow, inclusive. | |||||
* Rows between startRow and endRow that haven't been created are not included | |||||
* in result unless createRowIfMissing is true | |||||
* | |||||
* @param startRow the first row number in this sheet to return | |||||
* @param endRow the last row number in this sheet to return | |||||
* @param createRowIfMissing | |||||
* @return | |||||
* @throws IllegalArgumentException if startRowNum and endRowNum are not in ascending order | |||||
*/ | |||||
private List<XSSFRow> getRows(int startRowNum, int endRowNum, boolean createRowIfMissing) { | |||||
if (startRowNum > endRowNum) { | |||||
throw new IllegalArgumentException("getRows: startRowNum must be less than or equal to endRowNum"); | |||||
} | |||||
final List<XSSFRow> rows = new ArrayList<XSSFRow>(); | |||||
if (createRowIfMissing) { | |||||
for (int i = startRowNum; i <= endRowNum; i++) { | |||||
XSSFRow row = getRow(i); | |||||
if (row == null) { | |||||
row = createRow(i); | |||||
} | |||||
rows.add(row); | |||||
} | |||||
} | |||||
else { | |||||
rows.addAll(_rows.subMap(startRowNum, endRowNum+1).values()); | |||||
} | |||||
return rows; | |||||
} | |||||
/** | /** | ||||
* Horizontal page break information used for print layout view, page layout view, drawing print breaks in normal | * Horizontal page break information used for print layout view, page layout view, drawing print breaks in normal | ||||
getSheetTypeSheetView().setZoomScale(scale); | getSheetTypeSheetView().setZoomScale(scale); | ||||
} | } | ||||
/** | |||||
* copyRows rows from srcRows to this sheet starting at destStartRow | |||||
* | |||||
* Additionally copies merged regions that are completely defined in these | |||||
* rows (ie. merged 2 cells on a row to be shifted). | |||||
* @param srcRows the rows to copy. Formulas will be offset by the difference | |||||
* in the row number of the first row in srcRows and destStartRow (even if srcRows | |||||
* are from a different sheet). | |||||
* @param destStartRow the row in this sheet to paste the first row of srcRows | |||||
* the remainder of srcRows will be pasted below destStartRow per the cell copy policy | |||||
* @param policy is the cell copy policy, which can be used to merge the source and destination | |||||
* when the source is blank, copy styles only, paste as value, etc | |||||
*/ | |||||
@Beta | |||||
public void copyRows(List<? extends Row> srcRows, int destStartRow, CellCopyPolicy policy) { | |||||
if (srcRows == null || srcRows.size() == 0) { | |||||
throw new IllegalArgumentException("No rows to copy"); | |||||
} | |||||
final Row srcStartRow = srcRows.get(0); | |||||
final Row srcEndRow = srcRows.get(srcRows.size() - 1); | |||||
if (srcStartRow == null) { | |||||
throw new IllegalArgumentException("copyRows: First row cannot be null"); | |||||
} | |||||
final int srcStartRowNum = srcStartRow.getRowNum(); | |||||
final int srcEndRowNum = srcEndRow.getRowNum(); | |||||
// check row numbers to make sure they are continuous and increasing (monotonic) | |||||
// and srcRows does not contain null rows | |||||
for (int index=1; index < srcRows.size(); index++) { | |||||
final Row prevRow = srcRows.get(index-1); | |||||
final Row curRow = srcRows.get(index); | |||||
if (prevRow == null || curRow == null) { | |||||
throw new IllegalArgumentException("srcRows may not contain null rows. Found null row at index " + | |||||
index + " after Row " + prevRow.getRowNum() + "."); | |||||
//} else if (curRow.getRowNum() != prevRow.getRowNum() + 1) { | |||||
// throw new IllegalArgumentException("srcRows must contain continuously increasing row numbers. " + | |||||
// "Got srcRows[" + (index-1) + "]=Row " + prevRow.getRowNum() + ", srcRows[" + index + "]=Row " + curRow.getRowNum() + "."); | |||||
// FIXME: assumes row objects belong to non-null sheets and sheets belong to non-null workbooks. | |||||
} else if (srcStartRow.getSheet().getWorkbook() != curRow.getSheet().getWorkbook()) { | |||||
throw new IllegalArgumentException("All rows in srcRows must belong to the same sheet in the same workbook." + | |||||
"Expected all rows from same workbook (" + srcStartRow.getSheet().getWorkbook() + "). " + | |||||
"Got srcRows[" + index + "] from different workbook (" + curRow.getSheet().getWorkbook() + ")."); | |||||
} else if (srcStartRow.getSheet() != curRow.getSheet()) { | |||||
throw new IllegalArgumentException("All rows in srcRows must belong to the same sheet. " + | |||||
"Expected all rows from " + srcStartRow.getSheet().getSheetName() + ". " + | |||||
"Got srcRows[" + index + "] from " + curRow.getSheet().getSheetName()); | |||||
} | |||||
} | |||||
// FIXME: is special behavior needed if srcRows and destRows belong to the same sheets and the regions overlap? | |||||
final CellCopyPolicy options = policy.clone(); | |||||
// avoid O(N^2) performance scanning through all regions for each row | |||||
// merged regions will be copied after all the rows have been copied | |||||
options.setCopyMergedRegions(false); | |||||
// FIXME: if srcRows contains gaps or null values, clear out those rows that will be overwritten | |||||
// how will this work with merging (copy just values, leave cell styles in place?) | |||||
int r = destStartRow; | |||||
for (Row srcRow : srcRows) { | |||||
int destRowNum; | |||||
if (policy.isCondenseRows()) { | |||||
destRowNum = r++; | |||||
} else { | |||||
final int shift = (srcRow.getRowNum() - srcStartRowNum); | |||||
destRowNum = destStartRow + shift; | |||||
} | |||||
//removeRow(destRowNum); //this probably clears all external formula references to destRow, causing unwanted #REF! errors | |||||
final XSSFRow destRow = createRow(destRowNum); | |||||
destRow.copyRowFrom(srcRow, options); | |||||
} | |||||
// ====================== | |||||
// Only do additional copy operations here that cannot be done with Row.copyFromRow(Row, options) | |||||
// reasons: operation needs to interact with multiple rows or sheets | |||||
// Copy merged regions that are contained within the copy region | |||||
if (policy.isCopyMergedRegions()) { | |||||
// FIXME: is this something that rowShifter could be doing? | |||||
final int shift = destStartRow - srcStartRowNum; | |||||
for (CellRangeAddress srcRegion : srcStartRow.getSheet().getMergedRegions()) { | |||||
if (srcStartRowNum <= srcRegion.getFirstRow() && srcRegion.getLastRow() <= srcEndRowNum) { | |||||
// srcRegion is fully inside the copied rows | |||||
final CellRangeAddress destRegion = srcRegion.copy(); | |||||
destRegion.setFirstRow(destRegion.getFirstRow() + shift); | |||||
destRegion.setLastRow(destRegion.getLastRow() + shift); | |||||
addMergedRegion(destRegion); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Copies rows between srcStartRow and srcEndRow to the same sheet, starting at destStartRow | |||||
* Convenience function for {@link #copyRows(List, int, CellCopyPolicy)} | |||||
* | |||||
* Equivalent to copyRows(getRows(srcStartRow, srcEndRow, false), destStartRow, cellCopyPolicy) | |||||
* | |||||
* @param srcStartRow the index of the first row to copy the cells from in this sheet | |||||
* @param srcEndRow the index of the last row to copy the cells from in this sheet | |||||
* @param destStartRow the index of the first row to copy the cells to in this sheet | |||||
* @param cellCopyPolicy the policy to use to determine how cells are copied | |||||
*/ | |||||
@Beta | |||||
public void copyRows(int srcStartRow, int srcEndRow, int destStartRow, CellCopyPolicy cellCopyPolicy) { | |||||
final List<XSSFRow> srcRows = getRows(srcStartRow, srcEndRow, false); //FIXME: should be false, no need to create rows where src is only to copy them to dest | |||||
copyRows(srcRows, destStartRow, cellCopyPolicy); | |||||
} | |||||
/** | /** | ||||
* Shifts rows between startRow and endRow n number of rows. | * Shifts rows between startRow and endRow n number of rows. | ||||
* If you use a negative number, it will shift rows up. | * If you use a negative number, it will shift rows up. |
import org.apache.poi.ss.usermodel.Row; | import org.apache.poi.ss.usermodel.Row; | ||||
import org.apache.poi.ss.usermodel.Sheet; | import org.apache.poi.ss.usermodel.Sheet; | ||||
import org.apache.poi.ss.util.CellRangeAddress; | import org.apache.poi.ss.util.CellRangeAddress; | ||||
import org.apache.poi.util.Internal; | |||||
import org.apache.poi.util.POILogFactory; | import org.apache.poi.util.POILogFactory; | ||||
import org.apache.poi.util.POILogger; | import org.apache.poi.util.POILogger; | ||||
import org.apache.poi.xssf.usermodel.XSSFCell; | import org.apache.poi.xssf.usermodel.XSSFCell; | ||||
} | } | ||||
} | } | ||||
private void updateRowFormulas(XSSFRow row, FormulaShifter shifter) { | |||||
/** | |||||
* Update the formulas in specified row using the formula shifting policy specified by shifter | |||||
* | |||||
* @param row the row to update the formulas on | |||||
* @param shifter the formula shifting policy | |||||
*/ | |||||
@Internal | |||||
public void updateRowFormulas(XSSFRow row, FormulaShifter shifter) { | |||||
for (Cell c : row) { | for (Cell c : row) { | ||||
XSSFCell cell = (XSSFCell) c; | XSSFCell cell = (XSSFCell) c; | ||||
package org.apache.poi.xssf.usermodel; | package org.apache.poi.xssf.usermodel; | ||||
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
import static org.junit.Assert.assertNotEquals; | |||||
import static org.junit.Assert.assertNotNull; | import static org.junit.Assert.assertNotNull; | ||||
import static org.junit.Assert.assertNull; | import static org.junit.Assert.assertNull; | ||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import org.apache.poi.ss.SpreadsheetVersion; | import org.apache.poi.ss.SpreadsheetVersion; | ||||
import org.apache.poi.ss.usermodel.BaseTestXCell; | import org.apache.poi.ss.usermodel.BaseTestXCell; | ||||
import org.apache.poi.ss.usermodel.Cell; | import org.apache.poi.ss.usermodel.Cell; | ||||
import org.apache.poi.ss.usermodel.CellCopyPolicy; | |||||
import org.apache.poi.ss.usermodel.CellStyle; | |||||
import org.apache.poi.ss.usermodel.DataFormatter; | import org.apache.poi.ss.usermodel.DataFormatter; | ||||
import org.apache.poi.ss.usermodel.RichTextString; | import org.apache.poi.ss.usermodel.RichTextString; | ||||
import org.apache.poi.ss.usermodel.Row; | import org.apache.poi.ss.usermodel.Row; | ||||
wb.close(); | wb.close(); | ||||
} | } | ||||
} | } | ||||
private XSSFCell srcCell, destCell; //used for testCopyCellFrom_CellCopyPolicy | |||||
@Test | |||||
public final void testCopyCellFrom_CellCopyPolicy_default() { | |||||
setUp_testCopyCellFrom_CellCopyPolicy(); | |||||
// default copy policy | |||||
final CellCopyPolicy policy = new CellCopyPolicy(); | |||||
destCell.copyCellFrom(srcCell, policy); | |||||
assertEquals(Cell.CELL_TYPE_FORMULA, destCell.getCellType()); | |||||
assertEquals("2+3", destCell.getCellFormula()); | |||||
assertEquals(srcCell.getCellStyle(), destCell.getCellStyle()); | |||||
} | |||||
@Test | |||||
public final void testCopyCellFrom_CellCopyPolicy_value() { | |||||
setUp_testCopyCellFrom_CellCopyPolicy(); | |||||
// Paste values only | |||||
final CellCopyPolicy policy = new CellCopyPolicy.Builder().cellFormula(false).build(); | |||||
destCell.copyCellFrom(srcCell, policy); | |||||
assertEquals(Cell.CELL_TYPE_NUMERIC, destCell.getCellType()); | |||||
System.out.println("ERROR: fix formula evaluation"); | |||||
} | |||||
@Test | |||||
public final void testCopyCellFrom_CellCopyPolicy_style() { | |||||
setUp_testCopyCellFrom_CellCopyPolicy(); | |||||
srcCell.setCellValue((String) null); | |||||
// Paste styles only | |||||
final CellCopyPolicy policy = new CellCopyPolicy.Builder().cellValue(false).build(); | |||||
destCell.copyCellFrom(srcCell, policy); | |||||
assertEquals(srcCell.getCellStyle(), destCell.getCellStyle()); | |||||
// Old cell value should not have been overwritten | |||||
assertNotEquals(Cell.CELL_TYPE_BLANK, destCell.getCellType()); | |||||
assertEquals(Cell.CELL_TYPE_BOOLEAN, destCell.getCellType()); | |||||
assertEquals(true, destCell.getBooleanCellValue()); | |||||
} | |||||
private final void setUp_testCopyCellFrom_CellCopyPolicy() { | |||||
@SuppressWarnings("resource") | |||||
final XSSFWorkbook wb = new XSSFWorkbook(); | |||||
final XSSFRow row = wb.createSheet().createRow(0); | |||||
srcCell = row.createCell(0); | |||||
destCell = row.createCell(1); | |||||
srcCell.setCellFormula("2+3"); | |||||
final CellStyle style = wb.createCellStyle(); | |||||
style.setBorderTop(CellStyle.BORDER_THICK); | |||||
style.setFillBackgroundColor((short) 5); | |||||
srcCell.setCellStyle(style); | |||||
destCell.setCellValue(true); | |||||
} | |||||
} | } |
package org.apache.poi.xssf.usermodel; | package org.apache.poi.xssf.usermodel; | ||||
import static org.junit.Assert.assertEquals; | |||||
import static org.junit.Assert.assertNotNull; | |||||
import static org.junit.Assert.assertSame; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import org.apache.poi.ss.SpreadsheetVersion; | import org.apache.poi.ss.SpreadsheetVersion; | ||||
import org.apache.poi.ss.usermodel.BaseTestRow; | import org.apache.poi.ss.usermodel.BaseTestRow; | ||||
import org.apache.poi.ss.usermodel.Cell; | |||||
import org.apache.poi.ss.usermodel.CellCopyPolicy; | |||||
import org.apache.poi.ss.usermodel.Row; | |||||
import org.apache.poi.ss.usermodel.Sheet; | |||||
import org.apache.poi.xssf.XSSFITestDataProvider; | import org.apache.poi.xssf.XSSFITestDataProvider; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
public void testCellBounds() throws IOException { | public void testCellBounds() throws IOException { | ||||
baseTestCellBounds(SpreadsheetVersion.EXCEL2007.getLastColumnIndex()); | baseTestCellBounds(SpreadsheetVersion.EXCEL2007.getLastColumnIndex()); | ||||
} | } | ||||
public void testCopyRowFrom() throws IOException { | |||||
final XSSFWorkbook workbook = new XSSFWorkbook(); | |||||
final XSSFSheet sheet = workbook.createSheet("test"); | |||||
final XSSFRow srcRow = sheet.createRow(0); | |||||
srcRow.createCell(0).setCellValue("Hello"); | |||||
final XSSFRow destRow = sheet.createRow(1); | |||||
destRow.copyRowFrom(srcRow, new CellCopyPolicy()); | |||||
assertNotNull(destRow.getCell(0)); | |||||
assertEquals("Hello", destRow.getCell(0).getStringCellValue()); | |||||
workbook.close(); | |||||
} | |||||
public void testCopyRowFromExternalSheet() throws IOException { | |||||
final XSSFWorkbook workbook = new XSSFWorkbook(); | |||||
final Sheet srcSheet = workbook.createSheet("src"); | |||||
final XSSFSheet destSheet = workbook.createSheet("dest"); | |||||
workbook.createSheet("other"); | |||||
final Row srcRow = srcSheet.createRow(0); | |||||
int col = 0; | |||||
//Test 2D and 3D Ref Ptgs (Pxg for OOXML Workbooks) | |||||
srcRow.createCell(col++).setCellFormula("B5"); | |||||
srcRow.createCell(col++).setCellFormula("src!B5"); | |||||
srcRow.createCell(col++).setCellFormula("dest!B5"); | |||||
srcRow.createCell(col++).setCellFormula("other!B5"); | |||||
//Test 2D and 3D Ref Ptgs with absolute row | |||||
srcRow.createCell(col++).setCellFormula("B$5"); | |||||
srcRow.createCell(col++).setCellFormula("src!B$5"); | |||||
srcRow.createCell(col++).setCellFormula("dest!B$5"); | |||||
srcRow.createCell(col++).setCellFormula("other!B$5"); | |||||
//Test 2D and 3D Area Ptgs (Pxg for OOXML Workbooks) | |||||
srcRow.createCell(col++).setCellFormula("SUM(B5:D$5)"); | |||||
srcRow.createCell(col++).setCellFormula("SUM(src!B5:D$5)"); | |||||
srcRow.createCell(col++).setCellFormula("SUM(dest!B5:D$5)"); | |||||
srcRow.createCell(col++).setCellFormula("SUM(other!B5:D$5)"); | |||||
////////////////// | |||||
final XSSFRow destRow = destSheet.createRow(1); | |||||
destRow.copyRowFrom(srcRow, new CellCopyPolicy()); | |||||
////////////////// | |||||
//Test 2D and 3D Ref Ptgs (Pxg for OOXML Workbooks) | |||||
col = 0; | |||||
Cell cell = destRow.getCell(col++); | |||||
assertNotNull(cell); | |||||
assertEquals("RefPtg", "B6", cell.getCellFormula()); | |||||
cell = destRow.getCell(col++); | |||||
assertNotNull(cell); | |||||
assertEquals("Ref3DPtg", "src!B6", cell.getCellFormula()); | |||||
cell = destRow.getCell(col++); | |||||
assertNotNull(cell); | |||||
assertEquals("Ref3DPtg", "dest!B6", cell.getCellFormula()); | |||||
cell = destRow.getCell(col++); | |||||
assertNotNull(cell); | |||||
assertEquals("Ref3DPtg", "other!B6", cell.getCellFormula()); | |||||
///////////////////////////////////////////// | |||||
//Test 2D and 3D Ref Ptgs with absolute row (Ptg row number shouldn't change) | |||||
cell = destRow.getCell(col++); | |||||
assertNotNull(cell); | |||||
assertEquals("RefPtg", "B$5", cell.getCellFormula()); | |||||
cell = destRow.getCell(col++); | |||||
assertNotNull(cell); | |||||
assertEquals("Ref3DPtg", "src!B$5", cell.getCellFormula()); | |||||
cell = destRow.getCell(col++); | |||||
assertNotNull(cell); | |||||
assertEquals("Ref3DPtg", "dest!B$5", cell.getCellFormula()); | |||||
cell = destRow.getCell(col++); | |||||
assertNotNull(cell); | |||||
assertEquals("Ref3DPtg", "other!B$5", cell.getCellFormula()); | |||||
////////////////////////////////////////// | |||||
//Test 2D and 3D Area Ptgs (Pxg for OOXML Workbooks) | |||||
// Note: absolute row changes from last cell to first cell in order | |||||
// to maintain topLeft:bottomRight order | |||||
cell = destRow.getCell(col++); | |||||
assertNotNull(cell); | |||||
assertEquals("Area2DPtg", "SUM(B$5:D6)", cell.getCellFormula()); | |||||
cell = destRow.getCell(col++); | |||||
assertNotNull(cell); | |||||
assertEquals("Area3DPtg", "SUM(src!B$5:D6)", cell.getCellFormula()); | |||||
cell = destRow.getCell(col++); | |||||
assertNotNull(destRow.getCell(6)); | |||||
assertEquals("Area3DPtg", "SUM(dest!B$5:D6)", cell.getCellFormula()); | |||||
cell = destRow.getCell(col++); | |||||
assertNotNull(destRow.getCell(7)); | |||||
assertEquals("Area3DPtg", "SUM(other!B$5:D6)", cell.getCellFormula()); | |||||
workbook.close(); | |||||
} | |||||
public void testCopyRowOverwritesExistingRow() throws IOException { | |||||
final XSSFWorkbook workbook = new XSSFWorkbook(); | |||||
final XSSFSheet sheet1 = workbook.createSheet("Sheet1"); | |||||
final Sheet sheet2 = workbook.createSheet("Sheet2"); | |||||
final Row srcRow = sheet1.createRow(0); | |||||
final XSSFRow destRow = sheet1.createRow(1); | |||||
final Row observerRow = sheet1.createRow(2); | |||||
final Row externObserverRow = sheet2.createRow(0); | |||||
srcRow.createCell(0).setCellValue("hello"); | |||||
srcRow.createCell(1).setCellValue("world"); | |||||
destRow.createCell(0).setCellValue(5.0); //A2 -> 5.0 | |||||
destRow.createCell(1).setCellFormula("A1"); // B2 -> A1 -> "hello" | |||||
observerRow.createCell(0).setCellFormula("A2"); // A3 -> A2 -> 5.0 | |||||
observerRow.createCell(1).setCellFormula("B2"); // B3 -> B2 -> A1 -> "hello" | |||||
externObserverRow.createCell(0).setCellFormula("Sheet1!A2"); //Sheet2!A1 -> Sheet1!A2 -> 5.0 | |||||
// overwrite existing destRow with row-copy of srcRow | |||||
destRow.copyRowFrom(srcRow, new CellCopyPolicy()); | |||||
// copyRowFrom should update existing destRow, rather than creating a new row and reassigning the destRow pointer | |||||
// to the new row (and allow the old row to be garbage collected) | |||||
// this is mostly so existing references to rows that are overwritten are updated | |||||
// rather than allowing users to continue updating rows that are no longer part of the sheet | |||||
assertSame("existing references to srcRow are still valid", srcRow, sheet1.getRow(0)); | |||||
assertSame("existing references to destRow are still valid", destRow, sheet1.getRow(1)); | |||||
assertSame("existing references to observerRow are still valid", observerRow, sheet1.getRow(2)); | |||||
assertSame("existing references to externObserverRow are still valid", externObserverRow, sheet2.getRow(0)); | |||||
// Make sure copyRowFrom actually copied row (this is tested elsewhere) | |||||
assertEquals(Cell.CELL_TYPE_STRING, destRow.getCell(0).getCellType()); | |||||
assertEquals("hello", destRow.getCell(0).getStringCellValue()); | |||||
// We don't want #REF! errors if we copy a row that contains cells that are referred to by other cells outside of copied region | |||||
assertEquals("references to overwritten cells are unmodified", "A2", observerRow.getCell(0).getCellFormula()); | |||||
assertEquals("references to overwritten cells are unmodified", "B2", observerRow.getCell(1).getCellFormula()); | |||||
assertEquals("references to overwritten cells are unmodified", "Sheet1!A2", externObserverRow.getCell(0).getCellFormula()); | |||||
workbook.close(); | |||||
} | |||||
} | } |
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Calendar; | |||||
import java.util.Date; | |||||
import java.util.GregorianCalendar; | |||||
import java.util.HashSet; | import java.util.HashSet; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Set; | import java.util.Set; | ||||
import org.apache.poi.ss.usermodel.AutoFilter; | import org.apache.poi.ss.usermodel.AutoFilter; | ||||
import org.apache.poi.ss.usermodel.BaseTestSheet; | import org.apache.poi.ss.usermodel.BaseTestSheet; | ||||
import org.apache.poi.ss.usermodel.Cell; | import org.apache.poi.ss.usermodel.Cell; | ||||
import org.apache.poi.ss.usermodel.CellCopyPolicy; | |||||
import org.apache.poi.ss.usermodel.FormulaError; | |||||
import org.apache.poi.ss.usermodel.Row; | import org.apache.poi.ss.usermodel.Row; | ||||
import org.apache.poi.ss.usermodel.Sheet; | import org.apache.poi.ss.usermodel.Sheet; | ||||
import org.apache.poi.ss.usermodel.Workbook; | import org.apache.poi.ss.usermodel.Workbook; | ||||
import org.apache.poi.ss.util.AreaReference; | import org.apache.poi.ss.util.AreaReference; | ||||
import org.apache.poi.ss.util.CellRangeAddress; | import org.apache.poi.ss.util.CellRangeAddress; | ||||
import org.apache.poi.ss.util.CellReference; | import org.apache.poi.ss.util.CellReference; | ||||
import org.apache.poi.ss.util.CellUtil; | |||||
import org.apache.poi.xssf.XSSFITestDataProvider; | import org.apache.poi.xssf.XSSFITestDataProvider; | ||||
import org.apache.poi.xssf.XSSFTestDataSamples; | import org.apache.poi.xssf.XSSFTestDataSamples; | ||||
import org.apache.poi.xssf.model.CalculationChain; | import org.apache.poi.xssf.model.CalculationChain; | ||||
assertNotNull(sheet.createComment()); | assertNotNull(sheet.createComment()); | ||||
wb.close(); | wb.close(); | ||||
} | } | ||||
protected void testCopyOneRow(String copyRowsTestWorkbook) throws IOException { | |||||
final double FLOAT_PRECISION = 1e-9; | |||||
final XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook(copyRowsTestWorkbook); | |||||
final XSSFSheet sheet = workbook.getSheetAt(0); | |||||
final CellCopyPolicy defaultCopyPolicy = new CellCopyPolicy(); | |||||
sheet.copyRows(1, 1, 6, defaultCopyPolicy); | |||||
final Row srcRow = sheet.getRow(1); | |||||
final Row destRow = sheet.getRow(6); | |||||
int col = 0; | |||||
Cell cell; | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("Source row ->", cell.getStringCellValue()); | |||||
// Style | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("[Style] B7 cell value", "Red", cell.getStringCellValue()); | |||||
assertEquals("[Style] B7 cell style", CellUtil.getCell(srcRow, 1).getCellStyle(), cell.getCellStyle()); | |||||
// Blank | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("[Blank] C7 cell type", Cell.CELL_TYPE_BLANK, cell.getCellType()); | |||||
// Error | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("[Error] D7 cell type", Cell.CELL_TYPE_ERROR, cell.getCellType()); | |||||
final FormulaError error = FormulaError.forInt(cell.getErrorCellValue()); | |||||
assertEquals("[Error] D7 cell value", FormulaError.NA, error); //FIXME: XSSFCell and HSSFCell expose different interfaces. getErrorCellString would be helpful here | |||||
// Date | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("[Date] E7 cell type", Cell.CELL_TYPE_NUMERIC, cell.getCellType()); | |||||
final Date date = new GregorianCalendar(2000, Calendar.JANUARY, 1).getTime(); | |||||
assertEquals("[Date] E7 cell value", date, cell.getDateCellValue()); | |||||
// Boolean | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("[Boolean] F7 cell type", Cell.CELL_TYPE_BOOLEAN, cell.getCellType()); | |||||
assertEquals("[Boolean] F7 cell value", true, cell.getBooleanCellValue()); | |||||
// String | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("[String] G7 cell type", Cell.CELL_TYPE_STRING, cell.getCellType()); | |||||
assertEquals("[String] G7 cell value", "Hello", cell.getStringCellValue()); | |||||
// Int | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("[Int] H7 cell type", Cell.CELL_TYPE_NUMERIC, cell.getCellType()); | |||||
assertEquals("[Int] H7 cell value", 15, (int) cell.getNumericCellValue()); | |||||
// Float | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("[Float] I7 cell type", Cell.CELL_TYPE_NUMERIC, cell.getCellType()); | |||||
assertEquals("[Float] I7 cell value", 12.5, cell.getNumericCellValue(), FLOAT_PRECISION); | |||||
// Cell Formula | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("J7", new CellReference(cell).formatAsString()); | |||||
assertEquals("[Cell Formula] J7 cell type", Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula] J7 cell formula", "5+2", cell.getCellFormula()); | |||||
System.out.println("Cell formula evaluation currently unsupported"); | |||||
// Cell Formula with Reference | |||||
// Formula row references should be adjusted by destRowNum-srcRowNum | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("K7", new CellReference(cell).formatAsString()); | |||||
assertEquals("[Cell Formula with Reference] K7 cell type", | |||||
Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula with Reference] K7 cell formula", | |||||
"J7+H$2", cell.getCellFormula()); | |||||
// Cell Formula with Reference spanning multiple rows | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("[Cell Formula with Reference spanning multiple rows] L7 cell type", | |||||
Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula with Reference spanning multiple rows] L7 cell formula", | |||||
"G7&\" \"&G8", cell.getCellFormula()); | |||||
// Cell Formula with Reference spanning multiple rows | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("[Cell Formula with Area Reference] M7 cell type", | |||||
Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula with Area Reference] M7 cell formula", | |||||
"SUM(H7:I8)", cell.getCellFormula()); | |||||
// Array Formula | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
System.out.println("Array formulas currently unsupported"); | |||||
// FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() | |||||
/* | |||||
assertEquals("[Array Formula] N7 cell type", Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Array Formula] N7 cell formula", "{SUM(H7:J7*{1,2,3})}", cell.getCellFormula()); | |||||
*/ | |||||
// Data Format | |||||
cell = CellUtil.getCell(destRow, col++); | |||||
assertEquals("[Data Format] O7 cell type;", Cell.CELL_TYPE_NUMERIC, cell.getCellType()); | |||||
assertEquals("[Data Format] O7 cell value", 100.20, cell.getNumericCellValue(), FLOAT_PRECISION); | |||||
//FIXME: currently fails | |||||
final String moneyFormat = "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)"; | |||||
assertEquals("[Data Format] O7 data format", moneyFormat, cell.getCellStyle().getDataFormatString()); | |||||
// Merged | |||||
cell = CellUtil.getCell(destRow, col); | |||||
assertEquals("[Merged] P7:Q7 cell value", | |||||
"Merged cells", cell.getStringCellValue()); | |||||
assertTrue("[Merged] P7:Q7 merged region", | |||||
sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P7:Q7"))); | |||||
// Merged across multiple rows | |||||
// Microsoft Excel 2013 does not copy a merged region unless all rows of | |||||
// the source merged region are selected | |||||
// POI's behavior should match this behavior | |||||
col += 2; | |||||
cell = CellUtil.getCell(destRow, col); | |||||
// Note: this behavior deviates from Microsoft Excel, | |||||
// which will not overwrite a cell in destination row if merged region extends beyond the copied row. | |||||
// The Excel way would require: | |||||
//assertEquals("[Merged across multiple rows] R7:S8 merged region", "Should NOT be overwritten", cell.getStringCellValue()); | |||||
//assertFalse("[Merged across multiple rows] R7:S8 merged region", | |||||
// sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S8"))); | |||||
// As currently implemented, cell value is copied but merged region is not copied | |||||
assertEquals("[Merged across multiple rows] R7:S8 cell value", | |||||
"Merged cells across multiple rows", cell.getStringCellValue()); | |||||
assertFalse("[Merged across multiple rows] R7:S7 merged region (one row)", | |||||
sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S7"))); //shouldn't do 1-row merge | |||||
assertFalse("[Merged across multiple rows] R7:S8 merged region", | |||||
sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S8"))); //shouldn't do 2-row merge | |||||
// Make sure other rows are blank (off-by-one errors) | |||||
assertNull(sheet.getRow(5)); | |||||
assertNull(sheet.getRow(7)); | |||||
} | |||||
public void testCopyMultipleRows(String copyRowsTestWorkbook) throws IOException { | |||||
final double FLOAT_PRECISION = 1e-9; | |||||
final XSSFWorkbook workbook = XSSFTestDataSamples.openSampleWorkbook(copyRowsTestWorkbook); | |||||
final XSSFSheet sheet = workbook.getSheetAt(0); | |||||
final CellCopyPolicy defaultCopyPolicy = new CellCopyPolicy(); | |||||
sheet.copyRows(0, 3, 8, defaultCopyPolicy); | |||||
@SuppressWarnings("unused") | |||||
final Row srcHeaderRow = sheet.getRow(0); | |||||
final Row srcRow1 = sheet.getRow(1); | |||||
final Row srcRow2 = sheet.getRow(2); | |||||
final Row srcRow3 = sheet.getRow(3); | |||||
final Row destHeaderRow = sheet.getRow(8); | |||||
final Row destRow1 = sheet.getRow(9); | |||||
final Row destRow2 = sheet.getRow(10); | |||||
final Row destRow3 = sheet.getRow(11); | |||||
int col = 0; | |||||
Cell cell; | |||||
// Header row should be copied | |||||
assertNotNull(destHeaderRow); | |||||
// Data rows | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("Source row ->", cell.getStringCellValue()); | |||||
// Style | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Style] B10 cell value", "Red", cell.getStringCellValue()); | |||||
assertEquals("[Style] B10 cell style", CellUtil.getCell(srcRow1, 1).getCellStyle(), cell.getCellStyle()); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Style] B11 cell value", "Blue", cell.getStringCellValue()); | |||||
assertEquals("[Style] B11 cell style", CellUtil.getCell(srcRow2, 1).getCellStyle(), cell.getCellStyle()); | |||||
// Blank | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Blank] C10 cell type", Cell.CELL_TYPE_BLANK, cell.getCellType()); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Blank] C11 cell type", Cell.CELL_TYPE_BLANK, cell.getCellType()); | |||||
// Error | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Error] D10 cell type", Cell.CELL_TYPE_ERROR, cell.getCellType()); | |||||
FormulaError error = FormulaError.forInt(cell.getErrorCellValue()); | |||||
assertEquals("[Error] D10 cell value", FormulaError.NA, error); //FIXME: XSSFCell and HSSFCell expose different interfaces. getErrorCellString would be helpful here | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Error] D11 cell type", Cell.CELL_TYPE_ERROR, cell.getCellType()); | |||||
error = FormulaError.forInt(cell.getErrorCellValue()); | |||||
assertEquals("[Error] D11 cell value", FormulaError.NAME, error); //FIXME: XSSFCell and HSSFCell expose different interfaces. getErrorCellString would be helpful here | |||||
// Date | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Date] E10 cell type", Cell.CELL_TYPE_NUMERIC, cell.getCellType()); | |||||
Date date = new GregorianCalendar(2000, Calendar.JANUARY, 1).getTime(); | |||||
assertEquals("[Date] E10 cell value", date, cell.getDateCellValue()); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Date] E11 cell type", Cell.CELL_TYPE_NUMERIC, cell.getCellType()); | |||||
date = new GregorianCalendar(2000, Calendar.JANUARY, 2).getTime(); | |||||
assertEquals("[Date] E11 cell value", date, cell.getDateCellValue()); | |||||
// Boolean | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Boolean] F10 cell type", Cell.CELL_TYPE_BOOLEAN, cell.getCellType()); | |||||
assertEquals("[Boolean] F10 cell value", true, cell.getBooleanCellValue()); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Boolean] F11 cell type", Cell.CELL_TYPE_BOOLEAN, cell.getCellType()); | |||||
assertEquals("[Boolean] F11 cell value", false, cell.getBooleanCellValue()); | |||||
// String | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[String] G10 cell type", Cell.CELL_TYPE_STRING, cell.getCellType()); | |||||
assertEquals("[String] G10 cell value", "Hello", cell.getStringCellValue()); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[String] G11 cell type", Cell.CELL_TYPE_STRING, cell.getCellType()); | |||||
assertEquals("[String] G11 cell value", "World", cell.getStringCellValue()); | |||||
// Int | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Int] H10 cell type", Cell.CELL_TYPE_NUMERIC, cell.getCellType()); | |||||
assertEquals("[Int] H10 cell value", 15, (int) cell.getNumericCellValue()); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Int] H11 cell type", Cell.CELL_TYPE_NUMERIC, cell.getCellType()); | |||||
assertEquals("[Int] H11 cell value", 42, (int) cell.getNumericCellValue()); | |||||
// Float | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Float] I10 cell type", Cell.CELL_TYPE_NUMERIC, cell.getCellType()); | |||||
assertEquals("[Float] I10 cell value", 12.5, cell.getNumericCellValue(), FLOAT_PRECISION); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Float] I11 cell type", Cell.CELL_TYPE_NUMERIC, cell.getCellType()); | |||||
assertEquals("[Float] I11 cell value", 5.5, cell.getNumericCellValue(), FLOAT_PRECISION); | |||||
// Cell Formula | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Cell Formula] J10 cell type", Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula] J10 cell formula", "5+2", cell.getCellFormula()); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Cell Formula] J11 cell type", Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula] J11 cell formula", "6+18", cell.getCellFormula()); | |||||
// Cell Formula with Reference | |||||
col++; | |||||
// Formula row references should be adjusted by destRowNum-srcRowNum | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Cell Formula with Reference] K10 cell type", | |||||
Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula with Reference] K10 cell formula", | |||||
"J10+H$2", cell.getCellFormula()); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Cell Formula with Reference] K11 cell type", Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula with Reference] K11 cell formula", "J11+H$2", cell.getCellFormula()); | |||||
// Cell Formula with Reference spanning multiple rows | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Cell Formula with Reference spanning multiple rows] L10 cell type", | |||||
Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula with Reference spanning multiple rows] L10 cell formula", | |||||
"G10&\" \"&G11", cell.getCellFormula()); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Cell Formula with Reference spanning multiple rows] L11 cell type", | |||||
Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula with Reference spanning multiple rows] L11 cell formula", | |||||
"G11&\" \"&G12", cell.getCellFormula()); | |||||
// Cell Formula with Area Reference | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Cell Formula with Area Reference] M10 cell type", | |||||
Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula with Area Reference] M10 cell formula", | |||||
"SUM(H10:I11)", cell.getCellFormula()); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Cell Formula with Area Reference] M11 cell type", | |||||
Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Cell Formula with Area Reference] M11 cell formula", | |||||
"SUM($H$3:I10)", cell.getCellFormula()); //Also acceptable: SUM($H10:I$3), but this AreaReference isn't in ascending order | |||||
// Array Formula | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
System.out.println("Array formulas currently unsupported"); | |||||
/* | |||||
// FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() | |||||
assertEquals("[Array Formula] N10 cell type", Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Array Formula] N10 cell formula", "{SUM(H10:J10*{1,2,3})}", cell.getCellFormula()); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
// FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() | |||||
assertEquals("[Array Formula] N11 cell type", Cell.CELL_TYPE_FORMULA, cell.getCellType()); | |||||
assertEquals("[Array Formula] N11 cell formula", "{SUM(H11:J11*{1,2,3})}", cell.getCellFormula()); | |||||
*/ | |||||
// Data Format | |||||
col++; | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Data Format] O10 cell type", Cell.CELL_TYPE_NUMERIC, cell.getCellType()); | |||||
assertEquals("[Data Format] O10 cell value", 100.20, cell.getNumericCellValue(), FLOAT_PRECISION); | |||||
final String moneyFormat = "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)"; | |||||
assertEquals("[Data Format] O10 cell data format", moneyFormat, cell.getCellStyle().getDataFormatString()); | |||||
// Merged | |||||
col++; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Merged] P10:Q10 cell value", | |||||
"Merged cells", cell.getStringCellValue()); | |||||
assertTrue("[Merged] P10:Q10 merged region", | |||||
sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P10:Q10"))); | |||||
cell = CellUtil.getCell(destRow2, col); | |||||
assertEquals("[Merged] P11:Q11 cell value", "Merged cells", cell.getStringCellValue()); | |||||
assertTrue("[Merged] P11:Q11 merged region", | |||||
sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P11:Q11"))); | |||||
// Should Q10/Q11 be checked? | |||||
// Merged across multiple rows | |||||
// Microsoft Excel 2013 does not copy a merged region unless all rows of | |||||
// the source merged region are selected | |||||
// POI's behavior should match this behavior | |||||
col += 2; | |||||
cell = CellUtil.getCell(destRow1, col); | |||||
assertEquals("[Merged across multiple rows] R10:S11 cell value", | |||||
"Merged cells across multiple rows", cell.getStringCellValue()); | |||||
assertTrue("[Merged across multiple rows] R10:S11 merged region", | |||||
sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R10:S11"))); | |||||
// Row 3 (zero-based) was empty, so Row 11 (zero-based) should be empty too. | |||||
if (srcRow3 == null) { | |||||
assertNull("Row 3 was empty, so Row 11 should be empty", destRow3); | |||||
} | |||||
// Make sure other rows are blank (off-by-one errors) | |||||
assertNull("Off-by-one lower edge case", sheet.getRow(7)); //one row above destHeaderRow | |||||
assertNull("Off-by-one upper edge case", sheet.getRow(12)); //one row below destRow3 | |||||
} | |||||
@Test | |||||
public void testCopyOneRow() throws IOException { | |||||
testCopyOneRow("XSSFSheet.copyRows.xlsx"); | |||||
} | |||||
@Test | |||||
public void testCopyMultipleRows() throws IOException { | |||||
testCopyMultipleRows("XSSFSheet.copyRows.xlsx"); | |||||
} | |||||
} | } |