]> source.dussan.org Git - poi.git/commitdiff
more improvements in shiftRows: 1. shift named ranges and merged regions, JUnit added...
authorYegor Kozlov <yegor@apache.org>
Wed, 25 Feb 2009 19:12:06 +0000 (19:12 +0000)
committerYegor Kozlov <yegor@apache.org>
Wed, 25 Feb 2009 19:12:06 +0000 (19:12 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@747894 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/index.xml
src/java/org/apache/poi/hssf/model/Workbook.java
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFRowShifter.java [new file with mode: 0755]
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestSheetShiftRows.java
src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java
src/testcases/org/apache/poi/ss/usermodel/BaseTestSheetShiftRows.java

index 93bde6afba51aff21e1e5dcd555d64b2df0557f4..24e9efe079f2e06dde31979b91ba82c91b1ff7ee 100644 (file)
@@ -38,7 +38,7 @@
       <p>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
-       <link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">POI 3.5 beta 4</link>
+       <link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">POI 3.5 beta 5</link>
        the source and binaries from their
        <link href="http://www.apache.org/dyn/closer.cgi/poi/dev/">local mirror</link>.
        People interested should also follow the
index b2623ee6f3c68d5a8f83926e2c937efb3126d8a7..5feaa76893bbd41a549de4db1d20d9c6cf67892d 100644 (file)
@@ -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);
+            }
+        }
+    }
+
 }
