diff --git a/src/documentation/content/xdocs/index.xml b/src/documentation/content/xdocs/index.xml
index 93bde6afb..24e9efe07 100644
--- a/src/documentation/content/xdocs/index.xml
+++ b/src/documentation/content/xdocs/index.xml
@@ -38,7 +38,7 @@
Development for this is in a svn branch, but we are please to
announce our first preview release containing this support.
Users interested in the OOXML support should download the
- POI 3.5 beta 4
+ POI 3.5 beta 5
the source and binaries from their
local mirror.
People interested should also follow the
diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java
index b2623ee6f..5feaa7689 100644
--- a/src/java/org/apache/poi/hssf/model/Workbook.java
+++ b/src/java/org/apache/poi/hssf/model/Workbook.java
@@ -79,6 +79,8 @@ import org.apache.poi.hssf.record.WindowProtectRecord;
import org.apache.poi.hssf.record.WriteAccessRecord;
import org.apache.poi.hssf.record.WriteProtectRecord;
import org.apache.poi.hssf.record.formula.NameXPtg;
+import org.apache.poi.hssf.record.formula.FormulaShifter;
+import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
import org.apache.poi.util.POILogFactory;
@@ -2310,4 +2312,18 @@ public final class Workbook implements Model {
}
}
+
+ /**
+ * Updates named ranges due to moving of cells
+ */
+ public void updateNamesAfterCellShift(FormulaShifter shifter) {
+ for (int i = 0 ; i < getNumNames() ; ++i){
+ NameRecord nr = getNameRecord(i);
+ Ptg[] ptgs = nr.getNameDefinition();
+ if (shifter.adjustFormula(ptgs, nr.getExternSheetNumber())) {
+ nr.setNameDefinition(ptgs);
+ }
+ }
+ }
+
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
index 96a21810b..cda40b35d 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
@@ -1285,7 +1285,7 @@ public final class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet {
short otherExtSheetIx = _book.checkExternSheet(i);
otherSheet.updateFormulasAfterCellShift(shifter, otherExtSheetIx);
}
- // TODO - adjust formulas in named ranges
+ _workbook.getWorkbook().updateNamesAfterCellShift(shifter);
}
protected void insertChartRecords(List records) {
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 bdc917d83..ac82bd24a 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
@@ -37,6 +37,7 @@ import org.apache.poi.ss.formula.FormulaRenderer;
import org.apache.poi.xssf.model.CommentsTable;
import org.apache.poi.xssf.model.CalculationChain;
import org.apache.poi.xssf.usermodel.helpers.ColumnHelper;
+import org.apache.poi.xssf.usermodel.helpers.XSSFRowShifter;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLException;
import org.apache.poi.util.POILogger;
@@ -74,7 +75,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
protected CTSheet sheet;
protected CTWorksheet worksheet;
- private TreeMap rows;
+ private TreeMap rows;
private List hyperlinks;
private ColumnHelper columnHelper;
private CommentsTable sheetComments;
@@ -151,7 +152,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
}
private void initRows(CTWorksheet worksheet) {
- rows = new TreeMap();
+ rows = new TreeMap();
sharedFormulas = new HashMap();
for (CTRow row : worksheet.getSheetData().getRowArray()) {
XSSFRow r = new XSSFRow(row, this);
@@ -831,7 +832,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
* @return XSSFRow
representing the rownumber or null
if its not defined on the sheet
*/
public XSSFRow getRow(int rownum) {
- return (XSSFRow)rows.get(rownum);
+ return rows.get(rownum);
}
/**
@@ -1012,8 +1013,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
private short getMaxOutlineLevelRows(){
short outlineLevel=0;
- for(Row r : rows.values()){
- XSSFRow xrow=(XSSFRow)r;
+ for(XSSFRow xrow : rows.values()){
outlineLevel=xrow.getCTRow().getOutlineLevel()>outlineLevel? xrow.getCTRow().getOutlineLevel(): outlineLevel;
}
return outlineLevel;
@@ -1224,7 +1224,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
* Call getRowNum() on each row if you care which one it is.
*/
public Iterator rowIterator() {
- return rows.values().iterator();
+ return (Iterator)(Iterator extends Row>)rows.values().iterator();
}
/**
@@ -1466,18 +1466,16 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
for (Iterator it = rowIterator() ; it.hasNext() ; ) {
XSSFRow row = (XSSFRow)it.next();
int rownum = row.getRowNum();
+ if(rownum < startRow) continue;
if (!copyRowHeight) {
row.setHeight((short)-1);
}
- if (resetOriginalRowHeight && getDefaultRowHeight() >= 0) {
- row.setHeight(getDefaultRowHeight());
- }
- if (removeRow(startRow, endRow, n, row.getRowNum())) {
+ if (removeRow(startRow, endRow, n, rownum)) {
it.remove();
}
- else if (row.getRowNum() >= startRow && row.getRowNum() <= endRow) {
+ else if (rownum >= startRow && rownum <= endRow) {
row.shift(n);
}
@@ -1493,26 +1491,21 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
}
}
}
- //rebuild the rows map
+ XSSFRowShifter rowShifter = new XSSFRowShifter(this);
+
int sheetIndex = getWorkbook().getSheetIndex(this);
FormulaShifter shifter = FormulaShifter.createForRowShift(sheetIndex, startRow, endRow, n);
- TreeMap map = new TreeMap();
- for(Row r : this) {
- XSSFRow row = (XSSFRow)r;
- row.updateFormulasAfterCellShift(shifter);
+
+ rowShifter.updateNamedRanges(shifter);
+ rowShifter.updateFormulas(shifter);
+ rowShifter.shiftMerged(startRow, endRow, n);
+
+ //rebuild the rows map
+ TreeMap map = new TreeMap();
+ for(XSSFRow r : rows.values()) {
map.put(r.getRowNum(), r);
}
rows = map;
-
- //update formulas on other sheets
- for(XSSFSheet sheet : getWorkbook()) {
- if (sheet == this) continue;
- for(Row r : sheet) {
- XSSFRow row = (XSSFRow)r;
- row.updateFormulasAfterCellShift(shifter);
- }
- }
-
}
/**
@@ -1783,10 +1776,9 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
CTSheetData sheetData = worksheet.getSheetData();
ArrayList rArray = new ArrayList(rows.size());
- for(Row row : rows.values()){
- XSSFRow r = (XSSFRow)row;
- r.onDocumentWrite();
- rArray.add(r.getCTRow());
+ for(XSSFRow row : rows.values()){
+ row.onDocumentWrite();
+ rArray.add(row.getCTRow());
}
sheetData.setRowArray(rArray.toArray(new CTRow[rArray.size()]));
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
new file mode 100755
index 000000000..95ef50777
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFRowShifter.java
@@ -0,0 +1,189 @@
+/* ====================================================================
+ 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.xssf.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.formula.FormulaParser;
+import org.apache.poi.ss.formula.FormulaType;
+import org.apache.poi.ss.formula.FormulaRenderer;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.hssf.record.formula.FormulaShifter;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Yegor Kozlov
+ */
+public class XSSFRowShifter {
+ private final XSSFSheet sheet;
+
+ public XSSFRowShifter(XSSFSheet sh) {
+ sheet = 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
+ */
+ public List 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 < sheet.getNumMergedRegions(); 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 (!containsCell(merged, startRow - 1, 0) && !containsCell(merged, endRow + 1, 0)) {
+ merged.setFirstRow(merged.getFirstRow() + n);
+ merged.setLastRow(merged.getLastRow() + n);
+ //have to remove/add it back
+ shiftedRegions.add(merged);
+ sheet.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) {
+ 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;
+ }
+
+ /**
+ * Updated named ranges
+ */
+ public void updateNamedRanges(FormulaShifter shifter) {
+ XSSFWorkbook wb = sheet.getWorkbook();
+ int sheetIndex = wb.getSheetIndex(sheet);
+ XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
+ for (int i = 0; i < wb.getNumberOfNames(); i++) {
+ XSSFName name = wb.getNameAt(i);
+ String formula = name.getRefersToFormula();
+
+ Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.NAMEDRANGE, sheetIndex);
+ if (shifter.adjustFormula(ptgs, sheetIndex)) {
+ String shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);
+ name.setRefersToFormula(shiftedFmla);
+ }
+
+ }
+ }
+
+ /**
+ * Update formulas.
+ */
+ public void updateFormulas(FormulaShifter shifter) {
+ //update formulas on the parent sheet
+ updateSheetFormulas(sheet, shifter);
+
+ //update formulas on other sheets
+ XSSFWorkbook wb = sheet.getWorkbook();
+ for (XSSFSheet sh : wb) {
+ if (sheet == sh) continue;
+ updateSheetFormulas(sh, shifter);
+ }
+ }
+
+ private void updateSheetFormulas(XSSFSheet sh, FormulaShifter shifter) {
+ for (Row r : sh) {
+ XSSFRow row = (XSSFRow) r;
+ updateRowFormulas(row, shifter);
+ }
+ }
+
+ private void updateRowFormulas(XSSFRow row, FormulaShifter shifter) {
+ for (Cell c : row) {
+ XSSFCell cell = (XSSFCell) c;
+
+ CTCell ctCell = cell.getCTCell();
+ if (ctCell.isSetF()) {
+ CTCellFormula f = ctCell.getF();
+ String formula = f.getStringValue();
+ if (formula.length() > 0) {
+ String shiftedFormula = shiftFormula(row, formula, shifter);
+ if (shiftedFormula != null) {
+ f.setStringValue(shiftedFormula);
+ }
+ }
+
+ if (f.isSetRef()) { //Range of cells which the formula applies to.
+ String ref = f.getRef();
+ String shiftedRef = shiftFormula(row, ref, shifter);
+ if (shiftedRef != null) f.setRef(shiftedRef);
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Shift a formula using the supplied FormulaShifter
+ *
+ * @param row the row of the cell this formula belongs to. Used to get a reference to the parent workbook.
+ * @param formula the formula to shift
+ * @param shifter the FormulaShifter object that operates on the parsed formula tokens
+ * @return the shifted formula if the formula was changed,
+ * null
if the formula wasn't modified
+ */
+ private static String shiftFormula(XSSFRow row, String formula, FormulaShifter shifter) {
+ XSSFSheet sheet = row.getSheet();
+ XSSFWorkbook wb = sheet.getWorkbook();
+ int sheetIndex = wb.getSheetIndex(sheet);
+ XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);
+ Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex);
+ String shiftedFmla = null;
+ if (shifter.adjustFormula(ptgs, sheetIndex)) {
+ shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);
+ }
+ return shiftedFmla;
+ }
+
+}
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestSheetShiftRows.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestSheetShiftRows.java
index 212271d0b..fe9b8e425 100755
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestSheetShiftRows.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestSheetShiftRows.java
@@ -38,8 +38,8 @@ public class TestSheetShiftRows extends BaseTestSheetShiftRows {
baseTestShiftRow();
}
- public void testShiftRow0() {
- baseTestShiftRow0();
+ public void testShiftNames() {
+ baseTestShiftWithNames();
}
//TODO support shifting of page breaks
@@ -55,4 +55,8 @@ public class TestSheetShiftRows extends BaseTestSheetShiftRows {
public void testShiftWithFormulas() {
baseTestShiftWithFormulas("ForShifting.xlsx");
}
+
+ public void testShiftWithMergedRegions() {
+ baseTestShiftWithMergedRegions();
+ }
}
diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java
index 84e0aebdd..bcb6b34d0 100755
--- a/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java
+++ b/src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java
@@ -43,8 +43,8 @@ public final class TestSheetShiftRows extends BaseTestSheetShiftRows {
baseTestShiftRow();
}
- public void testShiftRow0() {
- baseTestShiftRow0();
+ public void testShiftNames() {
+ baseTestShiftWithNames();
}
public void testShiftRowBreaks() {
@@ -59,4 +59,7 @@ public final class TestSheetShiftRows extends BaseTestSheetShiftRows {
baseTestShiftWithFormulas("ForShifting.xls");
}
+ public void testShiftWithMergedRegions() {
+ baseTestShiftWithMergedRegions();
+ }
}
diff --git a/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetShiftRows.java b/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetShiftRows.java
index a9305d8ca..083779c0b 100755
--- a/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetShiftRows.java
+++ b/src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetShiftRows.java
@@ -19,6 +19,7 @@ package org.apache.poi.ss.usermodel;
import junit.framework.TestCase;
import org.apache.poi.ss.ITestDataProvider;
+import org.apache.poi.ss.util.CellRangeAddress;
/**
* Tests row shifting capabilities.
@@ -108,9 +109,10 @@ public abstract class BaseTestSheetShiftRows extends TestCase {
/**
* Tests when shifting the first row.
*/
- public final void baseTestShiftRow0() {
+ public final void baseTestActiveCell() {
Workbook b = getTestDataProvider().createWorkbook();
Sheet s = b.createSheet();
+
s.createRow(0).createCell(0).setCellValue("TEST1");
s.createRow(3).createCell(0).setCellValue("TEST2");
s.shiftRows(0,4,1);
@@ -190,6 +192,45 @@ public abstract class BaseTestSheetShiftRows extends TestCase {
assertEquals(comment4,comment4_shifted);
}
+ public final void baseTestShiftWithNames() {
+ Workbook wb = getTestDataProvider().createWorkbook();
+ Sheet sheet = wb.createSheet();
+ Row row = sheet.createRow(0);
+ row.createCell(0).setCellValue(1.1);
+ row.createCell(1).setCellValue(2.2);
+
+ Name name1 = wb.createName();
+ name1.setNameName("name1");
+ name1.setRefersToFormula("A1+B1");
+
+ Name name2 = wb.createName();
+ name2.setNameName("name2");
+ name2.setRefersToFormula("A1");
+
+ sheet.shiftRows(0, 1, 2);
+ name1 = wb.getNameAt(0);
+ assertEquals("A3+B3", name1.getRefersToFormula());
+
+ name2 = wb.getNameAt(1);
+ assertEquals("A3", name2.getRefersToFormula());
+ }
+
+ public final void baseTestShiftWithMergedRegions() {
+ Workbook wb = getTestDataProvider().createWorkbook();
+ Sheet sheet = wb.createSheet();
+ Row row = sheet.createRow(0);
+ row.createCell(0).setCellValue(1.1);
+ row.createCell(1).setCellValue(2.2);
+ CellRangeAddress region = new CellRangeAddress(0, 0, 0, 2);
+ assertEquals("A1:C1", region.formatAsString());
+
+ sheet.addMergedRegion(region);
+
+ sheet.shiftRows(0, 1, 2);
+ region = sheet.getMergedRegion(0);
+ assertEquals("A3:C3", region.formatAsString());
+ }
+
/**
* See bug #34023
*