diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index 9b10b70af..89b3c9e99 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -50,6 +50,7 @@ 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.HSSFRowShifter; import org.apache.poi.hssf.util.PaneInformation; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.FormulaShifter; @@ -64,6 +65,7 @@ import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.DataValidation; import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.helpers.RowShifter; import org.apache.poi.ss.util.CellAddress; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddressList; @@ -1485,50 +1487,11 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { * @param endRow the end-index of the rows to shift, zero-based * @param n how far to shift, negative to shift up * @param isRow unused, kept for backwards compatibility - * @deprecated POI 3.15 beta 2. This will be made private in future releases. + * @deprecated POI 3.15 beta 2. Use {@link HSSFRowShifter#shiftMergedRegions(int, int, int)}. */ protected void shiftMerged(int startRow, int endRow, int n, boolean isRow) { - shiftMerged(startRow, endRow, n); - } - - /** - * Shifts, grows, or shrinks the merged regions due to a row shift - * - * @param startRow the start-index of the rows to shift, zero-based - * @param endRow the end-index of the rows to shift, zero-based - * @param n how far to shift, negative to shift up - * This should be kept in sync with {@link org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter#shiftMerged(int, int, int)} - */ - private void shiftMerged(int startRow, int endRow, int n) { - List shiftedRegions = new ArrayList(); - //move merged regions completely if they fall within the new region boundaries when they are shifted - for (int i = 0; i < getNumMergedRegions(); i++) { - CellRangeAddress merged = getMergedRegion(i); - - boolean inStart = (merged.getFirstRow() >= startRow || merged.getLastRow() >= startRow); - boolean inEnd = (merged.getFirstRow() <= endRow || merged.getLastRow() <= endRow); - - //don't check if it's not within the shifted area - if (!inStart || !inEnd) { - continue; - } - - //only shift if the region outside the shifted rows is not merged too - if (!merged.containsRow(startRow - 1) && - !merged.containsRow(endRow + 1)) { - merged.setFirstRow(merged.getFirstRow() + n); - merged.setLastRow(merged.getLastRow() + n); - //have to remove/add it back - shiftedRegions.add(merged); - removeMergedRegion(i); - i = i - 1; // we have to back up now since we removed one - } - } - - //read so it doesn't get shifted again - for (CellRangeAddress region : shiftedRegions) { - this.addMergedRegion(region); - } + RowShifter rowShifter = new HSSFRowShifter(this); + rowShifter.shiftMergedRegions(startRow, endRow, n); } /** @@ -1607,6 +1570,8 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { // Nothing to do return; } + + RowShifter rowShifter = new HSSFRowShifter(this); // Shift comments if (moveComments) { @@ -1614,7 +1579,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet { } // Shift Merged Regions - shiftMerged(startRow, endRow, n); + rowShifter.shiftMergedRegions(startRow, endRow, n); // Shift Row Breaks _sheet.getPageSettings().shiftRowBreaks(startRow, endRow, n); diff --git a/src/java/org/apache/poi/hssf/usermodel/helpers/HSSFRowShifter.java b/src/java/org/apache/poi/hssf/usermodel/helpers/HSSFRowShifter.java new file mode 100644 index 000000000..88a6f9b73 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/helpers/HSSFRowShifter.java @@ -0,0 +1,68 @@ +/* ==================================================================== + 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.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.helpers.RowShifter; +import org.apache.poi.util.Internal; +import org.apache.poi.util.NotImplemented; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +/** + * Helper for shifting rows up or down + * + * When possible, code should be implemented in the RowShifter abstract class to avoid duplication with {@link org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter} + */ +public final class HSSFRowShifter extends RowShifter { + private static final POILogger logger = POILogFactory.getLogger(HSSFRowShifter.class); + + public HSSFRowShifter(HSSFSheet sh) { + super(sh); + } + + @NotImplemented + public void updateNamedRanges(FormulaShifter shifter) { + throw new NotImplementedException("HSSFRowShifter.updateNamedRanges"); + } + + @NotImplemented + public void updateFormulas(FormulaShifter shifter) { + throw new NotImplementedException("updateFormulas"); + } + + @Internal + @NotImplemented + public void updateRowFormulas(Row row, FormulaShifter shifter) { + throw new NotImplementedException("updateRowFormulas"); + } + + @NotImplemented + public void updateConditionalFormatting(FormulaShifter shifter) { + throw new NotImplementedException("updateConditionalFormatting"); + } + + @NotImplemented + public void updateHyperlinks(FormulaShifter shifter) { + throw new NotImplementedException("updateHyperlinks"); + } + +} diff --git a/src/java/org/apache/poi/ss/usermodel/helpers/RowShifter.java b/src/java/org/apache/poi/ss/usermodel/helpers/RowShifter.java new file mode 100644 index 000000000..a16808000 --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/helpers/RowShifter.java @@ -0,0 +1,121 @@ +/* ==================================================================== + 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.helpers; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.poi.ss.formula.FormulaShifter; +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.Internal; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +/** + * Helper for shifting rows up or down + * + * This abstract class exists to consolidate duplicated code between XSSFRowShifter and HSSFRowShifter (currently methods sprinkled throughout HSSFSheet) + */ +public abstract class RowShifter { + private static final POILogger logger = POILogFactory.getLogger(RowShifter.class); + protected final Sheet sheet; + + public RowShifter(Sheet sh) { + sheet = sh; + } + + /** + * Shifts, grows, or shrinks the merged regions due to a row shift + * + * @param startRow the row to start shifting + * @param endRow the row to end shifting + * @param n the number of rows to shift + * @return an array of affected merged regions + */ + public List shiftMergedRegions(int startRow, int endRow, int n) { + List shiftedRegions = new ArrayList(); + Set removedIndices = new HashSet(); + //move merged regions completely if they fall within the new region boundaries when they are shifted + int size = sheet.getNumMergedRegions(); + for (int i = 0; i < size; i++) { + CellRangeAddress merged = sheet.getMergedRegion(i); + + boolean inStart = (merged.getFirstRow() >= startRow || merged.getLastRow() >= startRow); + boolean inEnd = (merged.getFirstRow() <= endRow || merged.getLastRow() <= endRow); + + //don't check if it's not within the shifted area + if (!inStart || !inEnd) { + continue; + } + + //only shift if the region outside the shifted rows is not merged too + if (!merged.containsRow(startRow - 1) && !merged.containsRow(endRow + 1)) { + merged.setFirstRow(merged.getFirstRow() + n); + merged.setLastRow(merged.getLastRow() + n); + //have to remove/add it back + shiftedRegions.add(merged); + removedIndices.add(i); + } + } + + if(!removedIndices.isEmpty()) { + sheet.removeMergedRegions(removedIndices); + } + + //read so it doesn't get shifted again + for (CellRangeAddress region : shiftedRegions) { + sheet.addMergedRegion(region); + } + return shiftedRegions; + } + + /** + * Updated named ranges + */ + public abstract void updateNamedRanges(FormulaShifter shifter); + + /** + * Update formulas. + */ + public abstract void updateFormulas(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 abstract void updateRowFormulas(Row row, FormulaShifter shifter); + + public abstract void updateConditionalFormatting(FormulaShifter shifter); + + /** + * Shift the Hyperlink anchors (not the hyperlink text, even if the hyperlink + * is of type LINK_DOCUMENT and refers to a cell that was shifted). Hyperlinks + * do not track the content they point to. + * + * @param shifter + */ + public abstract void updateHyperlinks(FormulaShifter shifter); + +} diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index f8be5b0ef..8df7b42a6 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -3042,7 +3042,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { rowShifter.updateNamedRanges(shifter); rowShifter.updateFormulas(shifter); - rowShifter.shiftMerged(startRow, endRow, n); + rowShifter.shiftMergedRegions(startRow, endRow, n); rowShifter.updateConditionalFormatting(shifter); rowShifter.updateHyperlinks(shifter); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFRowShifter.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFRowShifter.java index 923af2a63..6f6d06d3c 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFRowShifter.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFRowShifter.java @@ -31,8 +31,12 @@ import org.apache.poi.ss.formula.ptg.AreaErrPtg; import org.apache.poi.ss.formula.ptg.AreaPtg; import org.apache.poi.ss.formula.ptg.Ptg; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Hyperlink; +import org.apache.poi.ss.usermodel.Name; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.helpers.RowShifter; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.util.Internal; import org.apache.poi.util.POILogFactory; @@ -40,7 +44,6 @@ import org.apache.poi.util.POILogger; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook; import org.apache.poi.xssf.usermodel.XSSFHyperlink; -import org.apache.poi.xssf.usermodel.XSSFName; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; @@ -53,87 +56,37 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellFormulaType; /** * Helper for shifting rows up or down + * + * When possible, code should be implemented in the RowShifter abstract class to avoid duplication with {@link org.apache.poi.hssf.usermodel.helpers.HSSFRowShifter} */ -public final class XSSFRowShifter { - private static POILogger logger = POILogFactory.getLogger(XSSFRowShifter.class); - private final XSSFSheet sheet; +public final class XSSFRowShifter extends RowShifter { + private static final POILogger logger = POILogFactory.getLogger(XSSFRowShifter.class); public XSSFRowShifter(XSSFSheet sh) { - sheet = sh; + super(sh); } - + /** * Shift merged regions - * + * * @param startRow the row to start shifting * @param endRow the row to end shifting * @param n the number of rows to shift - * @return an array of affected cell regions - * - * This should be kept in sync with {@link org.apache.poi.hssf.usermodel.HSSFSheet#shiftMerged(int, int, int)} + * @return an array of merged cell regions + * @deprecated POI 3.15 beta 2. Use {@link #shiftMergedRegions(int, int, int)} instead. */ public List shiftMerged(int startRow, int endRow, int n) { - List shiftedRegions = new ArrayList(); - Set removedIndices = new HashSet(); - //move merged regions completely if they fall within the new region boundaries when they are shifted - int size = sheet.getNumMergedRegions(); - for (int i = 0; i < size; i++) { - CellRangeAddress merged = sheet.getMergedRegion(i); - - boolean inStart = (merged.getFirstRow() >= startRow || merged.getLastRow() >= startRow); - boolean inEnd = (merged.getFirstRow() <= endRow || merged.getLastRow() <= endRow); - - //don't check if it's not within the shifted area - if (!inStart || !inEnd) { - continue; - } - - //only shift if the region outside the shifted rows is not merged too - if (!merged.containsRow(startRow - 1) && !merged.containsRow(endRow + 1)) { - merged.setFirstRow(merged.getFirstRow() + n); - merged.setLastRow(merged.getLastRow() + n); - //have to remove/add it back - shiftedRegions.add(merged); - removedIndices.add(i); - } - } - - if(!removedIndices.isEmpty()) { - sheet.removeMergedRegions(removedIndices); - } - - //read so it doesn't get shifted again - for (CellRangeAddress region : shiftedRegions) { - sheet.addMergedRegion(region); - } - return shiftedRegions; - } - - /** - * Check if the row and column are in the specified cell range - * - * @param cr the cell range to check in - * @param rowIx the row to check - * @param colIx the column to check - * @return true if the range contains the cell [rowIx,colIx] - */ - private static boolean containsCell(CellRangeAddress cr, int rowIx, int colIx) { - if (cr.getFirstRow() <= rowIx && cr.getLastRow() >= rowIx - && cr.getFirstColumn() <= colIx && cr.getLastColumn() >= colIx) { - return true; - } - return false; + return shiftMergedRegions(startRow, endRow, n); } /** * Updated named ranges */ - @SuppressWarnings("resource") public void updateNamedRanges(FormulaShifter shifter) { - XSSFWorkbook wb = sheet.getWorkbook(); - XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb); + Workbook wb = sheet.getWorkbook(); + XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create((XSSFWorkbook) wb); for (int i = 0; i < wb.getNumberOfNames(); i++) { - XSSFName name = wb.getNameAt(i); + Name name = wb.getNameAt(i); String formula = name.getRefersToFormula(); int sheetIndex = name.getSheetIndex(); final int rowIndex = -1; //don't care, named ranges are not allowed to include structured references @@ -149,13 +102,12 @@ public final class XSSFRowShifter { /** * Update formulas. */ - @SuppressWarnings("resource") public void updateFormulas(FormulaShifter shifter) { //update formulas on the parent sheet updateSheetFormulas(sheet, shifter); //update formulas on other sheets - XSSFWorkbook wb = sheet.getWorkbook(); + Workbook wb = sheet.getWorkbook(); for (Sheet sh : wb) { if (sheet == sh) continue; updateSheetFormulas(sh, shifter); @@ -176,7 +128,8 @@ public final class XSSFRowShifter { * @param shifter the formula shifting policy */ @Internal - public void updateRowFormulas(XSSFRow row, FormulaShifter shifter) { + public void updateRowFormulas(Row row, FormulaShifter shifter) { + XSSFSheet sheet = (XSSFSheet) row.getSheet(); for (Cell c : row) { XSSFCell cell = (XSSFCell) c; @@ -190,7 +143,7 @@ public final class XSSFRowShifter { f.setStringValue(shiftedFormula); if(f.getT() == STCellFormulaType.SHARED){ int si = (int)f.getSi(); - CTCellFormula sf = row.getSheet().getSharedFormula(si); + CTCellFormula sf = sheet.getSharedFormula(si); sf.setStringValue(shiftedFormula); } } @@ -216,13 +169,12 @@ public final class XSSFRowShifter { * @return the shifted formula if the formula was changed, * null if the formula wasn't modified */ - @SuppressWarnings("resource") - private static String shiftFormula(XSSFRow row, String formula, FormulaShifter shifter) { - XSSFSheet sheet = row.getSheet(); - XSSFWorkbook wb = sheet.getWorkbook(); + private static String shiftFormula(Row row, String formula, FormulaShifter shifter) { + Sheet sheet = row.getSheet(); + Workbook wb = sheet.getWorkbook(); int sheetIndex = wb.getSheetIndex(sheet); final int rowIndex = row.getRowNum(); - XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb); + XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create((XSSFWorkbook) wb); try { Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex, rowIndex); @@ -238,14 +190,14 @@ public final class XSSFRowShifter { } } - @SuppressWarnings("resource") public void updateConditionalFormatting(FormulaShifter shifter) { - XSSFWorkbook wb = sheet.getWorkbook(); + XSSFSheet xsheet = (XSSFSheet) sheet; + XSSFWorkbook wb = xsheet.getWorkbook(); int sheetIndex = wb.getSheetIndex(sheet); final int rowIndex = -1; //don't care, structured references not allowed in conditional formatting XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb); - CTWorksheet ctWorksheet = sheet.getCTWorksheet(); + CTWorksheet ctWorksheet = xsheet.getCTWorksheet(); CTConditionalFormatting[] conditionalFormattingArray = ctWorksheet.getConditionalFormattingArray(); // iterate backwards due to possible calls to ctWorksheet.removeConditionalFormatting(j) for (int j = conditionalFormattingArray.length - 1; j >= 0; j--) { @@ -307,17 +259,18 @@ public final class XSSFRowShifter { */ public void updateHyperlinks(FormulaShifter shifter) { int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet); - List hyperlinkList = sheet.getHyperlinkList(); + List hyperlinkList = sheet.getHyperlinkList(); - for (XSSFHyperlink hyperlink : hyperlinkList) { - String cellRef = hyperlink.getCellRef(); + for (Hyperlink hyperlink : hyperlinkList) { + XSSFHyperlink xhyperlink = (XSSFHyperlink) hyperlink; + String cellRef = xhyperlink.getCellRef(); CellRangeAddress cra = CellRangeAddress.valueOf(cellRef); CellRangeAddress shiftedRange = shiftRange(shifter, cra, sheetIndex); if (shiftedRange != null && shiftedRange != cra) { // shiftedRange should not be null. If shiftedRange is null, that means // that a hyperlink wasn't deleted at the beginning of shiftRows when // identifying rows that should be removed because they will be overwritten - hyperlink.setCellReference(shiftedRange.formatAsString()); + xhyperlink.setCellReference(shiftedRange.formatAsString()); } } }