bug 61474, github #81: add ColumnShifter interface; deduplicate some code in RowShifter, CFRecordsAggregate

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1814256 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Javen O'Neal 2017-11-04 07:17:44 +00:00
parent b671199b40
commit 4b92e7c67a
9 changed files with 410 additions and 66 deletions

View File

@ -29,9 +29,8 @@ import org.apache.poi.hssf.record.CFRuleBase;
import org.apache.poi.hssf.record.CFRuleRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.ss.formula.FormulaShifter;
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.helpers.BaseRowColShifter;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@ -223,7 +222,7 @@ public final class CFRecordsAggregate extends RecordAggregate {
boolean changed = false;
List<CellRangeAddress> temp = new ArrayList<>();
for (CellRangeAddress craOld : cellRanges) {
CellRangeAddress craNew = shiftRange(shifter, craOld, currentExternSheetIx);
CellRangeAddress craNew = BaseRowColShifter.shiftRange(shifter, craOld, currentExternSheetIx);
if (craNew == null) {
changed = true;
continue;
@ -264,23 +263,4 @@ public final class CFRecordsAggregate extends RecordAggregate {
}
return true;
}
private static CellRangeAddress shiftRange(FormulaShifter shifter, CellRangeAddress cra, int currentExternSheetIx) {
// FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here
AreaPtg aptg = new AreaPtg(cra.getFirstRow(), cra.getLastRow(), cra.getFirstColumn(), cra.getLastColumn(), false, false, false, false);
Ptg[] ptgs = { aptg, };
if (!shifter.adjustFormula(ptgs, currentExternSheetIx)) {
return cra;
}
Ptg ptg0 = ptgs[0];
if (ptg0 instanceof AreaPtg) {
AreaPtg bptg = (AreaPtg) ptg0;
return new CellRangeAddress(bptg.getFirstRow(), bptg.getLastRow(), bptg.getFirstColumn(), bptg.getLastColumn());
}
if (ptg0 instanceof AreaErrPtg) {
return null;
}
throw new IllegalStateException("Unexpected shifted ptg class (" + ptg0.getClass().getName() + ")");
}
}

View File

@ -0,0 +1,66 @@
/* ====================================================================
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.helpers.ColumnShifter;
import org.apache.poi.util.Beta;
import org.apache.poi.util.NotImplemented;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
/**
* Helper for shifting columns up or down
*/
// non-Javadoc: When possible, code should be implemented in the ColumnShifter abstract class to avoid duplication with
// {@link org.apache.poi.xssf.usermodel.helpers.XSSFColumnShifter}
@Beta
public final class HSSFColumnShifter extends ColumnShifter {
private static final POILogger logger = POILogFactory.getLogger(HSSFColumnShifter.class);
public HSSFColumnShifter(HSSFSheet sh) {
super(sh);
}
@Override
@NotImplemented
public void updateNamedRanges(FormulaShifter formulaShifter) {
throw new NotImplementedException("HSSFColumnShifter.updateNamedRanges");
}
@Override
@NotImplemented
public void updateFormulas(FormulaShifter formulaShifter) {
throw new NotImplementedException("updateFormulas");
}
@Override
@NotImplemented
public void updateConditionalFormatting(FormulaShifter formulaShifter) {
throw new NotImplementedException("updateConditionalFormatting");
}
@Override
@NotImplemented
public void updateHyperlinks(FormulaShifter formulaShifter) {
throw new NotImplementedException("updateHyperlinks");
}
}

View File

@ -29,9 +29,9 @@ 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}
*/
// non-Javadoc: 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);

View File

@ -0,0 +1,54 @@
/* ====================================================================
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 org.apache.poi.ss.formula.FormulaShifter;
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.util.CellRangeAddress;
import org.apache.poi.util.Internal;
/**
* Class for code common to {@link RowShifter} and {@link ColumnShifter}
* Helper for shifting rows up or down and columns left and right
*/
@Internal
public abstract class BaseRowColShifter {
public static CellRangeAddress shiftRange(FormulaShifter formulaShifter, CellRangeAddress cra, int currentExternSheetIx) {
// FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here
AreaPtg aptg = new AreaPtg(cra.getFirstRow(), cra.getLastRow(), cra.getFirstColumn(), cra.getLastColumn(), false, false, false, false);
Ptg[] ptgs = { aptg, };
if (!formulaShifter.adjustFormula(ptgs, currentExternSheetIx)) {
return cra;
}
Ptg ptg0 = ptgs[0];
if (ptg0 instanceof AreaPtg) {
AreaPtg bptg = (AreaPtg) ptg0;
return new CellRangeAddress(bptg.getFirstRow(), bptg.getLastRow(), bptg.getFirstColumn(), bptg.getLastColumn());
}
if (ptg0 instanceof AreaErrPtg) {
return null;
}
throw new IllegalStateException("Unexpected shifted ptg class (" + ptg0.getClass().getName() + ")");
}
}

