<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.2-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">45780 - Fixed HSSFSheet.shiftRows to also update Area refs</action>
<action dev="POI-DEVELOPERS" type="fix">45804 - Update HSMF to handle Outlook 3.0 msg files, which have a different string chunk type</action>
<action dev="POI-DEVELOPERS" type="add">Expose the name of Named Cell Styles via HSSFCellStyle (normally held on the parent style though)</action>
<action dev="POI-DEVELOPERS" type="fix">45978 - Fixed IOOBE in Ref3DPtg.toFormulaString() due eager initialisation of SheetReferences</action>
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.2-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">45780 - Fixed HSSFSheet.shiftRows to also update Area refs</action>
<action dev="POI-DEVELOPERS" type="fix">45804 - Update HSMF to handle Outlook 3.0 msg files, which have a different string chunk type</action>
<action dev="POI-DEVELOPERS" type="add">Expose the name of Named Cell Styles via HSSFCellStyle (normally held on the parent style though)</action>
<action dev="POI-DEVELOPERS" type="fix">45978 - Fixed IOOBE in Ref3DPtg.toFormulaString() due eager initialisation of SheetReferences</action>
import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.ConditionalFormattingTable;
import org.apache.poi.hssf.record.aggregates.DataValidityTable;
-import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.aggregates.MergedCellsTable;
import org.apache.poi.hssf.record.aggregates.PageSettingsBlock;
import org.apache.poi.hssf.record.aggregates.RecordAggregate;
/*package*/ColumnInfoRecordsAggregate _columnInfos;
/** the DimensionsRecord is always present */
private DimensionsRecord _dimensions;
- protected RowRecordsAggregate _rowsAggregate = null;
+ /** always present */
+ protected RowRecordsAggregate _rowsAggregate;
private DataValidityTable _dataValidityTable= null;
private ConditionalFormattingTable condFormatting;
if (retval.windowTwo == null) {
throw new RuntimeException("WINDOW2 was not found");
}
+ if (retval._rowsAggregate == null) {
+ retval._rowsAggregate = new RowRecordsAggregate();
+ records.add(retval.dimsloc + 1, retval._rowsAggregate);
+ }
// put merged cells table in the right place (regardless of where the first MergedCellsRecord was found */
RecordOrderer.addNewSheetRecord(records, retval._mergedCellsTable);
retval.records = records;
- retval.checkRows();
if (log.check( POILogger.DEBUG ))
log.log(POILogger.DEBUG, "sheet createSheet (existing file) exited");
return retval;
retval._dimensions = createDimensions();
records.add(retval._dimensions);
retval.dimsloc = records.size()-1;
+ retval._rowsAggregate = new RowRecordsAggregate();
+ records.add(retval._rowsAggregate);
// 'Sheet View Settings'
records.add(retval.windowTwo = retval.createWindowTwo());
retval.selection = createSelection();
return retval;
}
- private void checkRows()
- {
- if (_rowsAggregate == null)
- {
- _rowsAggregate = new RowRecordsAggregate();
- records.add(dimsloc + 1, _rowsAggregate);
- }
+ public RowRecordsAggregate getRowsAggregate() {
+ return _rowsAggregate;
}
+
private MergedCellsTable getMergedRecords() {
// always present
return _mergedCellsTable;
return result;
}
- /**
- * Create a row record. (does not add it to the records contained in this sheet)
- */
- private static RowRecord createRow(int row) {
- return RowRecordsAggregate.createRow( row );
- }
-
/**
* Adds a value record to the sheet's contained binary records
* (i.e. LabelSSTRecord or NumberRecord).
public void addRow(RowRecord row)
{
- checkRows();
if (log.check( POILogger.DEBUG ))
log.log(POILogger.DEBUG, "addRow ");
DimensionsRecord d = _dimensions;
* @param row the row record to remove
*/
public void removeRow(RowRecord row) {
- checkRows();
_rowsAggregate.removeRow(row);
}
}
/**
- * Returns the first occurance of a record matching a particular sid.
+ * Returns the first occurrence of a record matching a particular sid.
*/
public Record findFirstRecordBySid(short sid)
public void groupRowRange(int fromRow, int toRow, boolean indent)
{
- checkRows();
for (int rowNum = fromRow; rowNum <= toRow; rowNum++)
{
RowRecord row = getRow( rowNum );
if (row == null)
{
- row = createRow( rowNum );
+ row = RowRecordsAggregate.createRow(rowNum);
addRow( row );
}
int level = row.getOutlineLevel();
guts.setLeftRowGutter( (short) ( 29 + (12 * (maxLevel)) ) );
}
- public void setRowGroupCollapsed( int row, boolean collapse )
- {
- if (collapse)
- {
- _rowsAggregate.collapseRow( row );
- }
- else
- {
- _rowsAggregate.expandRow( row );
- }
- }
public DataValidityTable getOrCreateDataValidityTable() {
if (_dataValidityTable == null) {
DataValidityTable result = new DataValidityTable();
}
return _dataValidityTable;
}
-
- public FormulaRecordAggregate createFormula(int row, int col) {
- return _rowsAggregate.createFormula(row, col);
- }
}
temp.add(ptg);
}
} else {
- Ptg ptg = new Ref3DPtg();
- ((Ref3DPtg) ptg).setExternSheetIndex(externSheetIndex);
- ((Ref3DPtg) ptg).setArea(ref);
+ Ref3DPtg ptg = new Ref3DPtg(ra.getFromCell(), externSheetIndex);
temp.add(ptg);
}
Ptg[] ptgs = new Ptg[temp.size()];
import org.apache.poi.hssf.record.SharedFormulaRecord;
import org.apache.poi.hssf.record.TableRecord;
import org.apache.poi.hssf.record.UnknownRecord;
+import org.apache.poi.hssf.record.formula.FormulaShifter;
/**
*
fr.setColumn((short) col);
return new FormulaRecordAggregate(fr, null, _sharedValueManager);
}
+ public void updateFormulasAfterRowShift(FormulaShifter formulaShifter, int currentExternSheetIndex) {
+ _valuesAgg.updateFormulasAfterRowShift(formulaShifter, currentExternSheetIndex);
+ }
}
import org.apache.poi.hssf.record.RecordBase;
import org.apache.poi.hssf.record.StringRecord;
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
+import org.apache.poi.hssf.record.formula.FormulaShifter;
+import org.apache.poi.hssf.record.formula.Ptg;
/**
*
}
}
+ public void updateFormulasAfterRowShift(FormulaShifter shifter, int currentExternSheetIndex) {
+ for (int i = 0; i < records.length; i++) {
+ CellValueRecordInterface[] rowCells = records[i];
+ if (rowCells == null) {
+ continue;
+ }
+ for (int j = 0; j < rowCells.length; j++) {
+ CellValueRecordInterface cell = rowCells[j];
+ if (cell instanceof FormulaRecordAggregate) {
+ FormulaRecord fr = ((FormulaRecordAggregate)cell).getFormulaRecord();
+ Ptg[] ptgs = fr.getParsedExpression(); // needs clone() inside this getter?
+ if (shifter.adjustFormula(ptgs, currentExternSheetIndex)) {
+ fr.setParsedExpression(ptgs);
+ }
+ }
+ }
+ }
+ }
+
public CellValueRecordInterface[] getValueRecords() {
List temp = new ArrayList();
* formulas. The sheet name will get properly delimited if required.
*/
public String toFormulaString(Workbook book) {
- // First do the sheet name
- StringBuffer retval = new StringBuffer();
- String sheetName = book.findSheetNameFromExternSheet(field_1_index_extern_sheet);
- if(sheetName != null) {
- if(sheetName.length() == 0) {
- // What excel does if sheet has been deleted
- sheetName = "#REF";
- retval.append(sheetName);
- } else {
- // Normal
- SheetNameFormatter.appendFormat(retval, sheetName);
- }
- retval.append( '!' );
- }
-
- // Now the normal area bit
- retval.append(formatReferenceAsString());
-
- return retval.toString();
+ return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet, formatReferenceAsString());
}
}
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.ss.usermodel.ErrorConstants;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.LittleEndian;
* @author Daniel Noll (daniel at nuix dot com dot au)
*/
public final class AreaErrPtg extends OperandPtg {
- public final static byte sid = 0x2b;
-
- public AreaErrPtg(RecordInputStream in) {
- // 8 bytes unused:
- in.readInt();
- in.readInt();
- }
-
- public void writeBytes(byte [] array, int offset) {
- array[offset] = (byte) (sid + getPtgClass());
- LittleEndian.putInt(array, offset+1, 0);
- LittleEndian.putInt(array, offset+5, 0);
- }
-
- public String toFormulaString(Workbook book) {
- return "#REF!";
- }
+ public final static byte sid = 0x2B;
+ private final int unused1;
+ private final int unused2;
+
+ public AreaErrPtg() {
+ unused1 = 0;
+ unused2 = 0;
+ }
+
+ public AreaErrPtg(RecordInputStream in) {
+ // 8 bytes unused:
+ unused1 = in.readInt();
+ unused2 = in.readInt();
+ }
+
+ public void writeBytes(byte[] array, int offset) {
+ LittleEndian.putByte(array, offset + 0, sid + getPtgClass());
+ LittleEndian.putInt(array, offset + 1, unused1);
+ LittleEndian.putInt(array, offset + 5, unused2);
+ }
+
+
+ public String toFormulaString(Workbook book) {
+ return ErrorConstants.getText(ErrorConstants.ERROR_REF);
+ }
public byte getDefaultOperandClass() {
return Ptg.CLASS_REF;
private final int unused1;
private final int unused2;
- public DeletedArea3DPtg( RecordInputStream in) {
+ public DeletedArea3DPtg(int externSheetIndex) {
+ field_1_index_extern_sheet = externSheetIndex;
+ unused1 = 0;
+ unused2 = 0;
+ }
+
+ public DeletedArea3DPtg(RecordInputStream in) {
field_1_index_extern_sheet = in.readUShort();
unused1 = in.readInt();
unused2 = in.readInt();
}
+
public String toFormulaString(Workbook book) {
- return ErrorConstants.getText(ErrorConstants.ERROR_REF);
+ return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet,
+ ErrorConstants.getText(ErrorConstants.ERROR_REF));
}
public byte getDefaultOperandClass() {
return Ptg.CLASS_REF;
unused1 = in.readInt();
}
+ public DeletedRef3DPtg(int externSheetIndex) {
+ field_1_index_extern_sheet = externSheetIndex;
+ unused1 = 0;
+ }
+
public String toFormulaString(Workbook book) {
- return ErrorConstants.getText(ErrorConstants.ERROR_REF);
+ return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet,
+ ErrorConstants.getText(ErrorConstants.ERROR_REF));
}
public byte getDefaultOperandClass() {
return Ptg.CLASS_REF;
--- /dev/null
+/* ====================================================================
+ 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.record.formula;
+
+import org.apache.poi.ss.usermodel.Workbook;
+
+/**
+ * @author Josh Micich
+ */
+final class ExternSheetNameResolver {
+
+ private ExternSheetNameResolver() {
+ // no instances of this class
+ }
+
+ public static String prependSheetName(Workbook book, int field_1_index_extern_sheet, String cellRefText) {
+ String sheetName = book.findSheetNameFromExternSheet(field_1_index_extern_sheet);
+ StringBuffer sb = new StringBuffer(sheetName.length() + cellRefText.length() + 4);
+ if (sheetName.length() < 1) {
+ // What excel does if sheet has been deleted
+ sb.append("#REF"); // note - '!' added just once below
+ } else {
+ SheetNameFormatter.appendFormat(sb, sheetName);
+ }
+ sb.append('!');
+ sb.append(cellRefText);
+ return sb.toString();
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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.record.formula;
+
+
+/**
+ * @author Josh Micich
+ */
+public final class FormulaShifter {
+
+ /**
+ * Extern sheet index of sheet where moving is occurring
+ */
+ private final int _externSheetIndex;
+ private final int _firstMovedIndex;
+ private final int _lastMovedIndex;
+ private final int _amountToMove;
+
+ private FormulaShifter(int externSheetIndex, int firstMovedIndex, int lastMovedIndex, int amountToMove) {
+ if (amountToMove == 0) {
+ throw new IllegalArgumentException("amountToMove must not be zero");
+ }
+ if (firstMovedIndex > lastMovedIndex) {
+ throw new IllegalArgumentException("firstMovedIndex, lastMovedIndex out of order");
+ }
+ _externSheetIndex = externSheetIndex;
+ _firstMovedIndex = firstMovedIndex;
+ _lastMovedIndex = lastMovedIndex;
+ _amountToMove = amountToMove;
+ }
+
+ public static FormulaShifter createForRowShift(int externSheetIndex, int firstMovedRowIndex, int lastMovedRowIndex, int numberOfRowsToMove) {
+ return new FormulaShifter(externSheetIndex, firstMovedRowIndex, lastMovedRowIndex, numberOfRowsToMove);
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append(getClass().getName());
+ sb.append(" [");
+ sb.append(_firstMovedIndex);
+ sb.append(_lastMovedIndex);
+ sb.append(_amountToMove);
+ return sb.toString();
+ }
+
+ /**
+ * @param ptgs - if necessary, will get modified by this method
+ * @param currentExternSheetIx - the extern sheet index of the sheet that contains the formula being adjusted
+ * @return <code>true</code> if a change was made to the formula tokens
+ */
+ public boolean adjustFormula(Ptg[] ptgs, int currentExternSheetIx) {
+ boolean refsWereChanged = false;
+ for(int i=0; i<ptgs.length; i++) {
+ Ptg newPtg = adjustPtg(ptgs[i], currentExternSheetIx);
+ if (newPtg != null) {
+ refsWereChanged = true;
+ ptgs[i] = newPtg;
+ }
+ }
+ return refsWereChanged;
+ }
+
+ private Ptg adjustPtg(Ptg ptg, int currentExternSheetIx) {
+ return adjustPtgDueToRowMove(ptg, currentExternSheetIx);
+ }
+ /**
+ * @return <code>true</code> if this Ptg needed to be changed
+ */
+ private Ptg adjustPtgDueToRowMove(Ptg ptg, int currentExternSheetIx) {
+ if(ptg instanceof RefPtg) {
+ if (currentExternSheetIx != _externSheetIndex) {
+ // local refs on other sheets are unaffected
+ return null;
+ }
+ RefPtg rptg = (RefPtg)ptg;
+ return rowMoveRefPtg(rptg);
+ }
+ if(ptg instanceof Ref3DPtg) {
+ Ref3DPtg rptg = (Ref3DPtg)ptg;
+ if (_externSheetIndex != rptg.getExternSheetIndex()) {
+ // only move 3D refs that refer to the sheet with cells being moved
+ // (currentExternSheetIx is irrelevant)
+ return null;
+ }
+ return rowMoveRefPtg(rptg);
+ }
+ if(ptg instanceof Area2DPtgBase) {
+ if (currentExternSheetIx != _externSheetIndex) {
+ // local refs on other sheets are unaffected
+ return ptg;
+ }
+ return rowMoveAreaPtg((Area2DPtgBase)ptg);
+ }
+ if(ptg instanceof Area3DPtg) {
+ Area3DPtg aptg = (Area3DPtg)ptg;
+ if (_externSheetIndex != aptg.getExternSheetIndex()) {
+ // only move 3D refs that refer to the sheet with cells being moved
+ // (currentExternSheetIx is irrelevant)
+ return null;
+ }
+ return rowMoveAreaPtg(aptg);
+ }
+ return null;
+ }
+
+ private Ptg rowMoveRefPtg(RefPtgBase rptg) {
+ int refRow = rptg.getRow();
+ if (_firstMovedIndex <= refRow && refRow <= _lastMovedIndex) {
+ // Rows being moved completely enclose the ref.
+ // - move the area ref along with the rows regardless of destination
+ rptg.setRow(refRow + _amountToMove);
+ return rptg;
+ }
+ // else rules for adjusting area may also depend on the destination of the moved rows
+
+ int destFirstRowIndex = _firstMovedIndex + _amountToMove;
+ int destLastRowIndex = _lastMovedIndex + _amountToMove;
+
+ // ref is outside source rows
+ // check for clashes with destination
+
+ if (destLastRowIndex < refRow || refRow < destFirstRowIndex) {
+ // destination rows are completely outside ref
+ return null;
+ }
+
+ if (destFirstRowIndex <= refRow && refRow <= destLastRowIndex) {
+ // destination rows enclose the area (possibly exactly)
+ return createDeletedRef(rptg);
+ }
+ throw new IllegalStateException("Situation not covered: (" + _firstMovedIndex + ", " +
+ _lastMovedIndex + ", " + _amountToMove + ", " + refRow + ", " + refRow + ")");
+ }
+
+ private Ptg rowMoveAreaPtg(AreaPtgBase aptg) {
+ int aFirstRow = aptg.getFirstRow();
+ int aLastRow = aptg.getLastRow();
+ if (_firstMovedIndex <= aFirstRow && aLastRow <= _lastMovedIndex) {
+ // Rows being moved completely enclose the area ref.
+ // - move the area ref along with the rows regardless of destination
+ aptg.setFirstRow(aFirstRow + _amountToMove);
+ aptg.setLastRow(aLastRow + _amountToMove);
+ return aptg;
+ }
+ // else rules for adjusting area may also depend on the destination of the moved rows
+
+ int destFirstRowIndex = _firstMovedIndex + _amountToMove;
+ int destLastRowIndex = _lastMovedIndex + _amountToMove;
+
+ if (aFirstRow < _firstMovedIndex && _lastMovedIndex < aLastRow) {
+ // Rows moved were originally *completely* within the area ref
+
+ // If the destination of the rows overlaps either the top
+ // or bottom of the area ref there will be a change
+ if (destFirstRowIndex < aFirstRow && aFirstRow <= destLastRowIndex) {
+ // truncate the top of the area by the moved rows
+ aptg.setFirstRow(destLastRowIndex+1);
+ return aptg;
+ } else if (destFirstRowIndex <= aLastRow && aLastRow < destLastRowIndex) {
+ // truncate the bottom of the area by the moved rows
+ aptg.setLastRow(destFirstRowIndex-1);
+ return aptg;
+ }
+ // else - rows have moved completely outside the area ref,
+ // or still remain completely within the area ref
+ return null; // - no change to the area
+ }
+ if (_firstMovedIndex <= aFirstRow && aFirstRow <= _lastMovedIndex) {
+ // Rows moved include the first row of the area ref, but not the last row
+ // btw: (aLastRow > _lastMovedIndex)
+ if (_amountToMove < 0) {
+ // simple case - expand area by shifting top upward
+ aptg.setFirstRow(aFirstRow + _amountToMove);
+ return aptg;
+ }
+ if (destFirstRowIndex > aLastRow) {
+ // in this case, excel ignores the row move
+ return null;
+ }
+ int newFirstRowIx = aFirstRow + _amountToMove;
+ if (destLastRowIndex < aLastRow) {
+ // end of area is preserved (will remain exact same row)
+ // the top area row is moved simply
+ aptg.setFirstRow(newFirstRowIx);
+ return aptg;
+ }
+ // else - bottom area row has been replaced - both area top and bottom may move now
+ int areaRemainingTopRowIx = _lastMovedIndex + 1;
+ if (destFirstRowIndex > areaRemainingTopRowIx) {
+ // old top row of area has moved deep within the area, and exposed a new top row
+ newFirstRowIx = areaRemainingTopRowIx;
+ }
+ aptg.setFirstRow(newFirstRowIx);
+ aptg.setLastRow(Math.max(aLastRow, destLastRowIndex));
+ return aptg;
+ }
+ if (_firstMovedIndex <= aLastRow && aLastRow <= _lastMovedIndex) {
+ // Rows moved include the last row of the area ref, but not the first
+ // btw: (aFirstRow < _firstMovedIndex)
+ if (_amountToMove > 0) {
+ // simple case - expand area by shifting bottom downward
+ aptg.setLastRow(aLastRow + _amountToMove);
+ return aptg;
+ }
+ if (destLastRowIndex < aFirstRow) {
+ // in this case, excel ignores the row move
+ return null;
+ }
+ int newLastRowIx = aLastRow + _amountToMove;
+ if (destFirstRowIndex > aFirstRow) {
+ // top of area is preserved (will remain exact same row)
+ // the bottom area row is moved simply
+ aptg.setLastRow(newLastRowIx);
+ return aptg;
+ }
+ // else - top area row has been replaced - both area top and bottom may move now
+ int areaRemainingBottomRowIx = _firstMovedIndex - 1;
+ if (destLastRowIndex < areaRemainingBottomRowIx) {
+ // old bottom row of area has moved up deep within the area, and exposed a new bottom row
+ newLastRowIx = areaRemainingBottomRowIx;
+ }
+ aptg.setFirstRow(Math.min(aFirstRow, destFirstRowIndex));
+ aptg.setLastRow(newLastRowIx);
+ return aptg;
+ }
+ // else source rows include none of the rows of the area ref
+ // check for clashes with destination
+
+ if (destLastRowIndex < aFirstRow || aLastRow < destFirstRowIndex) {
+ // destination rows are completely outside area ref
+ return null;
+ }
+
+ if (destFirstRowIndex <= aFirstRow && aLastRow <= destLastRowIndex) {
+ // destination rows enclose the area (possibly exactly)
+ return createDeletedRef(aptg);
+ }
+
+ if (aFirstRow <= destFirstRowIndex && destLastRowIndex <= aLastRow) {
+ // destination rows are within area ref (possibly exact on top or bottom, but not both)
+ return null; // - no change to area
+ }
+
+ if (destFirstRowIndex < aFirstRow && aFirstRow <= destLastRowIndex) {
+ // dest rows overlap top of area
+ // - truncate the top
+ aptg.setFirstRow(destLastRowIndex+1);
+ return aptg;
+ }
+ if (destFirstRowIndex < aLastRow && aLastRow <= destLastRowIndex) {
+ // dest rows overlap bottom of area
+ // - truncate the bottom
+ aptg.setLastRow(destFirstRowIndex-1);
+ return aptg;
+ }
+ throw new IllegalStateException("Situation not covered: (" + _firstMovedIndex + ", " +
+ _lastMovedIndex + ", " + _amountToMove + ", " + aFirstRow + ", " + aLastRow + ")");
+ }
+
+ private static Ptg createDeletedRef(Ptg ptg) {
+ if (ptg instanceof RefPtg) {
+ return new RefErrorPtg();
+ }
+ if (ptg instanceof Ref3DPtg) {
+ Ref3DPtg rptg = (Ref3DPtg) ptg;
+ return new DeletedRef3DPtg(rptg.getExternSheetIndex());
+ }
+ if (ptg instanceof AreaPtg) {
+ return new AreaErrPtg();
+ }
+ if (ptg instanceof Area3DPtg) {
+ Area3DPtg area3DPtg = (Area3DPtg) ptg;
+ return new DeletedArea3DPtg(area3DPtg.getExternSheetIndex());
+ }
+
+ throw new IllegalArgumentException("Unexpected ref ptg class (" + ptg.getClass().getName() + ")");
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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.record.formula;
+
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * @author Josh Micich
+ */
+abstract class Ref2DPtgBase extends RefPtgBase {
+ private final static int SIZE = 5;
+
+ /**
+ * Takes in a String representation of a cell reference and fills out the
+ * numeric fields.
+ */
+ protected Ref2DPtgBase(String cellref) {
+ super(cellref);
+ }
+
+ protected Ref2DPtgBase(int row, int column, boolean isRowRelative, boolean isColumnRelative) {
+ setRow(row);
+ setColumn(column);
+ setRowRelative(isRowRelative);
+ setColRelative(isColumnRelative);
+ }
+
+ protected Ref2DPtgBase(RecordInputStream in) {
+ readCoordinates(in);
+ }
+ public final void writeBytes(byte [] array, int offset) {
+ LittleEndian.putByte(array, offset+0, getSid() + getPtgClass());
+ writeCoordinates(array, offset+1);
+ }
+ public final String toFormulaString(Workbook book) {
+ return formatReferenceAsString();
+ }
+
+ protected abstract byte getSid();
+ public final int getSize() {
+ return SIZE;
+ }
+ public final String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(getClass().getName());
+ sb.append(" [");
+ sb.append(formatReferenceAsString());
+ sb.append("]");
+ return sb.toString();
+ }
+}
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.util.RangeAddress;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellReference;
-import org.apache.poi.ss.util.SheetReferences;
-import org.apache.poi.util.BitField;
-import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.LittleEndian;
/**
* @author Jason Height (jheight at chariot dot net dot au)
* @version 1.0-pre
*/
-public final class Ref3DPtg extends OperandPtg {
+public final class Ref3DPtg extends RefPtgBase {
public final static byte sid = 0x3a;
- private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000);
- private static final BitField colRelative = BitFieldFactory.getInstance(0x4000);
-
private final static int SIZE = 7; // 6 + 1 for Ptg
private int field_1_index_extern_sheet;
- /** The row index - zero based unsigned 16 bit value */
- private int field_2_row;
- /** Field 2
- * - lower 8 bits is the zero based unsigned byte column index
- * - bit 16 - isRowRelative
- * - bit 15 - isColumnRelative
- */
- private int field_3_column;
/** Creates new AreaPtg */
public Ref3DPtg() {}
public Ref3DPtg(RecordInputStream in) {
field_1_index_extern_sheet = in.readShort();
- field_2_row = in.readShort();
- field_3_column = in.readShort();
+ readCoordinates(in);
}
public Ref3DPtg(String cellref, short externIdx ) {
CellReference c= new CellReference(cellref);
setRow(c.getRow());
- setColumn((short)c.getCol());
+ setColumn(c.getCol());
setColRelative(!c.isColAbsolute());
setRowRelative(!c.isRowAbsolute());
setExternSheetIndex(externIdx);
}
public String toString() {
- CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(),!isColRelative());
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName());
sb.append(" [");
sb.append("sheetIx=").append(getExternSheetIndex());
sb.append(" ! ");
- sb.append(cr.formatAsString());
+ sb.append(formatReferenceAsString());
sb.append("]");
return sb.toString();
}
public void writeBytes(byte [] array, int offset) {
array[ 0 + offset ] = (byte) (sid + getPtgClass());
LittleEndian.putShort(array, 1 + offset , getExternSheetIndex());
- LittleEndian.putShort(array, 3 + offset , (short)getRow());
- LittleEndian.putShort(array, 5 + offset , (short)getColumnRaw());
+ writeCoordinates(array, offset+3);
}
public int getSize() {
field_1_index_extern_sheet = index;
}
- public int getRow() {
- return field_2_row;
- }
-
- public void setRow(int row) {
- field_2_row = row;
- }
-
- public int getColumn() {
- return field_3_column & 0xFF;
- }
-
- public int getColumnRaw() {
- return field_3_column;
- }
-
- public boolean isRowRelative()
- {
- return rowRelative.isSet(field_3_column);
- }
-
- public void setRowRelative(boolean rel) {
- field_3_column=rowRelative.setBoolean(field_3_column,rel);
- }
-
- public boolean isColRelative()
- {
- return colRelative.isSet(field_3_column);
- }
-
- public void setColRelative(boolean rel) {
- field_3_column=colRelative.setBoolean(field_3_column,rel);
- }
- public void setColumn(short column) {
- field_3_column &= 0xFF00;
- field_3_column |= column & 0xFF;
- }
-
- public void setColumnRaw(short column) {
- field_3_column = column;
- }
-
- /* public String getArea(){
- RangeAddress ra = new RangeAddress("");
-
- String result = (ra.numTo26Sys(getColumn()) + (getRow() + 1));
-
- return result;
- }*/
-
- public void setArea(String ref){
- RangeAddress ra = new RangeAddress(ref);
-
- String from = ra.getFromCell();
-
- setColumn((short) (ra.getXPosition(from) -1));
- setRow((short) (ra.getYPosition(from) -1));
-
- }
-
/**
* @return text representation of this cell reference that can be used in text
* formulas. The sheet name will get properly delimited if required.
*/
- public String toFormulaString(Workbook book)
- {
- StringBuffer retval = new StringBuffer();
- String sheetName = book.findSheetNameFromExternSheet(field_1_index_extern_sheet);
- if(sheetName != null) {
- SheetNameFormatter.appendFormat(retval, sheetName);
- retval.append( '!' );
- }
- retval.append((new CellReference(getRow(),getColumn(),!isRowRelative(),!isColRelative())).formatAsString());
- return retval.toString();
+ public String toFormulaString(Workbook book) {
+ return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet, formatReferenceAsString());
}
-
- public byte getDefaultOperandClass() {
- return Ptg.CLASS_REF;
- }
}
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.ss.usermodel.ErrorConstants;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.LittleEndian;
public final class RefErrorPtg extends OperandPtg {
private final static int SIZE = 5;
- public final static byte sid = 0x2a;
+ public final static byte sid = 0x2A;
private int field_1_reserved;
- public RefErrorPtg(RecordInputStream in)
- {
+ public RefErrorPtg() {
+ field_1_reserved = 0;
+ }
+ public RefErrorPtg(RecordInputStream in) {
field_1_reserved = in.readInt();
-
}
- public String toString()
- {
- StringBuffer buffer = new StringBuffer("[RefError]\n");
-
- buffer.append("reserved = ").append(getReserved()).append("\n");
- return buffer.toString();
+ public String toString() {
+ return getClass().getName();
}
- public void writeBytes(byte [] array, int offset)
- {
- array[offset] = (byte) (sid + getPtgClass());
+ public void writeBytes(byte [] array, int offset) {
+ LittleEndian.putByte(array, offset+0, sid + getPtgClass());
LittleEndian.putInt(array,offset+1,field_1_reserved);
}
- public void setReserved(int reserved)
- {
- field_1_reserved = reserved;
- }
-
- public int getReserved()
- {
- return field_1_reserved;
- }
-
public int getSize()
{
return SIZE;
}
- public String toFormulaString(Workbook book)
- {
- //TODO -- should we store a cellreference instance in this ptg?? but .. memory is an issue, i believe!
- return "#REF!";
+ public String toFormulaString(Workbook book) {
+ return ErrorConstants.getText(ErrorConstants.ERROR_REF);
}
public byte getDefaultOperandClass() {
* RefNPtg
* @author Jason Height (jheight at apache dot com)
*/
-public final class RefNPtg extends RefPtgBase {
- public final static byte sid = 0x2C;
+public final class RefNPtg extends Ref2DPtgBase {
+ public final static byte sid = 0x2C;
- /** Creates new ValueReferencePtg */
+ public RefNPtg(RecordInputStream in) {
+ super(in);
+ }
- public RefNPtg(RecordInputStream in) {
- super(in);
- }
-
- protected byte getSid() {
- return sid;
- }
+ protected byte getSid() {
+ return sid;
+ }
}
* @author Andrew C. Oliver (acoliver@apache.org)\r
* @author Jason Height (jheight at chariot dot net dot au)\r
*/\r
-public final class RefPtg extends RefPtgBase {\r
- public final static byte sid = 0x24;\r
-\r
- /**\r
- * Takes in a String representation of a cell reference and fills out the\r
- * numeric fields.\r
- */\r
- public RefPtg(String cellref) {\r
- super(cellref);\r
- }\r
-\r
- public RefPtg(int row, int column, boolean isRowRelative, boolean isColumnRelative) {\r
- setRow(row);\r
- setColumn(column);\r
- setRowRelative(isRowRelative);\r
- setColRelative(isColumnRelative);\r
- }\r
-\r
- public RefPtg(RecordInputStream in) {\r
- super(in);\r
- }\r
+public final class RefPtg extends Ref2DPtgBase {\r
+ public final static byte sid = 0x24;\r
+\r
+ /**\r
+ * Takes in a String representation of a cell reference and fills out the\r
+ * numeric fields.\r
+ */\r
+ public RefPtg(String cellref) {\r
+ super(cellref);\r
+ }\r
+\r
+ public RefPtg(int row, int column, boolean isRowRelative, boolean isColumnRelative) {\r
+ super(row, column, isRowRelative, isColumnRelative);\r
+ }\r
+\r
+ public RefPtg(RecordInputStream in) {\r
+ super(in);\r
+ }\r
\r
protected byte getSid() {\r
return sid;\r
}\r
- \r
}\r
package org.apache.poi.hssf.record.formula;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
-
-import org.apache.poi.ss.util.CellReference;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.util.LittleEndian;
/**
* ReferencePtgBase - handles references (such as A1, A2, IA4)
*/
public abstract class RefPtgBase extends OperandPtg {
- private final static int SIZE = 5;
private final static int MAX_ROW_NUMBER = 65536;
/** The row index - zero based unsigned 16 bit value */
setColRelative(isColumnRelative);
}
- protected RefPtgBase(RecordInputStream in) {
+ protected final void readCoordinates(RecordInputStream in) {
field_1_row = in.readUShort();
field_2_col = in.readUShort();
}
-
- public final String toString() {
- CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(),!isColRelative());
- StringBuffer sb = new StringBuffer();
- sb.append(getClass().getName());
- sb.append(" [");
- sb.append(cr.formatAsString());
- sb.append("]");
- return sb.toString();
- }
-
- public final void writeBytes(byte [] array, int offset) {
- array[offset] = (byte) (getSid() + getPtgClass());
-
- LittleEndian.putShort(array, offset+1, (short)field_1_row);
- LittleEndian.putShort(array, offset+3, (short)field_2_col);
+ protected final void writeCoordinates(byte[] array, int offset) {
+ LittleEndian.putUShort(array, offset + 0, field_1_row);
+ LittleEndian.putUShort(array, offset + 2, field_2_col);
}
-
- protected abstract byte getSid();
public final void setRow(int row) {
if(row < 0 || row >= MAX_ROW_NUMBER) {
}
/**
- * Returns the row number as a short, which will be
- * wrapped (negative) for values between 32769 and 65535
+ * @return the row number as an int, between 0 and 65535
*/
public final int getRow(){
return field_1_row;
}
- /**
- * Returns the row number as an int, between 0 and 65535
- */
- public final int getRowAsInt() {
- return field_1_row;
- }
public final boolean isRowRelative() {
return rowRelative.isSet(field_2_col);
if(col < 0 || col >= 0x100) {
throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range");
}
- field_2_col = column.setValue(field_2_col, col);
+ field_2_col = column.setValue(field_2_col, col);
}
public final int getColumn() {
- return column.getValue(field_2_col);
- }
-
- public final int getSize() {
- return SIZE;
+ return column.getValue(field_2_col);
}
-
- public final String toFormulaString(Workbook book) {
- //TODO -- should we store a cellreference instance in this ptg?? but .. memory is an issue, i believe!
- return (new CellReference(getRowAsInt(),getColumn(),!isRowRelative(),!isColRelative())).formatAsString();
+ protected final String formatReferenceAsString() {
+ // Only make cell references as needed. Memory is an issue
+ CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(), !isColRelative());
+ return cr.formatAsString();
}
public final byte getDefaultOperandClass() {
FormulaRecordAggregate frec;
if (cellType != this.cellType) {
- frec = sheet.getSheet().createFormula(row, col);
+ frec = sheet.getSheet().getRowsAggregate().createFormula(row, col);
} else {
frec = (FormulaRecordAggregate) record;
frec.setRow(row);
Ptg[] ptgs = FormulaParser.parse(formula, book);
frec.setParsedExpression(ptgs);
}
- /* package */ void setFormulaOnly(Ptg[] ptgs) {
- if (ptgs == null) {
- throw new IllegalArgumentException("ptgs must not be null");
- }
- ((FormulaRecordAggregate)record).getFormulaRecord().setParsedExpression(ptgs);
- }
public String getCellFormula() {
return FormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
* @return true if the name refers to a deleted cell, false otherwise
*/
public boolean isDeleted(){
- String ref = getReference();
- return "#REF!".endsWith(ref);
+ String formulaText = getReference();
+ if (formulaText.startsWith("#REF!")) {
+ // sheet deleted
+ return true;
+ }
+ if (formulaText.endsWith("#REF!")) {
+ // cell range deleted
+ return true;
+ }
+ return false;
}
public boolean isFunctionName() {
return _definedNameRec.isFunctionName();
import java.util.TreeMap;
import org.apache.poi.ddf.EscherRecord;
-import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.WSBoolRecord;
import org.apache.poi.hssf.record.WindowTwoRecord;
import org.apache.poi.hssf.record.aggregates.DataValidityTable;
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.RefPtg;
+import org.apache.poi.hssf.record.formula.FormulaShifter;
import org.apache.poi.hssf.util.PaneInformation;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
* @param resetOriginalRowHeight whether to set the original row's height to the default
* @param moveComments whether to move comments at the same time as the cells they are attached to
*/
- public void shiftRows( int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight, boolean moveComments)
- {
- int s, e, inc;
- if ( n < 0 )
- {
+ public void shiftRows(int startRow, int endRow, int n,
+ boolean copyRowHeight, boolean resetOriginalRowHeight, boolean moveComments) {
+ int s, inc;
+ if (n < 0) {
s = startRow;
- e = endRow;
inc = 1;
- }
- else
- {
+ } else {
s = endRow;
- e = startRow;
inc = -1;
}
shiftMerged(startRow, endRow, n, true);
sheet.getPageSettings().shiftRowBreaks(startRow, endRow, n);
- for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc )
- {
+ for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc ) {
HSSFRow row = getRow( rowNum );
HSSFRow row2Replace = getRow( rowNum + n );
if ( row2Replace == null )
row2Replace = createRow( rowNum + n );
- HSSFCell cell;
// Remove all the old cells from the row we'll
// be writing too, before we start overwriting
// Copy each cell from the source row to
// the destination row
for(Iterator cells = row.cellIterator(); cells.hasNext(); ) {
- cell = (HSSFCell)cells.next();
+ HSSFCell cell = (HSSFCell)cells.next();
row.removeCell( cell );
CellValueRecordInterface cellRecord = cell.getCellValueRecord();
cellRecord.setRow( rowNum + n );
// Update any formulas on this sheet that point to
// rows which have been moved
- updateFormulasAfterShift(startRow, endRow, n);
- }
-
- /**
- * Called by shiftRows to update formulas on this sheet
- * to point to the new location of moved rows
- */
- private void updateFormulasAfterShift(int startRow, int endRow, int n) {
- // Need to look at every cell on the sheet
- // Not just those that were moved
- Iterator ri = rowIterator();
- while(ri.hasNext()) {
- HSSFRow r = (HSSFRow)ri.next();
- Iterator ci = r.cellIterator();
- while(ci.hasNext()) {
- HSSFCell c = (HSSFCell)ci.next();
- if(c.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
- // Since it's a formula cell, process the
- // formula string, and look to see if
- // it contains any references
-
- // Look for references, and update if needed
- Ptg[] ptgs = FormulaParser.parse(c.getCellFormula(), workbook);
- boolean changed = false;
- for(int i=0; i<ptgs.length; i++) {
- if(ptgs[i] instanceof RefPtg) {
- RefPtg rptg = (RefPtg)ptgs[i];
- if(startRow <= rptg.getRowAsInt() &&
- rptg.getRowAsInt() <= endRow) {
- // References a row that moved
- rptg.setRow(rptg.getRowAsInt() + n);
- changed = true;
- }
- }
- }
- // If any references were changed, then
- // re-create the formula string
- if(changed) {
- c.setFormulaOnly(ptgs);
- }
- }
+ int sheetIndex = workbook.getSheetIndex(this);
+ short externSheetIndex = book.checkExternSheet(sheetIndex);
+ FormulaShifter shifter = FormulaShifter.createForRowShift(externSheetIndex, startRow, endRow, n);
+ sheet.getRowsAggregate().updateFormulasAfterRowShift(shifter, externSheetIndex);
+
+ int nSheets = workbook.getNumberOfSheets();
+ for(int i=0; i<nSheets; i++) {
+ Sheet otherSheet = workbook.getSheetAt(i).getSheet();
+ if (otherSheet == this.sheet) {
+ continue;
}
+ short otherExtSheetIx = book.checkExternSheet(i);
+ otherSheet.getRowsAggregate().updateFormulasAfterRowShift(shifter, otherExtSheetIx);
}
+ // TODO - adjust formulas in named ranges
}
protected void insertChartRecords( List records )
sheet.groupRowRange( fromRow, toRow, false );
}
- public void setRowGroupCollapsed( int row, boolean collapse )
- {
- sheet.setRowGroupCollapsed( row, collapse );
+ public void setRowGroupCollapsed(int rowIndex, boolean collapse) {
+ if (collapse) {
+ sheet.getRowsAggregate().collapseRow(rowIndex);
+ } else {
+ sheet.getRowsAggregate().expandRow(rowIndex);
+ }
}
/**
* if needed.
* Used by some of the more obscure formula and
* named range things.
+ * @deprecated for POI internal use only (formula parsing). This method is likely to
+ * be removed in future versions of POI.
*/
public int getExternalSheetIndex(int internalSheetIndex) {
return workbook.checkExternSheet(internalSheetIndex);
}
+ /**
+ * @deprecated for POI internal use only (formula rendering). This method is likely to
+ * be removed in future versions of POI.
+ */
+ public String findSheetNameFromExternSheet(int externSheetIndex){
+ // TODO - don't expose internal ugliness like externSheet indexes to the user model API
+ return workbook.findSheetNameFromExternSheet(externSheetIndex);
+ }
+ /**
+ * @deprecated for POI internal use only (formula rendering). This method is likely to
+ * be removed in future versions of POI.
+ *
+ * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
+ * @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
+ * @return the string representation of the defined or external name
+ */
+ public String resolveNameXText(int refIndex, int definedNameIndex) {
+ // TODO - make this less cryptic / move elsewhere
+ return workbook.resolveNameXText(refIndex, definedNameIndex);
+ }
+
+
+
/**
* create an HSSFSheet for this HSSFWorkbook, adds it to the sheets and returns
return retval;
}
- /**
- * @deprecated for POI internal use only (formula rendering). This method is likely to
- * be removed in future versions of POI.
- */
- public String findSheetNameFromExternSheet(int externSheetIndex){
- // TODO - don't expose internal ugliness like externSheet indexes to the user model API
- return workbook.findSheetNameFromExternSheet(externSheetIndex);
- }
-
/**
* Removes sheet at the given index.<p/>
*
return result;
}
- /**
- * @deprecated for POI internal use only (formula rendering). This method is likely to
- * be removed in future versions of POI.
- *
- * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
- * @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
- * @return the string representation of the defined or external name
- */
- public String resolveNameXText(int refIndex, int definedNameIndex) {
- // TODO - make this less cryptic / move elsewhere
- return workbook.resolveNameXText(refIndex, definedNameIndex);
- }
-
-
/**
* Sets the printarea for the sheet provided
* <p>
result.addTestSuite(TestArrayPtg.class);
result.addTestSuite(TestErrPtg.class);
result.addTestSuite(TestExternalFunctionFormulas.class);
+ result.addTestSuite(TestFormulaShifter.class);
result.addTestSuite(TestFuncPtg.class);
result.addTestSuite(TestFuncVarPtg.class);
result.addTestSuite(TestIntersectionPtg.class);
--- /dev/null
+/* ====================================================================
+ 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.record.formula;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link FormulaShifter}.
+ *
+ * @author Josh Micich
+ */
+public final class TestFormulaShifter extends TestCase {
+ // Note - the expected result row coordinates here were determined/verified
+ // in Excel 2007 by manually testing.
+
+ /**
+ * Tests what happens to area refs when a range of rows from inside, or overlapping are
+ * moved
+ */
+ public void testShiftAreasSourceRows() {
+
+ // all these operations are on an area ref spanning rows 10 to 20
+ AreaPtg aptg = createAreaPtg(10, 20);
+
+ confirmAreaShift(aptg, 9, 21, 20, 30, 40);
+ confirmAreaShift(aptg, 10, 21, 20, 30, 40);
+ confirmAreaShift(aptg, 9, 20, 20, 30, 40);
+
+ confirmAreaShift(aptg, 8, 11, -3, 7, 20); // simple expansion of top
+ // rows containing area top being shifted down:
+ confirmAreaShift(aptg, 8, 11, 3, 13, 20);
+ confirmAreaShift(aptg, 8, 11, 7, 17, 20);
+ confirmAreaShift(aptg, 8, 11, 8, 18, 20);
+ confirmAreaShift(aptg, 8, 11, 9, 12, 20); // note behaviour changes here
+ confirmAreaShift(aptg, 8, 11, 10, 12, 21);
+ confirmAreaShift(aptg, 8, 11, 12, 12, 23);
+ confirmAreaShift(aptg, 8, 11, 13, 10, 20); // ignored
+
+ // rows from within being moved:
+ confirmAreaShift(aptg, 12, 16, 3, 10, 20); // stay within - no change
+ confirmAreaShift(aptg, 11, 19, 20, 10, 20); // move completely out - no change
+ confirmAreaShift(aptg, 16, 17, -6, 10, 20); // moved exactly to top - no change
+ confirmAreaShift(aptg, 16, 17, -7, 11, 20); // truncation at top
+ confirmAreaShift(aptg, 12, 16, 4, 10, 20); // moved exactly to bottom - no change
+ confirmAreaShift(aptg, 12, 16, 6, 10, 17); // truncation at bottom
+
+ // rows containing area bottom being shifted up:
+ confirmAreaShift(aptg, 18, 22, -1, 10, 19); // simple contraction at bottom
+ confirmAreaShift(aptg, 18, 22, -7, 10, 13); // simple contraction at bottom
+ confirmAreaShift(aptg, 18, 22, -8, 10, 17); // top calculated differently here
+ confirmAreaShift(aptg, 18, 22, -9, 9, 17);
+ confirmAreaShift(aptg, 18, 22,-15, 10, 20); // no change because range would be turned inside out
+ confirmAreaShift(aptg, 15, 19, -7, 13, 20); // dest truncates top (even though src is from inside range)
+ confirmAreaShift(aptg, 19, 23,-12, 7, 18); // complex: src encloses bottom, dest encloses top
+
+ confirmAreaShift(aptg, 18, 22, 5, 10, 25); // simple expansion at bottom
+ }
+ /**
+ * Tests what happens to an area ref when some outside rows are moved to overlap
+ * that area ref
+ */
+ public void testShiftAreasDestRows() {
+ // all these operations are on an area ref spanning rows 20 to 25
+ AreaPtg aptg = createAreaPtg(20, 25);
+
+ // no change because no overlap:
+ confirmAreaShift(aptg, 5, 10, 9, 20, 25);
+ confirmAreaShift(aptg, 5, 10, 21, 20, 25);
+
+ confirmAreaShift(aptg, 11, 14, 10, 20, 25);
+
+ confirmAreaShift(aptg, 7, 17, 10, -1, -1); // converted to DeletedAreaRef
+ confirmAreaShift(aptg, 5, 15, 7, 23, 25); // truncation at top
+ confirmAreaShift(aptg, 13, 16, 10, 20, 22); // truncation at bottom
+ }
+
+ private static void confirmAreaShift(AreaPtg aptg,
+ int firstRowMoved, int lastRowMoved, int numberRowsMoved,
+ int expectedAreaFirstRow, int expectedAreaLastRow) {
+
+ FormulaShifter fs = FormulaShifter.createForRowShift(0, firstRowMoved, lastRowMoved, numberRowsMoved);
+ boolean expectedChanged = aptg.getFirstRow() != expectedAreaFirstRow || aptg.getLastRow() != expectedAreaLastRow;
+
+ AreaPtg copyPtg = (AreaPtg) aptg.copy(); // clone so we can re-use aptg in calling method
+ Ptg[] ptgs = { copyPtg, };
+ boolean actualChanged = fs.adjustFormula(ptgs, 0);
+ if (expectedAreaFirstRow < 0) {
+ assertEquals(AreaErrPtg.class, ptgs[0].getClass());
+ return;
+ }
+ assertEquals(expectedChanged, actualChanged);
+ assertEquals(copyPtg, ptgs[0]); // expected to change in place (although this is not a strict requirement)
+ assertEquals(expectedAreaFirstRow, copyPtg.getFirstRow());
+ assertEquals(expectedAreaLastRow, copyPtg.getLastRow());
+
+ }
+ private static AreaPtg createAreaPtg(int initialAreaFirstRow, int initialAreaLastRow) {
+ return new AreaPtg(initialAreaFirstRow, initialAreaLastRow, 2, 5, false, false, false, false);
+ }
+}
sb.write(new FileOutputStream(file));
}
- /*Unknown Ptg 3C*/
+ /** Unknown Ptg 3C*/
public void test27272_1() throws Exception {
HSSFWorkbook wb = openSample("27272_1.xls");
wb.getSheetAt(0);
- assertEquals("Reference for named range ", "#REF!",wb.getNameAt(0).getReference());
+ assertEquals("Reference for named range ", "Compliance!#REF!",wb.getNameAt(0).getReference());
File outF = File.createTempFile("bug27272_1",".xls");
wb.write(new FileOutputStream(outF));
System.out.println("Open "+outF.getAbsolutePath()+" in Excel");
}
- /*Unknown Ptg 3D*/
+ /** Unknown Ptg 3D*/
public void test27272_2() throws Exception {
HSSFWorkbook wb = openSample("27272_2.xls");
- assertEquals("Reference for named range ", "#REF!",wb.getNameAt(0).getReference());
+ assertEquals("Reference for named range ", "'LOAD.POD_HISTORIES'!#REF!",wb.getNameAt(0).getReference());
File outF = File.createTempFile("bug27272_2",".xls");
wb.write(new FileOutputStream(outF));
System.out.println("Open "+outF.getAbsolutePath()+" in Excel");
}
- /* MissingArgPtg */
+ /** MissingArgPtg */
public void testMissingArgPtg() throws Exception {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFCell cell = wb.createSheet("Sheet1").createRow(4).createCell(0);
HSSFName name2 = wb.getNameAt(1);
assertEquals("b", name2.getNameName());
- assertEquals("#REF!", name2.getReference());
+ assertEquals("Sheet1!#REF!", name2.getReference());
assertTrue(name2.isDeleted());
try {
AreaReference ref2 = new AreaReference(name2.getReference());
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
import junit.framework.TestCase;
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ForShifting.xls");
HSSFSheet sheet = wb.getSheet("Sheet1");
- assertEquals(19, sheet.getLastRowNum());
-
- assertEquals("cell B1 (ref)", sheet.getRow(0).getCell(3).getRichStringCellValue().toString());
- assertEquals("CONCATENATE(B1,\" (ref)\")", sheet.getRow(0).getCell(3).getCellFormula());
- assertEquals("cell B2 (ref)", sheet.getRow(1).getCell(3).getRichStringCellValue().toString());
- assertEquals("CONCATENATE(B2,\" (ref)\")", sheet.getRow(1).getCell(3).getCellFormula());
- assertEquals("cell B3 (ref)", sheet.getRow(2).getCell(3).getRichStringCellValue().toString());
- assertEquals("CONCATENATE(B3,\" (ref)\")", sheet.getRow(2).getCell(3).getCellFormula());
- assertEquals("cell B2 (ref)", sheet.getRow(6).getCell(1).getRichStringCellValue().toString());
- assertEquals("CONCATENATE(B2,\" (ref)\")", sheet.getRow(6).getCell(1).getCellFormula());
-
+ assertEquals(20, sheet.getLastRowNum());
+
+ confirmRow(sheet, 0, 1, 171, 1, "ROW(D1)", "100+B1", "COUNT(D1:E1)");
+ confirmRow(sheet, 1, 2, 172, 1, "ROW(D2)", "100+B2", "COUNT(D2:E2)");
+ confirmRow(sheet, 2, 3, 173, 1, "ROW(D3)", "100+B3", "COUNT(D3:E3)");
+
+ confirmCell(sheet, 6, 1, 271, "200+B1");
+ confirmCell(sheet, 7, 1, 272, "200+B2");
+ confirmCell(sheet, 8, 1, 273, "200+B3");
+
+ confirmCell(sheet, 14, 0, 0.0, "A12"); // the cell referred to by this formula will be replaced
+
+ // -----------
+ // Row index 1 -> 11 (row "2" -> row "12")
sheet.shiftRows(1, 1, 10);
+
+ // Now check what sheet looks like after move
- // Row 1 => Row 11
- // So strings on row 11 unchanged, but reference in formula is
- assertEquals("cell B1 (ref)", sheet.getRow(0).getCell(3).getRichStringCellValue().toString());
- assertEquals("CONCATENATE(B1,\" (ref)\")", sheet.getRow(0).getCell(3).getCellFormula());
+ // no changes on row "1"
+ confirmRow(sheet, 0, 1, 171, 1, "ROW(D1)", "100+B1", "COUNT(D1:E1)");
+
+ // row "2" is now empty
assertEquals(0, sheet.getRow(1).getPhysicalNumberOfCells());
- // still save b2
- assertEquals("cell B2 (ref)", sheet.getRow(11).getCell(3).getRichStringCellValue().toString());
- // but points to b12
- assertEquals("CONCATENATE(B12,\" (ref)\")", sheet.getRow(11).getCell(3).getCellFormula());
+ // Row "2" moved to row "12", and the formula has been updated.
+ // note however that the cached formula result (2) has not been updated. (POI differs from Excel here)
+ confirmRow(sheet, 11, 2, 172, 1, "ROW(D12)", "100+B12", "COUNT(D12:E12)");
+
+ // no changes on row "3"
+ confirmRow(sheet, 2, 3, 173, 1, "ROW(D3)", "100+B3", "COUNT(D3:E3)");
+
+
+ confirmCell(sheet, 14, 0, 0.0, "#REF!");
+
+
+ // Formulas on rows that weren't shifted:
+ confirmCell(sheet, 6, 1, 271, "200+B1");
+ confirmCell(sheet, 7, 1, 272, "200+B12"); // this one moved
+ confirmCell(sheet, 8, 1, 273, "200+B3");
+
+ // check formulas on other sheets
+ HSSFSheet sheet2 = wb.getSheet("Sheet2");
+ confirmCell(sheet2, 0, 0, 371, "300+Sheet1!B1");
+ confirmCell(sheet2, 1, 0, 372, "300+Sheet1!B12");
+ confirmCell(sheet2, 2, 0, 373, "300+Sheet1!B3");
+
+ confirmCell(sheet2, 11, 0, 300, "300+Sheet1!#REF!");
+
+
+ // Note - named ranges formulas have not been updated
+ }
- assertEquals("cell B3 (ref)", sheet.getRow(2).getCell(3).getRichStringCellValue().toString());
- assertEquals("CONCATENATE(B3,\" (ref)\")", sheet.getRow(2).getCell(3).getCellFormula());
+ private static void confirmRow(HSSFSheet sheet, int rowIx, double valA, double valB, double valC,
+ String formulaA, String formulaB, String formulaC) {
+ confirmCell(sheet, rowIx, 4, valA, formulaA);
+ confirmCell(sheet, rowIx, 5, valB, formulaB);
+ confirmCell(sheet, rowIx, 6, valC, formulaC);
+ }
- // one on a non-shifted row also updated
- assertEquals("cell B2 (ref)", sheet.getRow(6).getCell(1).getRichStringCellValue().toString());
- assertEquals("CONCATENATE(B12,\" (ref)\")", sheet.getRow(6).getCell(1).getCellFormula());
+ private static void confirmCell(HSSFSheet sheet, int rowIx, int colIx,
+ double expectedValue, String expectedFormula) {
+ HSSFCell cell = sheet.getRow(rowIx).getCell(colIx);
+ assertEquals(expectedValue, cell.getNumericCellValue(), 0.0);
+ assertEquals(expectedFormula, cell.getCellFormula());
}
}
-