<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
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;
}
}
+
+ /**
+ * 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);
+ }
+ }
+ }
+
}
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) {
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;
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;
}
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);
* @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);
}
/**
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;
* 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();
}
/**
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);
}
}
}
}
- //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);
- }
- }
-
}
/**
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()]));
--- /dev/null
+/* ====================================================================\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
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
public void testShiftWithFormulas() {\r
baseTestShiftWithFormulas("ForShifting.xlsx");\r
}\r
+\r
+ public void testShiftWithMergedRegions() {\r
+ baseTestShiftWithMergedRegions();\r
+ }\r
}\r
baseTestShiftRow();
}
- public void testShiftRow0() {
- baseTestShiftRow0();
+ public void testShiftNames() {
+ baseTestShiftWithNames();
}
public void testShiftRowBreaks() {
baseTestShiftWithFormulas("ForShifting.xls");
}
+ public void testShiftWithMergedRegions() {
+ baseTestShiftWithMergedRegions();
+ }
}
\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
/**\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
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