View File

@ -0,0 +1,136 @@
/* ====================================================================
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.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.Beta;
/**
* Helper for shifting columns up or down
*/
// non-Javadoc: This abstract class exists to consolidate duplicated code between XSSFColumnShifter and HSSFColumnShifter
// (currently methods sprinkled throughout HSSFSheet)
@Beta
public abstract class ColumnShifter extends BaseRowColShifter {
protected final Sheet sheet;
public ColumnShifter(Sheet sh) {
sheet = sh;
}
/**
* Shifts, grows, or shrinks the merged regions due to a column shift.
* Merged regions that are completely overlaid by shifting will be deleted.
*
* @param startColumn the column to start shifting
* @param endColumn the column to end shifting
* @param n the number of columns to shift
* @return an array of affected merged regions, doesn't contain deleted ones
*/
public List<CellRangeAddress> shiftMergedRegions(int startColumn, int endColumn, int n) {
List<CellRangeAddress> shiftedRegions = new ArrayList<>();
Set<Integer> 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);
// remove merged region that are replaced by the shifting,
// i.e. where the area includes something in the overwritten area
if(removalNeeded(merged, startColumn, endColumn, n)) {
removedIndices.add(i);
continue;
}
boolean inStart = (merged.getFirstColumn() >= startColumn || merged.getLastColumn() >= startColumn);
boolean inEnd = (merged.getFirstColumn() <= endColumn || merged.getLastColumn() <= endColumn);
//don't check if it's not within the shifted area
if (!inStart || !inEnd) {
continue;
}
//only shift if the region outside the shifted columns is not merged too
if (!merged.containsColumn(startColumn - 1) && !merged.containsColumn(endColumn + 1)) {
merged.setFirstColumn(merged.getFirstColumn() + n);
merged.setLastColumn(merged.getLastColumn() + 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;
}
private boolean removalNeeded(CellRangeAddress merged, int startColumn, int endColumn, int n) {
final int movedColumns = endColumn - startColumn + 1;
// build a range of the columns that are overwritten, i.e. the target-area, but without
// columns that are moved along
final CellRangeAddress overwrite;
if(n > 0) {
// area is moved down => overwritten area is [endColumn + n - movedColumns, endColumn + n]
overwrite = new CellRangeAddress(Math.max(endColumn + 1, endColumn + n - movedColumns), endColumn + n, 0, 0);
} else {
// area is moved up => overwritten area is [startColumn + n, startColumn + n + movedColumns]
overwrite = new CellRangeAddress(startColumn + n, Math.min(startColumn - 1, startColumn + n + movedColumns), 0, 0);
}
// if the merged-region and the overwritten area intersect, we need to remove it
return merged.intersects(overwrite);
}
/**
* Updated named ranges
*/
public abstract void updateNamedRanges(FormulaShifter formulaShifter);
/**
* Update formulas.
*/
public abstract void updateFormulas(FormulaShifter formulaShifter);
public abstract void updateConditionalFormatting(FormulaShifter formulaShifter);
/**
* 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 formulaShifter the formula shifting policy
*/
public abstract void updateHyperlinks(FormulaShifter formulaShifter);
}

View File

@ -30,10 +30,12 @@ import org.apache.poi.util.Internal;
/**
* 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 {
// non-Javadoc: This abstract class exists to consolidate duplicated code between
// {@link org.apache.poi.hssf.usermodel.helpers.HSSFRowShifter} and
// {@link org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter}
// (currently methods sprinkled throughout HSSFSheet)
public abstract class RowShifter extends BaseRowColShifter {
protected final Sheet sheet;
public RowShifter(Sheet sh) {

View File

@ -0,0 +1,83 @@
/* ====================================================================
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.helpers;
import org.apache.poi.ss.formula.*;
import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.helpers.ColumnShifter;
import org.apache.poi.util.Beta;
import org.apache.poi.util.NotImplemented;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.xssf.usermodel.*;
/**
* Helper for shifting columns up or down
*/
// non-Javadoc: When possible, code should be implemented in the ColumnShifter abstract class to avoid duplication with
// {@link org.apache.poi.hssf.usermodel.helpers.HSSFColumnShifter}
@Beta
public final class XSSFColumnShifter extends ColumnShifter {
private static final POILogger logger = POILogFactory.getLogger(XSSFColumnShifter.class);
public XSSFColumnShifter(XSSFSheet sh) {
super(sh);
}
/**
* Updated named ranges
*/
@NotImplemented
@Override
public void updateNamedRanges(FormulaShifter formulaShifter) {
throw new NotImplementedException("updateNamedRanges");
}
/**
* Update formulas.
*/
@NotImplemented
@Override
public void updateFormulas(FormulaShifter formulaShifter) {
throw new NotImplementedException("updateFormulas");
}
private void updateSheetFormulas(Sheet sh, FormulaShifter formulaShifter) {
throw new NotImplementedException("updateSheetFormulas");
}
@NotImplemented
@Override
public void updateConditionalFormatting(FormulaShifter formulaShifter) {
throw new NotImplementedException("updateConditionalformatting");
}
/**
* 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 formulaShifter
*/
@Override
public void updateHyperlinks(FormulaShifter formulaShifter) {
XSSFRowColShifter.updateHyperlinks(sheet, formulaShifter);
}
}

View File

@ -0,0 +1,59 @@
/* ====================================================================
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.helpers;
import org.apache.poi.ss.formula.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.helpers.BaseRowColShifter;
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;
import org.apache.poi.xssf.usermodel.*;
import java.util.List;
/**
* Class for code common to {@link XSSFRowShifter} and {@link XSSFColumnShifter}
*/
@Internal
/*private*/ final class XSSFRowColShifter extends BaseRowColShifter {
private static final POILogger logger = POILogFactory.getLogger(XSSFRowColShifter.class);
private XSSFRowColShifter() { /*no instances for static classes*/}
/*package*/ static void updateHyperlinks(Sheet sheet, FormulaShifter formulaShifter) {
int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet);
List<? extends Hyperlink> hyperlinkList = sheet.getHyperlinkList();
for (Hyperlink hyperlink : hyperlinkList) {
XSSFHyperlink xhyperlink = (XSSFHyperlink) hyperlink;
String cellRef = xhyperlink.getCellRef();
CellRangeAddress cra = CellRangeAddress.valueOf(cellRef);
CellRangeAddress shiftedRange = shiftRange(formulaShifter, 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
xhyperlink.setCellReference(shiftedRange.formatAsString());
}
}
}
}

