You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RangeCopier.java 9.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /*
  2. * ====================================================================
  3. * Licensed to the Apache Software Foundation (ASF) under one or more
  4. * contributor license agreements. See the NOTICE file distributed with
  5. * this work for additional information regarding copyright ownership.
  6. * The ASF licenses this file to You under the Apache License, Version 2.0
  7. * (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. * ====================================================================
  18. */
  19. package org.apache.poi.ss.usermodel;
  20. import java.util.HashMap;
  21. import java.util.Map;
  22. import org.apache.poi.ss.formula.FormulaShifter;
  23. import org.apache.poi.ss.formula.ptg.Ptg;
  24. import org.apache.poi.ss.util.CellRangeAddress;
  25. import org.apache.poi.util.Beta;
  26. @Beta
  27. public abstract class RangeCopier {
  28. private Sheet sourceSheet;
  29. private Sheet destSheet;
  30. private FormulaShifter horizontalFormulaShifter;
  31. private FormulaShifter verticalFormulaShifter;
  32. public RangeCopier(Sheet sourceSheet, Sheet destSheet) {
  33. this.sourceSheet = sourceSheet;
  34. this.destSheet = destSheet;
  35. }
  36. public RangeCopier(Sheet sheet) {
  37. this(sheet, sheet);
  38. }
  39. /** Uses input pattern to tile destination region, overwriting existing content. Works in following manner :
  40. * 1.Start from top-left of destination.
  41. * 2.Paste source but only inside of destination borders.
  42. * 3.If there is space left on right or bottom side of copy, process it as in step 2.
  43. * @param tilePatternRange source range which should be copied in tiled manner
  44. * @param tileDestRange destination range, which should be overridden
  45. */
  46. public void copyRange(CellRangeAddress tilePatternRange, CellRangeAddress tileDestRange) {
  47. copyRange(tilePatternRange, tileDestRange, false, false);
  48. }
  49. /** Uses input pattern to tile destination region, overwriting existing content. Works in following manner :
  50. * 1.Start from top-left of destination.
  51. * 2.Paste source but only inside of destination borders.
  52. * 3.If there is space left on right or bottom side of copy, process it as in step 2.
  53. * @param tilePatternRange source range which should be copied in tiled manner
  54. * @param tileDestRange destination range, which should be overridden
  55. * @param copyStyles whether to copy the cell styles
  56. * @param copyMergedRanges whether to copy merged ranges
  57. * @since 5.0.0
  58. */
  59. public void copyRange(CellRangeAddress tilePatternRange, CellRangeAddress tileDestRange, boolean copyStyles, boolean copyMergedRanges) {
  60. Sheet sourceCopy = sourceSheet.getWorkbook().cloneSheet(sourceSheet.getWorkbook().getSheetIndex(sourceSheet));
  61. Map<Integer, CellStyle> styleMap = copyStyles ? new HashMap<Integer, CellStyle>() {} : null;
  62. int sourceWidthMinus1 = tilePatternRange.getLastColumn() - tilePatternRange.getFirstColumn();
  63. int sourceHeightMinus1 = tilePatternRange.getLastRow() - tilePatternRange.getFirstRow();
  64. int rightLimitToCopy;
  65. int bottomLimitToCopy;
  66. int nextRowIndexToCopy = tileDestRange.getFirstRow();
  67. do {
  68. int nextCellIndexInRowToCopy = tileDestRange.getFirstColumn();
  69. int heightToCopyMinus1 = Math.min(sourceHeightMinus1, tileDestRange.getLastRow() - nextRowIndexToCopy);
  70. bottomLimitToCopy = tilePatternRange.getFirstRow() + heightToCopyMinus1;
  71. do {
  72. int widthToCopyMinus1 = Math.min(sourceWidthMinus1, tileDestRange.getLastColumn() - nextCellIndexInRowToCopy);
  73. rightLimitToCopy = tilePatternRange.getFirstColumn() + widthToCopyMinus1;
  74. CellRangeAddress rangeToCopy = new CellRangeAddress(
  75. tilePatternRange.getFirstRow(), bottomLimitToCopy,
  76. tilePatternRange.getFirstColumn(), rightLimitToCopy
  77. );
  78. copyRange(rangeToCopy, nextCellIndexInRowToCopy - rangeToCopy.getFirstColumn(), nextRowIndexToCopy - rangeToCopy.getFirstRow(), sourceCopy, styleMap);
  79. nextCellIndexInRowToCopy += widthToCopyMinus1 + 1;
  80. } while (nextCellIndexInRowToCopy <= tileDestRange.getLastColumn());
  81. nextRowIndexToCopy += heightToCopyMinus1 + 1;
  82. } while (nextRowIndexToCopy <= tileDestRange.getLastRow());
  83. if (copyMergedRanges) {
  84. sourceSheet.getMergedRegions().forEach((mergedRangeAddress) -> destSheet.addMergedRegion(mergedRangeAddress));
  85. }
  86. int tempCopyIndex = sourceSheet.getWorkbook().getSheetIndex(sourceCopy);
  87. sourceSheet.getWorkbook().removeSheetAt(tempCopyIndex);
  88. }
  89. private void copyRange(CellRangeAddress sourceRange, int deltaX, int deltaY, Sheet sourceClone, Map<Integer, CellStyle> styleMap) { //NOSONAR, it's a bit complex but monolith method, does not make much sense to divide it
  90. if(deltaX != 0)
  91. horizontalFormulaShifter = FormulaShifter.createForColumnCopy(sourceSheet.getWorkbook().getSheetIndex(sourceSheet),
  92. sourceSheet.getSheetName(), sourceRange.getFirstColumn(), sourceRange.getLastColumn(), deltaX, sourceSheet.getWorkbook().getSpreadsheetVersion());
  93. if(deltaY != 0)
  94. verticalFormulaShifter = FormulaShifter.createForRowCopy(sourceSheet.getWorkbook().getSheetIndex(sourceSheet),
  95. sourceSheet.getSheetName(), sourceRange.getFirstRow(), sourceRange.getLastRow(), deltaY, sourceSheet.getWorkbook().getSpreadsheetVersion());
  96. for(int rowNo = sourceRange.getFirstRow(); rowNo <= sourceRange.getLastRow(); rowNo++) {
  97. // copy from source copy, original source might be overridden in process!
  98. Row sourceRow = sourceClone.getRow(rowNo);
  99. if(sourceRow == null) {
  100. continue;
  101. }
  102. for (int columnIndex = sourceRange.getFirstColumn(); columnIndex <= sourceRange.getLastColumn(); columnIndex++) {
  103. Cell sourceCell = sourceRow.getCell(columnIndex);
  104. if(sourceCell == null)
  105. continue;
  106. Row destRow = destSheet.getRow(rowNo + deltaY);
  107. if(destRow == null)
  108. destRow = destSheet.createRow(rowNo + deltaY);
  109. Cell newCell = destRow.getCell(columnIndex + deltaX);
  110. if(newCell == null) {
  111. newCell = destRow.createCell(columnIndex + deltaX);
  112. }
  113. cloneCellContent(sourceCell, newCell, styleMap);
  114. if(newCell.getCellType() == CellType.FORMULA)
  115. adjustCellReferencesInsideFormula(newCell, destSheet, deltaX, deltaY);
  116. }
  117. }
  118. }
  119. protected abstract void adjustCellReferencesInsideFormula(Cell cell, Sheet destSheet, int deltaX, int deltaY); // this part is different for HSSF and XSSF
  120. protected boolean adjustInBothDirections(Ptg[] ptgs, int sheetIndex, int deltaX, int deltaY) {
  121. boolean adjustSucceeded = true;
  122. if(deltaY != 0)
  123. adjustSucceeded = verticalFormulaShifter.adjustFormula(ptgs, sheetIndex);
  124. if(deltaX != 0)
  125. adjustSucceeded = adjustSucceeded && horizontalFormulaShifter.adjustFormula(ptgs, sheetIndex);
  126. return adjustSucceeded;
  127. }
  128. // TODO clone some more properties ?
  129. public static void cloneCellContent(Cell srcCell, Cell destCell, Map<Integer, CellStyle> styleMap) {
  130. if(styleMap != null) {
  131. if(srcCell.getSheet().getWorkbook() == destCell.getSheet().getWorkbook()){
  132. destCell.setCellStyle(srcCell.getCellStyle());
  133. } else {
  134. int stHashCode = srcCell.getCellStyle().hashCode();
  135. CellStyle newCellStyle = styleMap.get(stHashCode);
  136. if(newCellStyle == null){
  137. newCellStyle = destCell.getSheet().getWorkbook().createCellStyle();
  138. newCellStyle.cloneStyleFrom(srcCell.getCellStyle());
  139. styleMap.put(stHashCode, newCellStyle);
  140. }
  141. destCell.setCellStyle(newCellStyle);
  142. }
  143. }
  144. switch(srcCell.getCellType()) {
  145. case STRING:
  146. destCell.setCellValue(srcCell.getStringCellValue());
  147. break;
  148. case NUMERIC:
  149. destCell.setCellValue(srcCell.getNumericCellValue());
  150. break;
  151. case BLANK:
  152. destCell.setBlank();
  153. break;
  154. case BOOLEAN:
  155. destCell.setCellValue(srcCell.getBooleanCellValue());
  156. break;
  157. case ERROR:
  158. destCell.setCellErrorValue(srcCell.getErrorCellValue());
  159. break;
  160. case FORMULA:
  161. String oldFormula = srcCell.getCellFormula();
  162. destCell.setCellFormula(oldFormula);
  163. break;
  164. default:
  165. break;
  166. }
  167. }
  168. }