index 96a21810b28e4c4037f064e728a5bf564ea1bcfe..cda40b35d408e0098f6e568a6dabd6a05f1daa71 100644 (file)
@@ -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<Record> records) {
index bdc917d8395ea80a4f7adb8e1e1a290f33e279e2..ac82bd24a2b9e37f52f1f7a4d4694fd33dca9b08 100644 (file)
@@ -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<Integer, Row> rows;
+    private TreeMap<Integer, XSSFRow> rows;
     private List<XSSFHyperlink> 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<Integer, Row>();
+        rows = new TreeMap<Integer, XSSFRow>();
         sharedFormulas = new HashMap<Integer, XSSFCell>();
         for (CTRow row : worksheet.getSheetData().getRowArray()) {
             XSSFRow r = new XSSFRow(row, this);
@@ -831,7 +832,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
      * @return <code>XSSFRow</code> representing the rownumber or <code>null</code> 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<Row> rowIterator() {
-        return rows.values().iterator();
+        return (Iterator<Row>)(Iterator<? extends Row>)rows.values().iterator();
     }
 
     /**
@@ -1466,18 +1466,16 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
         for (Iterator<Row> 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<Integer, Row> map = new TreeMap<Integer, Row>();
-        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<Integer, XSSFRow> map = new TreeMap<Integer, XSSFRow>();
+        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<CTRow> rArray = new ArrayList<CTRow>(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 (executable)
index 0000000..95ef507
--- /dev/null
@@ -0,0 +1,189 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.xssf.usermodel.helpers;\r
+\r
+import org.apache.poi.xssf.usermodel.*;\r
+import org.apache.poi.ss.util.CellRangeAddress;\r
+import org.apache.poi.ss.formula.FormulaParser;\r
+import org.apache.poi.ss.formula.FormulaType;\r
+import org.apache.poi.ss.formula.FormulaRenderer;\r
+import org.apache.poi.ss.usermodel.Row;\r
+import org.apache.poi.ss.usermodel.Cell;\r
+import org.apache.poi.hssf.record.formula.FormulaShifter;\r
+import org.apache.poi.hssf.record.formula.Ptg;\r
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell;\r
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula;\r
+\r
+import java.util.List;\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * @author Yegor Kozlov\r
+ */\r
+public class XSSFRowShifter {\r
+    private final XSSFSheet sheet;\r
+\r
+    public XSSFRowShifter(XSSFSheet sh) {\r
+        sheet = sh;\r
+    }\r
+\r
+    /**\r
+     * Shift merged regions\r
+     *\r
+     * @param startRow the row to start shifting\r
+     * @param endRow   the row to end shifting\r
+     * @param n        the number of rows to shift\r
+     * @return an array of affected cell regions\r
+     */\r
+    public List<CellRangeAddress> shiftMerged(int startRow, int endRow, int n) {\r
+        List<CellRangeAddress> shiftedRegions = new ArrayList<CellRangeAddress>();\r
+        //move merged regions completely if they fall within the new region boundaries when they are shifted\r
+        for (int i = 0; i < sheet.getNumMergedRegions(); i++) {\r
+            CellRangeAddress merged = sheet.getMergedRegion(i);\r
+\r
+            boolean inStart = (merged.getFirstRow() >= startRow || merged.getLastRow() >= startRow);\r
+            boolean inEnd = (merged.getFirstRow() <= endRow || merged.getLastRow() <= endRow);\r
+\r
+            //don't check if it's not within the shifted area\r
+            if (!inStart || !inEnd) {\r
+                continue;\r
+            }\r
+\r
+            //only shift if the region outside the shifted rows is not merged too\r
+            if (!containsCell(merged, startRow - 1, 0) && !containsCell(merged, endRow + 1, 0)) {\r
+                merged.setFirstRow(merged.getFirstRow() + n);\r
+                merged.setLastRow(merged.getLastRow() + n);\r
+                //have to remove/add it back\r
+                shiftedRegions.add(merged);\r
+                sheet.removeMergedRegion(i);\r
+                i = i - 1; // we have to back up now since we removed one\r
+            }\r
+        }\r
+\r
+        //read so it doesn't get shifted again\r
+        for (CellRangeAddress region : shiftedRegions) {\r
+            sheet.addMergedRegion(region);\r
+        }\r
+        return shiftedRegions;\r
+    }\r
+\r
+    /**\r
+     * Check if the  row and column are in the specified cell range\r
+     *\r
+     * @param cr    the cell range to check in\r
+     * @param rowIx the row to check\r
+     * @param colIx the column to check\r
+     * @return true if the range contains the cell [rowIx,colIx]\r
+     */\r
+    private static boolean containsCell(CellRangeAddress cr, int rowIx, int colIx) {\r
+        if (cr.getFirstRow() <= rowIx && cr.getLastRow() >= rowIx\r
+                && cr.getFirstColumn() <= colIx && cr.getLastColumn() >= colIx) {\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Updated named ranges\r
+     */\r
+    public void updateNamedRanges(FormulaShifter shifter) {\r
+        XSSFWorkbook wb = sheet.getWorkbook();\r
+        int sheetIndex = wb.getSheetIndex(sheet);\r
+        XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);\r
+        for (int i = 0; i < wb.getNumberOfNames(); i++) {\r
+            XSSFName name = wb.getNameAt(i);\r
+            String formula = name.getRefersToFormula();\r
+\r
+            Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.NAMEDRANGE, sheetIndex);\r
+            if (shifter.adjustFormula(ptgs, sheetIndex)) {\r
+                String shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);\r
+                name.setRefersToFormula(shiftedFmla);\r
+            }\r
+\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Update formulas.\r
+     */\r
+    public void updateFormulas(FormulaShifter shifter) {\r
+        //update formulas on the parent sheet\r
+        updateSheetFormulas(sheet, shifter);\r
+\r
+        //update formulas on other sheets\r
+        XSSFWorkbook wb = sheet.getWorkbook();\r
+        for (XSSFSheet sh : wb) {\r
+            if (sheet == sh) continue;\r
+            updateSheetFormulas(sh, shifter);\r
+        }\r
+    }\r
+\r
+    private void updateSheetFormulas(XSSFSheet sh, FormulaShifter shifter) {\r
+        for (Row r : sh) {\r
+            XSSFRow row = (XSSFRow) r;\r
+            updateRowFormulas(row, shifter);\r
+        }\r
+    }\r
+\r
+    private void updateRowFormulas(XSSFRow row, FormulaShifter shifter) {\r
+        for (Cell c : row) {\r
+            XSSFCell cell = (XSSFCell) c;\r
+\r
+            CTCell ctCell = cell.getCTCell();\r
+            if (ctCell.isSetF()) {\r
+                CTCellFormula f = ctCell.getF();\r
+                String formula = f.getStringValue();\r
+                if (formula.length() > 0) {\r
+                    String shiftedFormula = shiftFormula(row, formula, shifter);\r
+                    if (shiftedFormula != null) {\r
+                        f.setStringValue(shiftedFormula);\r
+                    }\r
+                }\r
+\r
+                if (f.isSetRef()) { //Range of cells which the formula applies to.\r
+                    String ref = f.getRef();\r
+                    String shiftedRef = shiftFormula(row, ref, shifter);\r
+                    if (shiftedRef != null) f.setRef(shiftedRef);\r
+                }\r
+            }\r
+\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Shift a formula using the supplied FormulaShifter\r
+     *\r
+     * @param row     the row of the cell this formula belongs to. Used to get a reference to the parent workbook.\r
+     * @param formula the formula to shift\r
+     * @param shifter the FormulaShifter object that operates on the parsed formula tokens\r
+     * @return the shifted formula if the formula was changed,\r
+     *         <code>null</code> if the formula wasn't modified\r
+     */\r
+    private static String shiftFormula(XSSFRow row, String formula, FormulaShifter shifter) {\r
+        XSSFSheet sheet = row.getSheet();\r
+        XSSFWorkbook wb = sheet.getWorkbook();\r
+        int sheetIndex = wb.getSheetIndex(sheet);\r
+        XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb);\r
+        Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex);\r
+        String shiftedFmla = null;\r
+        if (shifter.adjustFormula(ptgs, sheetIndex)) {\r
+            shiftedFmla = FormulaRenderer.toFormulaString(fpb, ptgs);\r
+        }\r
+        return shiftedFmla;\r
+    }\r
+\r
+}\r
index 212271d0b71f6671b2de260d40e243dde7f398a8..fe9b8e425bc340cd48179c21489ac08eb8b3f0ae 100755 (executable)
@@ -38,8 +38,8 @@ public class TestSheetShiftRows  extends BaseTestSheetShiftRows {
         baseTestShiftRow();\r
     }\r
 \r
-    public void testShiftRow0() {\r
-        baseTestShiftRow0();\r
+    public void testShiftNames() {\r
+        baseTestShiftWithNames();\r
     }\r
 \r
     //TODO support shifting of page breaks\r
@@ -55,4 +55,8 @@ public class TestSheetShiftRows  extends BaseTestSheetShiftRows {
     public void testShiftWithFormulas() {\r
         baseTestShiftWithFormulas("ForShifting.xlsx");\r
     }\r
+\r
+    public void testShiftWithMergedRegions() {\r
+        baseTestShiftWithMergedRegions();\r
+    }\r
 }\r
index 84e0aebdd2080c0b9680a53a943fabb680d06d2c..bcb6b34d031251275949123d6fb8d4c1d10eb0bf 100755 (executable)
@@ -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();
+    }
 }
index a9305d8cae7d4a1a8322f6ae0a787984931487a7..083779c0b580eb59d6152a7914290c422734e9eb 100755 (executable)
@@ -19,6 +19,7 @@ package org.apache.poi.ss.usermodel;
 \r
 import junit.framework.TestCase;\r
 import org.apache.poi.ss.ITestDataProvider;\r
+import org.apache.poi.ss.util.CellRangeAddress;\r
 \r
 /**\r
  * Tests row shifting capabilities.\r
@@ -108,9 +109,10 @@ public abstract class BaseTestSheetShiftRows  extends TestCase {
     /**\r
      * Tests when shifting the first row.\r
      */\r
-    public final void baseTestShiftRow0() {\r
+    public final void baseTestActiveCell() {\r
         Workbook b = getTestDataProvider().createWorkbook();\r
         Sheet s        = b.createSheet();\r
+\r
         s.createRow(0).createCell(0).setCellValue("TEST1");\r
         s.createRow(3).createCell(0).setCellValue("TEST2");\r
         s.shiftRows(0,4,1);\r
@@ -190,6 +192,45 @@ public abstract class BaseTestSheetShiftRows  extends TestCase {
         assertEquals(comment4,comment4_shifted);\r
     }\r
 \r
+    public final void baseTestShiftWithNames() {\r
+        Workbook wb = getTestDataProvider().createWorkbook();\r
+        Sheet sheet    = wb.createSheet();\r
+        Row row = sheet.createRow(0);\r
+        row.createCell(0).setCellValue(1.1);\r
+        row.createCell(1).setCellValue(2.2);\r
+\r
+        Name name1 = wb.createName();\r
+        name1.setNameName("name1");\r
+        name1.setRefersToFormula("A1+B1");\r
+\r
+        Name name2 = wb.createName();\r
+        name2.setNameName("name2");\r
+        name2.setRefersToFormula("A1");\r
+\r
+        sheet.shiftRows(0, 1, 2);\r
+        name1 = wb.getNameAt(0);\r
+        assertEquals("A3+B3", name1.getRefersToFormula());\r
+\r
+        name2 = wb.getNameAt(1);\r
+        assertEquals("A3", name2.getRefersToFormula());    \r
+    }\r
+\r
+    public final void baseTestShiftWithMergedRegions() {\r
+        Workbook wb = getTestDataProvider().createWorkbook();\r
+        Sheet sheet    = wb.createSheet();\r
+        Row row = sheet.createRow(0);\r
+        row.createCell(0).setCellValue(1.1);\r
+        row.createCell(1).setCellValue(2.2);\r
+        CellRangeAddress region = new CellRangeAddress(0, 0, 0, 2);\r
+        assertEquals("A1:C1", region.formatAsString());\r
+\r
+        sheet.addMergedRegion(region);\r
+\r
+        sheet.shiftRows(0, 1, 2);\r
+        region = sheet.getMergedRegion(0);\r
+        assertEquals("A3:C3", region.formatAsString());\r
+   }\r
+\r
     /**\r
      * See bug #34023\r
      *\r