merged 696038 from trunk - (Fix for bug 45780 - update area refs during HSSFSheet.shiftRows())

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@696453 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-09-17 21:23:58 +00:00
parent 3ed09846f9
commit fe820eed5c
28 changed files with 789 additions and 394 deletions

View File

@ -67,6 +67,7 @@
<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>

View File

@ -64,6 +64,7 @@
<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>

View File

@ -62,7 +62,6 @@ import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
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;
@ -127,7 +126,8 @@ public final class Sheet implements Model {
/*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;
@ -329,10 +329,13 @@ public final class Sheet implements Model {
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;
@ -441,6 +444,8 @@ public final class Sheet implements Model {
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();
@ -456,14 +461,10 @@ public final class Sheet implements Model {
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;
@ -623,13 +624,6 @@ public final class Sheet implements Model {
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).
@ -714,7 +708,6 @@ public final class Sheet implements Model {
public void addRow(RowRecord row)
{
checkRows();
if (log.check( POILogger.DEBUG ))
log.log(POILogger.DEBUG, "addRow ");
DimensionsRecord d = _dimensions;
@ -748,7 +741,6 @@ public final class Sheet implements Model {
* @param row the row record to remove
*/
public void removeRow(RowRecord row) {
checkRows();
_rowsAggregate.removeRow(row);
}
@ -1295,7 +1287,7 @@ public final class Sheet implements Model {
}
/**
* 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)
@ -1781,13 +1773,12 @@ public final class Sheet implements Model {
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();
@ -1817,17 +1808,6 @@ public final class Sheet implements Model {
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();
@ -1836,8 +1816,4 @@ public final class Sheet implements Model {
}
return _dataValidityTable;
}
public FormulaRecordAggregate createFormula(int row, int col) {
return _rowsAggregate.createFormula(row, col);
}
}

View File

@ -537,9 +537,7 @@ public final class NameRecord extends Record {
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()];

View File

@ -35,6 +35,7 @@ import org.apache.poi.hssf.record.RowRecord;
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;
/**
*
@ -507,4 +508,7 @@ public final class RowRecordsAggregate extends RecordAggregate {
fr.setColumn((short) col);
return new FormulaRecordAggregate(fr, null, _sharedValueManager);
}
public void updateFormulasAfterRowShift(FormulaShifter formulaShifter, int currentExternSheetIndex) {
_valuesAgg.updateFormulasAfterRowShift(formulaShifter, currentExternSheetIndex);
}
}

View File

@ -28,6 +28,8 @@ import org.apache.poi.hssf.record.Record;
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;
/**
*
@ -225,6 +227,25 @@ public final class ValueRecordsAggregate {
}
}
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();

View File

@ -90,24 +90,6 @@ public final class Area3DPtg extends AreaPtgBase {
* 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());
}
}

View File

@ -18,6 +18,7 @@
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;
@ -27,22 +28,30 @@ 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 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:
in.readInt();
in.readInt();
unused1 = in.readInt();
unused2 = 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);
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 "#REF!";
return ErrorConstants.getText(ErrorConstants.ERROR_REF);
}
public byte getDefaultOperandClass() {

View File

@ -35,13 +35,21 @@ public final class DeletedArea3DPtg extends OperandPtg {
private final int unused1;
private final int unused2;
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;

View File

@ -41,8 +41,14 @@ public final class DeletedRef3DPtg extends OperandPtg {
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;

View File

@ -0,0 +1,44 @@
/* ====================================================================
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();
}
}

View File

@ -0,0 +1,294 @@
/* ====================================================================
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() + ")");
}
}

View File

@ -0,0 +1,68 @@
/* ====================================================================
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();
}
}

View File

@ -18,12 +18,8 @@
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;
/**
@ -34,49 +30,36 @@ 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();
}
@ -84,8 +67,7 @@ public final class Ref3DPtg extends OperandPtg {
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() {
@ -100,83 +82,11 @@ public final class Ref3DPtg extends OperandPtg {
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 byte getDefaultOperandClass() {
return Ptg.CLASS_REF;
public String toFormulaString(Workbook book) {
return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet, formatReferenceAsString());
}
}

View File

@ -18,6 +18,7 @@
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;
@ -28,48 +29,32 @@ 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() {

View File

@ -23,11 +23,9 @@ import org.apache.poi.hssf.record.RecordInputStream;
* RefNPtg
* @author Jason Height (jheight at apache dot com)
*/
public final class RefNPtg extends RefPtgBase {
public final class RefNPtg extends Ref2DPtgBase {
public final static byte sid = 0x2C;
/** Creates new ValueReferencePtg */
public RefNPtg(RecordInputStream in) {
super(in);
}

View File

@ -24,7 +24,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
* @author Andrew C. Oliver (acoliver@apache.org)
* @author Jason Height (jheight at chariot dot net dot au)
*/
public final class RefPtg extends RefPtgBase {
public final class RefPtg extends Ref2DPtgBase {
public final static byte sid = 0x24;
/**
@ -36,10 +36,7 @@ public final class RefPtg extends RefPtgBase {
}
public RefPtg(int row, int column, boolean isRowRelative, boolean isColumnRelative) {
setRow(row);
setColumn(column);
setRowRelative(isRowRelative);
setColRelative(isColumnRelative);
super(row, column, isRowRelative, isColumnRelative);
}
public RefPtg(RecordInputStream in) {
@ -49,5 +46,4 @@ public final class RefPtg extends RefPtgBase {
protected byte getSid() {
return sid;
}
}

View File

@ -17,13 +17,11 @@
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)
@ -32,7 +30,6 @@ import org.apache.poi.hssf.record.RecordInputStream;
*/
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 */
@ -70,30 +67,15 @@ public abstract class RefPtgBase extends OperandPtg {
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();
protected final void writeCoordinates(byte[] array, int offset) {
LittleEndian.putUShort(array, offset + 0, field_1_row);
LittleEndian.putUShort(array, offset + 2, field_2_col);
}
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 abstract byte getSid();
public final void setRow(int row) {
if(row < 0 || row >= MAX_ROW_NUMBER) {
throw new IllegalArgumentException("The row number, when specified as an integer, must be between 0 and " + MAX_ROW_NUMBER);
@ -102,18 +84,11 @@ public abstract class RefPtgBase extends OperandPtg {
}
/**
* 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);
@ -141,14 +116,10 @@ public abstract class RefPtgBase extends OperandPtg {
public final int getColumn() {
return column.getValue(field_2_col);
}
public final int getSize() {
return SIZE;
}
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() {

View File

@ -300,7 +300,7 @@ public class HSSFCell implements Cell {
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);
@ -600,12 +600,6 @@ public class HSSFCell implements Cell {
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());

View File

@ -126,8 +126,16 @@ public class HSSFName implements Name {
* @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();

View File

@ -31,7 +31,6 @@ import java.util.List;
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;
@ -43,8 +42,7 @@ import org.apache.poi.hssf.record.SCLRecord;
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;
@ -1180,33 +1178,26 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
* @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
@ -1236,7 +1227,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
// 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 );
@ -1263,49 +1254,21 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
// Update any formulas on this sheet that point to
// rows which have been moved
updateFormulasAfterShift(startRow, endRow, n);
}
int sheetIndex = workbook.getSheetIndex(this);
short externSheetIndex = book.checkExternSheet(sheetIndex);
FormulaShifter shifter = FormulaShifter.createForRowShift(externSheetIndex, startRow, endRow, n);
sheet.getRowsAggregate().updateFormulasAfterRowShift(shifter, externSheetIndex);
/**
* 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 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 )
@ -1652,9 +1615,12 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
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);
}
}
/**

View File

@ -678,10 +678,35 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
* 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
@ -865,15 +890,6 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
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/>
*
@ -1384,20 +1400,6 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
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>

View File

@ -43,6 +43,7 @@ public final class AllFormulaTests {
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);

View File

@ -0,0 +1,115 @@
/* ====================================================================
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);
}
}

View File

@ -1052,25 +1052,25 @@ public final class TestFormulas extends TestCase {
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);

View File

@ -476,7 +476,7 @@ public final class TestNamedRange extends TestCase {
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());

View File

@ -19,6 +19,10 @@ package org.apache.poi.hssf.usermodel;
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;
@ -201,36 +205,69 @@ public final class TestSheetShiftRows extends TestCase {
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ForShifting.xls");
HSSFSheet sheet = wb.getSheet("Sheet1");
assertEquals(19, sheet.getLastRowNum());
assertEquals(20, 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());
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);
// 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());
// Now check what sheet looks like after move
// 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)");
assertEquals("cell B3 (ref)", sheet.getRow(2).getCell(3).getRichStringCellValue().toString());
assertEquals("CONCATENATE(B3,\" (ref)\")", sheet.getRow(2).getCell(3).getCellFormula());
// no changes on row "3"
confirmRow(sheet, 2, 3, 173, 1, "ROW(D3)", "100+B3", "COUNT(D3:E3)");
// 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());
}
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
}
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);
}
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());
}
}