View File

@ -25,11 +25,8 @@ import org.apache.poi.ss.formula.FormulaParser;
import org.apache.poi.ss.formula.FormulaRenderer;
import org.apache.poi.ss.formula.FormulaShifter;
import org.apache.poi.ss.formula.FormulaType;
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;
@ -41,7 +38,6 @@ import org.apache.poi.util.POILogFactory;
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.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
@ -54,9 +50,9 @@ 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}
*/
// non-Javadoc: 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 extends RowShifter {
private static final POILogger logger = POILogFactory.getLogger(XSSFRowShifter.class);
@ -253,40 +249,8 @@ public final class XSSFRowShifter extends RowShifter {
*/
@Override
public void updateHyperlinks(FormulaShifter formulaShifter) {
int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet);
List<? extends Hyperlink> hyperlinkList = sheet.getHyperlinkList();
for (Hyperlink hyperlink : hyperlinkList) {
XSSFHyperlink xhyperlink = (XSSFHyperlink) hyperlink;
String cellRef = xhyperlink.getCellRef();
CellRangeAddress cra = CellRangeAddress.valueOf(cellRef);
CellRangeAddress shiftedRange = shiftRange(formulaShifter, 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
xhyperlink.setCellReference(shiftedRange.formatAsString());
}
}
XSSFRowColShifter.updateHyperlinks(sheet, formulaShifter);
}
private static CellRangeAddress shiftRange(FormulaShifter formulaShifter, CellRangeAddress cra, int currentExternSheetIx) {
// FormulaShifter works well in terms of Ptgs - so convert CellRangeAddress to AreaPtg (and back) here
AreaPtg aptg = new AreaPtg(cra.getFirstRow(), cra.getLastRow(), cra.getFirstColumn(), cra.getLastColumn(), false, false, false, false);
Ptg[] ptgs = { aptg, };
if (!formulaShifter.adjustFormula(ptgs, currentExternSheetIx)) {
return cra;
}
Ptg ptg0 = ptgs[0];
if (ptg0 instanceof AreaPtg) {
AreaPtg bptg = (AreaPtg) ptg0;
return new CellRangeAddress(bptg.getFirstRow(), bptg.getLastRow(), bptg.getFirstColumn(), bptg.getLastColumn());
}
if (ptg0 instanceof AreaErrPtg) {
return null;
}
throw new IllegalStateException("Unexpected shifted ptg class (" + ptg0.getClass().getName() + ")");
}
}