git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1824266 13f79535-47bb-0310-9956-ffa450edef68tags/REL_4_0_0_FINAL
@@ -0,0 +1,42 @@ | |||
/* | |||
* ==================================================================== | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
* ==================================================================== | |||
*/ | |||
package org.apache.poi.hssf.usermodel; | |||
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; | |||
import org.apache.poi.ss.formula.ptg.Ptg; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
import org.apache.poi.ss.usermodel.RangeCopier; | |||
import org.apache.poi.ss.usermodel.Sheet; | |||
import org.apache.poi.util.Beta; | |||
@Beta | |||
public class HSSFRangeCopier extends RangeCopier { | |||
public HSSFRangeCopier(Sheet sourceSheet, Sheet destSheet) { | |||
super(sourceSheet, destSheet); | |||
} | |||
protected void adjustCellReferencesInsideFormula(Cell cell, Sheet destSheet, int deltaX, int deltaY) { | |||
FormulaRecordAggregate fra = (FormulaRecordAggregate)((HSSFCell)cell).getCellValueRecord(); | |||
int destSheetIndex = destSheet.getWorkbook().getSheetIndex(destSheet); | |||
Ptg[] ptgs = fra.getFormulaTokens(); | |||
if(adjustInBothDirections(ptgs, destSheetIndex, deltaX, deltaY)) | |||
fra.setParsedExpression(ptgs); | |||
} | |||
} |
@@ -0,0 +1,159 @@ | |||
/* | |||
* ==================================================================== | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
* ==================================================================== | |||
*/ | |||
package org.apache.poi.ss.usermodel; | |||
import java.util.Map; | |||
import org.apache.poi.ss.formula.FormulaShifter; | |||
import org.apache.poi.ss.formula.ptg.Ptg; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.util.Beta; | |||
@Beta | |||
public abstract class RangeCopier { | |||
private Sheet sourceSheet; | |||
private Sheet destSheet; | |||
private FormulaShifter horizontalFormulaShifter; | |||
private FormulaShifter verticalFormulaShifter; | |||
public RangeCopier(Sheet sourceSheet, Sheet destSheet) { | |||
this.sourceSheet = sourceSheet; | |||
this.destSheet = destSheet; | |||
} | |||
public RangeCopier(Sheet sheet) { | |||
this(sheet, sheet); | |||
} | |||
/** Uses input pattern to tile destination region, overwriting existing content. Works in following manner : | |||
* 1.Start from top-left of destination. | |||
* 2.Paste source but only inside of destination borders. | |||
* 3.If there is space left on right or bottom side of copy, process it as in step 2. | |||
* @param tilePatternRange source range which should be copied in tiled manner | |||
* @param tileDestRange destination range, which should be overridden | |||
*/ | |||
public void copyRange(CellRangeAddress tilePatternRange, CellRangeAddress tileDestRange) { | |||
Sheet sourceCopy = sourceSheet.getWorkbook().cloneSheet(sourceSheet.getWorkbook().getSheetIndex(sourceSheet)); | |||
int sourceWidthMinus1 = tilePatternRange.getLastColumn() - tilePatternRange.getFirstColumn(); | |||
int sourceHeightMinus1 = tilePatternRange.getLastRow() - tilePatternRange.getFirstRow(); | |||
int rightLimitToCopy; | |||
int bottomLimitToCopy; | |||
int nextRowIndexToCopy = tileDestRange.getFirstRow(); | |||
do { | |||
int nextCellIndexInRowToCopy = tileDestRange.getFirstColumn(); | |||
int heightToCopyMinus1 = Math.min(sourceHeightMinus1, tileDestRange.getLastRow() - nextRowIndexToCopy); | |||
bottomLimitToCopy = tilePatternRange.getFirstRow() + heightToCopyMinus1; | |||
do { | |||
int widthToCopyMinus1 = Math.min(sourceWidthMinus1, tileDestRange.getLastColumn() - nextCellIndexInRowToCopy); | |||
rightLimitToCopy = tilePatternRange.getFirstColumn() + widthToCopyMinus1; | |||
CellRangeAddress rangeToCopy = new CellRangeAddress( | |||
tilePatternRange.getFirstRow(), bottomLimitToCopy, | |||
tilePatternRange.getFirstColumn(), rightLimitToCopy | |||
); | |||
copyRange(rangeToCopy, nextCellIndexInRowToCopy - rangeToCopy.getFirstColumn(), nextRowIndexToCopy - rangeToCopy.getFirstRow(), sourceCopy); | |||
nextCellIndexInRowToCopy += widthToCopyMinus1 + 1; | |||
} while (nextCellIndexInRowToCopy <= tileDestRange.getLastColumn()); | |||
nextRowIndexToCopy += heightToCopyMinus1 + 1; | |||
} while (nextRowIndexToCopy <= tileDestRange.getLastRow()); | |||
int tempCopyIndex = sourceSheet.getWorkbook().getSheetIndex(sourceCopy); | |||
sourceSheet.getWorkbook().removeSheetAt(tempCopyIndex); | |||
} | |||
private void copyRange(CellRangeAddress sourceRange, int deltaX, int deltaY, Sheet sourceClone) { //NOSONAR, it's a bit complex but monolith method, does not make much sense to divide it | |||
if(deltaX != 0) | |||
horizontalFormulaShifter = FormulaShifter.createForColumnCopy(sourceSheet.getWorkbook().getSheetIndex(sourceSheet), | |||
sourceSheet.getSheetName(), sourceRange.getFirstColumn(), sourceRange.getLastColumn(), deltaX, sourceSheet.getWorkbook().getSpreadsheetVersion()); | |||
if(deltaY != 0) | |||
verticalFormulaShifter = FormulaShifter.createForRowCopy(sourceSheet.getWorkbook().getSheetIndex(sourceSheet), | |||
sourceSheet.getSheetName(), sourceRange.getFirstRow(), sourceRange.getLastRow(), deltaY, sourceSheet.getWorkbook().getSpreadsheetVersion()); | |||
for(int rowNo = sourceRange.getFirstRow(); rowNo <= sourceRange.getLastRow(); rowNo++) { | |||
Row sourceRow = sourceClone.getRow(rowNo); // copy from source copy, original source might be overridden in process! | |||
for (int columnIndex = sourceRange.getFirstColumn(); columnIndex <= sourceRange.getLastColumn(); columnIndex++) { | |||
Cell sourceCell = sourceRow.getCell(columnIndex); | |||
if(sourceCell == null) | |||
continue; | |||
Row destRow = destSheet.getRow(rowNo + deltaY); | |||
if(destRow == null) | |||
destRow = destSheet.createRow(rowNo + deltaY); | |||
Cell newCell = destRow.getCell(columnIndex + deltaX); | |||
if(newCell != null) | |||
newCell.setCellType(sourceCell.getCellType()); | |||
else newCell = destRow.createCell(columnIndex + deltaX, sourceCell.getCellType()); | |||
cloneCellContent(sourceCell, newCell, null); | |||
if(newCell.getCellType() == CellType.FORMULA) | |||
adjustCellReferencesInsideFormula(newCell, destSheet, deltaX, deltaY); | |||
} | |||
} | |||
} | |||
protected abstract void adjustCellReferencesInsideFormula(Cell cell, Sheet destSheet, int deltaX, int deltaY); // this part is different for HSSF and XSSF | |||
protected boolean adjustInBothDirections(Ptg[] ptgs, int sheetIndex, int deltaX, int deltaY) { | |||
boolean adjustSucceeded = true; | |||
if(deltaY != 0) | |||
adjustSucceeded = verticalFormulaShifter.adjustFormula(ptgs, sheetIndex); | |||
if(deltaX != 0) | |||
adjustSucceeded = adjustSucceeded && horizontalFormulaShifter.adjustFormula(ptgs, sheetIndex); | |||
return adjustSucceeded; | |||
} | |||
// TODO clone some more properties ? | |||
public static void cloneCellContent(Cell srcCell, Cell destCell, Map<Integer, CellStyle> styleMap) { | |||
if(styleMap != null) { | |||
if(srcCell.getSheet().getWorkbook() == destCell.getSheet().getWorkbook()){ | |||
destCell.setCellStyle(srcCell.getCellStyle()); | |||
} else { | |||
int stHashCode = srcCell.getCellStyle().hashCode(); | |||
CellStyle newCellStyle = styleMap.get(stHashCode); | |||
if(newCellStyle == null){ | |||
newCellStyle = destCell.getSheet().getWorkbook().createCellStyle(); | |||
newCellStyle.cloneStyleFrom(srcCell.getCellStyle()); | |||
styleMap.put(stHashCode, newCellStyle); | |||
} | |||
destCell.setCellStyle(newCellStyle); | |||
} | |||
} | |||
switch(srcCell.getCellType()) { | |||
case STRING: | |||
destCell.setCellValue(srcCell.getStringCellValue()); | |||
break; | |||
case NUMERIC: | |||
destCell.setCellValue(srcCell.getNumericCellValue()); | |||
break; | |||
case BLANK: | |||
destCell.setCellType(CellType.BLANK); | |||
break; | |||
case BOOLEAN: | |||
destCell.setCellValue(srcCell.getBooleanCellValue()); | |||
break; | |||
case ERROR: | |||
destCell.setCellErrorValue(srcCell.getErrorCellValue()); | |||
break; | |||
case FORMULA: | |||
String oldFormula = srcCell.getCellFormula(); | |||
destCell.setCellFormula(oldFormula); | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
/* | |||
* ==================================================================== | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
* ==================================================================== | |||
*/ | |||
package org.apache.poi.xssf.usermodel; | |||
import org.apache.poi.ss.formula.FormulaParser; | |||
import org.apache.poi.ss.formula.FormulaRenderer; | |||
import org.apache.poi.ss.formula.FormulaType; | |||
import org.apache.poi.ss.formula.ptg.Ptg; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
import org.apache.poi.ss.usermodel.RangeCopier; | |||
import org.apache.poi.ss.usermodel.Sheet; | |||
import org.apache.poi.util.Beta; | |||
@Beta | |||
public class XSSFRangeCopier extends RangeCopier { | |||
public XSSFRangeCopier(Sheet sourceSheet, Sheet destSheet){ | |||
super(sourceSheet, destSheet); | |||
} | |||
protected void adjustCellReferencesInsideFormula(Cell cell, Sheet destSheet, int deltaX, int deltaY){ | |||
XSSFWorkbook hostWorkbook = (XSSFWorkbook) destSheet.getWorkbook(); | |||
XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(hostWorkbook); | |||
Ptg[] ptgs = FormulaParser.parse(cell.getCellFormula(), fpb, FormulaType.CELL, 0); | |||
int destSheetIndex = hostWorkbook.getSheetIndex(destSheet); | |||
if(adjustInBothDirections(ptgs, destSheetIndex, deltaX, deltaY)) | |||
cell.setCellFormula(FormulaRenderer.toFormulaString(fpb, ptgs)); | |||
} | |||
} |
@@ -0,0 +1,56 @@ | |||
/* | |||
* ==================================================================== | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
* ==================================================================== | |||
*/ | |||
package org.apache.poi.ss.usermodel; | |||
import static org.junit.Assert.assertEquals; | |||
import org.apache.poi.xssf.XSSFITestDataProvider; | |||
import org.apache.poi.xssf.XSSFTestDataSamples; | |||
import org.apache.poi.xssf.usermodel.XSSFRangeCopier; | |||
import org.apache.poi.xssf.usermodel.XSSFRow; | |||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
public class TestXSSFRangeCopier extends TestRangeCopier { | |||
public TestXSSFRangeCopier() { | |||
super(); | |||
workbook = new XSSFWorkbook(); | |||
testDataProvider = XSSFITestDataProvider.instance; | |||
} | |||
@Before | |||
public void init() { | |||
workbook = XSSFTestDataSamples.openSampleWorkbook("tile-range-test.xlsx"); | |||
initSheets(); | |||
rangeCopier = new XSSFRangeCopier(sheet1, sheet1); | |||
transSheetRangeCopier = new XSSFRangeCopier(sheet1, sheet2); | |||
} | |||
@Test // XSSF only. HSSF version wouldn't be so simple. And also this test is contained in following, more complex tests, so it's not really important. | |||
public void copyRow() { | |||
Row existingRow = sheet1.getRow(4); | |||
XSSFRow newRow = (XSSFRow)sheet1.getRow(5); | |||
CellCopyPolicy policy = new CellCopyPolicy(); | |||
newRow.copyRowFrom(existingRow, policy); | |||
assertEquals("$C2+B$2", newRow.getCell(1).getCellFormula()); | |||
} | |||
} |
@@ -40,4 +40,4 @@ public class TestXSSFSheetShiftColumns extends BaseTestSheetShiftColumns { | |||
return XSSFTestDataSamples.writeOutAndReadBack(wb); | |||
} | |||
} | |||
} |
@@ -31,5 +31,4 @@ public class TestXSSFColumnShifting extends BaseTestColumnShifting { | |||
protected void initColumnShifter(){ | |||
columnShifter = new XSSFColumnShifter((XSSFSheet)sheet1); | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
/* | |||
* ==================================================================== | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
* ==================================================================== | |||
*/ | |||
package org.apache.poi.hssf.usermodel; | |||
import org.apache.poi.hssf.HSSFITestDataProvider; | |||
import org.apache.poi.hssf.HSSFTestDataSamples; | |||
import org.apache.poi.ss.usermodel.TestRangeCopier; | |||
import org.junit.Before; | |||
public class TestHSSFRangeCopier extends TestRangeCopier { | |||
public TestHSSFRangeCopier() { | |||
super(); | |||
workbook = new HSSFWorkbook(); | |||
testDataProvider = HSSFITestDataProvider.instance; | |||
} | |||
@Before | |||
public void init() { | |||
workbook = HSSFTestDataSamples.openSampleWorkbook("tile-range-test.xls"); | |||
initSheets(); | |||
rangeCopier = new HSSFRangeCopier(sheet1, sheet1); | |||
transSheetRangeCopier = new HSSFRangeCopier(sheet1, sheet2); | |||
} | |||
} |
@@ -79,7 +79,6 @@ public class TestHSSFSheetShiftColumns extends BaseTestSheetShiftColumns { | |||
// 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 |
@@ -94,7 +94,6 @@ public abstract class BaseTestSheetShiftColumns { | |||
style.setVerticalAlignment(VerticalAlignment.BOTTOM); | |||
return style; | |||
} | |||
@Test | |||
public void testShiftOneColumnRight() { | |||
sheet1.shiftColumns(1, 2, 1); | |||
@@ -271,7 +270,6 @@ public abstract class BaseTestSheetShiftColumns { | |||
// 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)); | |||
@@ -398,6 +396,4 @@ public abstract class BaseTestSheetShiftColumns { | |||
assertEquals("X", cell.getStringCellValue()); | |||
wb.close(); | |||
} | |||
} |
@@ -0,0 +1,99 @@ | |||
/* | |||
* ==================================================================== | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
* ==================================================================== | |||
*/ | |||
package org.apache.poi.ss.usermodel; | |||
import static org.junit.Assert.assertEquals; | |||
import org.junit.Ignore; | |||
import org.junit.Test; | |||
import org.apache.poi.ss.ITestDataProvider; | |||
import org.apache.poi.ss.util.CellRangeAddress; | |||
import org.apache.poi.ss.util.CellReference; | |||
@Ignore | |||
public abstract class TestRangeCopier { | |||
protected Sheet sheet1; | |||
protected Sheet sheet2; | |||
protected Workbook workbook; | |||
protected RangeCopier rangeCopier; | |||
protected RangeCopier transSheetRangeCopier; | |||
protected ITestDataProvider testDataProvider; | |||
protected void initSheets() { | |||
sheet1 = workbook.getSheet("sheet1"); | |||
sheet2 = workbook.getSheet("sheet2"); | |||
} | |||
@Test | |||
public void copySheetRangeWithoutFormulas() { | |||
CellRangeAddress rangeToCopy = CellRangeAddress.valueOf("B1:C2"); //2x2 | |||
CellRangeAddress destRange = CellRangeAddress.valueOf("C2:D3"); //2x2 | |||
rangeCopier.copyRange(rangeToCopy, destRange); | |||
assertEquals("1.1", sheet1.getRow(2).getCell(2).toString()); | |||
assertEquals("2.1", sheet1.getRow(2).getCell(3).toString()); | |||
} | |||
@Test | |||
public void tileTheRangeAway() { | |||
CellRangeAddress tileRange = CellRangeAddress.valueOf("C4:D5"); | |||
CellRangeAddress destRange = CellRangeAddress.valueOf("F4:K5"); | |||
rangeCopier.copyRange(tileRange, destRange); | |||
assertEquals("1.3", getCellContent(sheet1, "H4")); | |||
assertEquals("1.3", getCellContent(sheet1, "J4")); | |||
assertEquals("$C1+G$2", getCellContent(sheet1, "G5")); | |||
assertEquals("SUM(G3:I3)", getCellContent(sheet1, "H5")); | |||
assertEquals("$C1+I$2", getCellContent(sheet1, "I5")); | |||
assertEquals("", getCellContent(sheet1, "L5")); //out of borders | |||
assertEquals("", getCellContent(sheet1, "G7")); //out of borders | |||
} | |||
@Test | |||
public void tileTheRangeOver() { | |||
CellRangeAddress tileRange = CellRangeAddress.valueOf("C4:D5"); | |||
CellRangeAddress destRange = CellRangeAddress.valueOf("A4:C5"); | |||
rangeCopier.copyRange(tileRange, destRange); | |||
assertEquals("1.3", getCellContent(sheet1, "A4")); | |||
assertEquals("$C1+B$2", getCellContent(sheet1, "B5")); | |||
assertEquals("SUM(B3:D3)", getCellContent(sheet1, "C5")); | |||
} | |||
@Test | |||
public void copyRangeToOtherSheet() { | |||
Sheet destSheet = sheet2; | |||
CellRangeAddress tileRange = CellRangeAddress.valueOf("C4:D5"); // on sheet1 | |||
CellRangeAddress destRange = CellRangeAddress.valueOf("F4:J6"); // on sheet2 | |||
transSheetRangeCopier.copyRange(tileRange, destRange); | |||
assertEquals("1.3", getCellContent(destSheet, "H4")); | |||
assertEquals("1.3", getCellContent(destSheet, "J4")); | |||
assertEquals("$C1+G$2", getCellContent(destSheet, "G5")); | |||
assertEquals("SUM(G3:I3)", getCellContent(destSheet, "H5")); | |||
assertEquals("$C1+I$2", getCellContent(destSheet, "I5")); | |||
} | |||
protected static String getCellContent(Sheet sheet, String coordinates) { | |||
try { | |||
CellReference p = new CellReference(coordinates); | |||
return sheet.getRow(p.getRow()).getCell(p.getCol()).toString(); | |||
} | |||
catch (NullPointerException e) { // row or cell does not exist | |||
return ""; | |||
} | |||
} | |||
} |