2003-05-07 20:02:03 -04:00
|
|
|
/* ====================================================================
|
2006-12-22 14:18:16 -05:00
|
|
|
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
|
2004-04-09 09:05:39 -04:00
|
|
|
|
|
|
|
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.
|
|
|
|
==================================================================== */
|
2008-03-07 07:06:18 -05:00
|
|
|
|
2003-05-07 20:02:03 -04:00
|
|
|
package org.apache.poi.hssf.record;
|
|
|
|
|
2006-07-25 10:30:34 -04:00
|
|
|
import java.util.Stack;
|
|
|
|
|
|
|
|
import org.apache.poi.hssf.record.formula.*;
|
2003-05-07 20:02:03 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Title: SharedFormulaRecord
|
|
|
|
* Description: Primarily used as an excel optimization so that multiple similar formulas
|
|
|
|
* are not written out too many times. We should recognize this record and
|
|
|
|
* serialize as is since this is used when reading templates.
|
|
|
|
* <p>
|
|
|
|
* Note: the documentation says that the SID is BC where biffviewer reports 4BC. The hex dump shows
|
|
|
|
* that the two byte sid representation to be 'BC 04' that is consistent with the other high byte
|
|
|
|
* record types.
|
|
|
|
* @author Danny Mui at apache dot org
|
|
|
|
*/
|
2008-03-07 07:06:18 -05:00
|
|
|
public final class SharedFormulaRecord extends Record {
|
2008-05-05 10:20:39 -04:00
|
|
|
public final static short sid = 0x4BC;
|
2006-07-25 10:30:34 -04:00
|
|
|
|
|
|
|
private int field_1_first_row;
|
|
|
|
private int field_2_last_row;
|
|
|
|
private short field_3_first_column;
|
|
|
|
private short field_4_last_column;
|
|
|
|
private int field_5_reserved;
|
|
|
|
private short field_6_expression_len;
|
|
|
|
private Stack field_7_parsed_expr;
|
2003-05-07 20:02:03 -04:00
|
|
|
|
|
|
|
public SharedFormulaRecord()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2007-06-29 10:48:00 -04:00
|
|
|
* @param in the RecordInputstream to read the record from
|
2003-05-07 20:02:03 -04:00
|
|
|
*/
|
|
|
|
|
2005-08-18 03:06:44 -04:00
|
|
|
public SharedFormulaRecord(RecordInputStream in)
|
2003-05-07 20:02:03 -04:00
|
|
|
{
|
2008-03-07 07:06:18 -05:00
|
|
|
super(in);
|
2003-05-07 20:02:03 -04:00
|
|
|
}
|
2006-07-25 10:30:34 -04:00
|
|
|
|
|
|
|
protected void validateSid(short id)
|
|
|
|
{
|
2008-03-07 07:06:18 -05:00
|
|
|
if (id != this.sid)
|
|
|
|
{
|
|
|
|
throw new RecordFormatException("Not a valid SharedFormula");
|
|
|
|
}
|
2006-07-25 10:30:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
public int getFirstRow() {
|
|
|
|
return field_1_first_row;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getLastRow() {
|
|
|
|
return field_2_last_row;
|
|
|
|
}
|
|
|
|
|
|
|
|
public short getFirstColumn() {
|
|
|
|
return field_3_first_column;
|
|
|
|
}
|
|
|
|
|
|
|
|
public short getLastColumn() {
|
|
|
|
return field_4_last_column;
|
|
|
|
}
|
|
|
|
|
|
|
|
public short getExpressionLength()
|
|
|
|
{
|
|
|
|
return field_6_expression_len;
|
|
|
|
}
|
2003-05-07 20:02:03 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* spit the record out AS IS. no interperatation or identification
|
|
|
|
*/
|
|
|
|
|
|
|
|
public int serialize(int offset, byte [] data)
|
|
|
|
{
|
2008-03-07 07:06:18 -05:00
|
|
|
//Because this record is converted to individual Formula records, this method is not required.
|
|
|
|
throw new UnsupportedOperationException("Cannot serialize a SharedFormulaRecord");
|
2003-05-07 20:02:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
public int getRecordSize()
|
|
|
|
{
|
2008-03-07 07:06:18 -05:00
|
|
|
//Because this record is converted to individual Formula records, this method is not required.
|
|
|
|
throw new UnsupportedOperationException("Cannot get the size for a SharedFormulaRecord");
|
2003-05-07 20:02:03 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* print a sort of string representation ([SHARED FORMULA RECORD] id = x [/SHARED FORMULA RECORD])
|
|
|
|
*/
|
|
|
|
|
|
|
|
public String toString()
|
|
|
|
{
|
|
|
|
StringBuffer buffer = new StringBuffer();
|
|
|
|
|
|
|
|
buffer.append("[SHARED FORMULA RECORD:" + Integer.toHexString(sid) + "]\n");
|
|
|
|
buffer.append(" .id = ").append(Integer.toHexString(sid))
|
|
|
|
.append("\n");
|
2006-07-25 10:30:34 -04:00
|
|
|
buffer.append(" .first_row = ")
|
|
|
|
.append(Integer.toHexString(getFirstRow())).append("\n");
|
|
|
|
buffer.append(" .last_row = ")
|
|
|
|
.append(Integer.toHexString(getLastRow()))
|
|
|
|
.append("\n");
|
|
|
|
buffer.append(" .first_column = ")
|
|
|
|
.append(Integer.toHexString(getFirstColumn())).append("\n");
|
|
|
|
buffer.append(" .last_column = ")
|
|
|
|
.append(Integer.toHexString(getLastColumn()))
|
|
|
|
.append("\n");
|
|
|
|
buffer.append(" .reserved = ")
|
|
|
|
.append(Integer.toHexString(field_5_reserved))
|
|
|
|
.append("\n");
|
|
|
|
buffer.append(" .expressionlength= ").append(getExpressionLength())
|
|
|
|
.append("\n");
|
|
|
|
|
|
|
|
buffer.append(" .numptgsinarray = ").append(field_7_parsed_expr.size())
|
|
|
|
.append("\n");
|
|
|
|
|
|
|
|
for (int k = 0; k < field_7_parsed_expr.size(); k++ ) {
|
|
|
|
buffer.append("Formula ")
|
|
|
|
.append(k)
|
|
|
|
.append("\n")
|
|
|
|
.append(field_7_parsed_expr.get(k).toString())
|
|
|
|
.append("\n");
|
|
|
|
}
|
|
|
|
|
2003-05-07 20:02:03 -04:00
|
|
|
buffer.append("[/SHARED FORMULA RECORD]\n");
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public short getSid()
|
|
|
|
{
|
2006-01-03 02:24:01 -05:00
|
|
|
return sid;
|
2003-05-07 20:02:03 -04:00
|
|
|
}
|
|
|
|
|
2005-08-18 03:06:44 -04:00
|
|
|
protected void fillFields(RecordInputStream in)
|
2003-05-07 20:02:03 -04:00
|
|
|
{
|
2008-01-22 09:48:51 -05:00
|
|
|
field_1_first_row = in.readUShort();
|
|
|
|
field_2_last_row = in.readUShort();
|
2008-01-08 18:01:12 -05:00
|
|
|
field_3_first_column = in.readUByte();
|
|
|
|
field_4_last_column = in.readUByte();
|
2006-07-25 10:30:34 -04:00
|
|
|
field_5_reserved = in.readShort();
|
|
|
|
field_6_expression_len = in.readShort();
|
|
|
|
field_7_parsed_expr = getParsedExpressionTokens(in);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Stack getParsedExpressionTokens(RecordInputStream in)
|
|
|
|
{
|
|
|
|
Stack stack = new Stack();
|
|
|
|
|
|
|
|
while (in.remaining() != 0) {
|
|
|
|
Ptg ptg = Ptg.createPtg(in);
|
|
|
|
stack.push(ptg);
|
|
|
|
}
|
|
|
|
return stack;
|
|
|
|
}
|
|
|
|
|
2008-01-08 18:01:12 -05:00
|
|
|
/**
|
|
|
|
* Are we shared by the supplied formula record?
|
|
|
|
*/
|
2006-07-25 10:30:34 -04:00
|
|
|
public boolean isFormulaInShared(FormulaRecord formula) {
|
|
|
|
final int formulaRow = formula.getRow();
|
|
|
|
final int formulaColumn = formula.getColumn();
|
|
|
|
return ((getFirstRow() <= formulaRow) && (getLastRow() >= formulaRow) &&
|
|
|
|
(getFirstColumn() <= formulaColumn) && (getLastColumn() >= formulaColumn));
|
|
|
|
}
|
2008-03-19 08:28:56 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a non shared formula from the shared formula
|
|
|
|
* counter part
|
|
|
|
*/
|
|
|
|
protected static Stack convertSharedFormulas(Stack ptgs, int formulaRow, int formulaColumn) {
|
2008-05-05 10:20:39 -04:00
|
|
|
if(false) {
|
|
|
|
/*
|
|
|
|
* TODO - (May-2008) Stop converting relative ref Ptgs in shared formula records.
|
|
|
|
* If/when POI writes out the workbook, this conversion makes an unnecessary diff in the BIFF records.
|
|
|
|
* Disabling this code breaks one existing junit.
|
|
|
|
* Some fix-up will be required to make Ptg.toFormulaString(HSSFWorkbook) work properly.
|
|
|
|
* That method will need 2 extra params: rowIx and colIx.
|
|
|
|
*/
|
|
|
|
return ptgs;
|
|
|
|
}
|
2006-07-25 10:30:34 -04:00
|
|
|
Stack newPtgStack = new Stack();
|
|
|
|
|
2008-03-19 08:28:56 -04:00
|
|
|
if (ptgs != null)
|
|
|
|
for (int k = 0; k < ptgs.size(); k++) {
|
|
|
|
Ptg ptg = (Ptg) ptgs.get(k);
|
2006-07-27 10:15:11 -04:00
|
|
|
if (ptg instanceof RefNPtg) {
|
|
|
|
RefNPtg refNPtg = (RefNPtg)ptg;
|
2008-01-22 09:48:51 -05:00
|
|
|
ptg = new ReferencePtg(fixupRelativeRow(formulaRow,refNPtg.getRow(),refNPtg.isRowRelative()),
|
|
|
|
fixupRelativeColumn(formulaColumn,refNPtg.getColumn(),refNPtg.isColRelative()),
|
2006-07-27 10:15:11 -04:00
|
|
|
refNPtg.isRowRelative(),
|
|
|
|
refNPtg.isColRelative());
|
|
|
|
} else if (ptg instanceof RefNVPtg) {
|
|
|
|
RefNVPtg refNVPtg = (RefNVPtg)ptg;
|
2008-01-22 09:48:51 -05:00
|
|
|
ptg = new RefVPtg(fixupRelativeRow(formulaRow,refNVPtg.getRow(),refNVPtg.isRowRelative()),
|
|
|
|
fixupRelativeColumn(formulaColumn,refNVPtg.getColumn(),refNVPtg.isColRelative()),
|
|
|
|
refNVPtg.isRowRelative(),
|
|
|
|
refNVPtg.isColRelative());
|
2006-07-27 10:15:11 -04:00
|
|
|
} else if (ptg instanceof RefNAPtg) {
|
|
|
|
RefNAPtg refNAPtg = (RefNAPtg)ptg;
|
2008-01-22 09:48:51 -05:00
|
|
|
ptg = new RefAPtg( fixupRelativeRow(formulaRow,refNAPtg.getRow(),refNAPtg.isRowRelative()),
|
|
|
|
fixupRelativeColumn(formulaColumn,refNAPtg.getColumn(),refNAPtg.isColRelative()),
|
2006-07-27 10:15:11 -04:00
|
|
|
refNAPtg.isRowRelative(),
|
|
|
|
refNAPtg.isColRelative());
|
|
|
|
} else if (ptg instanceof AreaNPtg) {
|
|
|
|
AreaNPtg areaNPtg = (AreaNPtg)ptg;
|
2008-01-22 09:48:51 -05:00
|
|
|
ptg = new AreaPtg(fixupRelativeRow(formulaRow,areaNPtg.getFirstRow(),areaNPtg.isFirstRowRelative()),
|
|
|
|
fixupRelativeRow(formulaRow,areaNPtg.getLastRow(),areaNPtg.isLastRowRelative()),
|
|
|
|
fixupRelativeColumn(formulaColumn,areaNPtg.getFirstColumn(),areaNPtg.isFirstColRelative()),
|
|
|
|
fixupRelativeColumn(formulaColumn,areaNPtg.getLastColumn(),areaNPtg.isLastColRelative()),
|
2006-07-27 10:15:11 -04:00
|
|
|
areaNPtg.isFirstRowRelative(),
|
|
|
|
areaNPtg.isLastRowRelative(),
|
|
|
|
areaNPtg.isFirstColRelative(),
|
|
|
|
areaNPtg.isLastColRelative());
|
|
|
|
} else if (ptg instanceof AreaNVPtg) {
|
|
|
|
AreaNVPtg areaNVPtg = (AreaNVPtg)ptg;
|
2008-01-22 09:48:51 -05:00
|
|
|
ptg = new AreaVPtg(fixupRelativeRow(formulaRow,areaNVPtg.getFirstRow(),areaNVPtg.isFirstRowRelative()),
|
|
|
|
fixupRelativeRow(formulaRow,areaNVPtg.getLastRow(),areaNVPtg.isLastRowRelative()),
|
|
|
|
fixupRelativeColumn(formulaColumn,areaNVPtg.getFirstColumn(),areaNVPtg.isFirstColRelative()),
|
|
|
|
fixupRelativeColumn(formulaColumn,areaNVPtg.getLastColumn(),areaNVPtg.isLastColRelative()),
|
2006-07-27 10:15:11 -04:00
|
|
|
areaNVPtg.isFirstRowRelative(),
|
|
|
|
areaNVPtg.isLastRowRelative(),
|
|
|
|
areaNVPtg.isFirstColRelative(),
|
|
|
|
areaNVPtg.isLastColRelative());
|
|
|
|
} else if (ptg instanceof AreaNAPtg) {
|
|
|
|
AreaNAPtg areaNAPtg = (AreaNAPtg)ptg;
|
2008-01-22 09:48:51 -05:00
|
|
|
ptg = new AreaAPtg(fixupRelativeRow(formulaRow,areaNAPtg.getFirstRow(),areaNAPtg.isFirstRowRelative()),
|
|
|
|
fixupRelativeRow(formulaRow,areaNAPtg.getLastRow(),areaNAPtg.isLastRowRelative()),
|
|
|
|
fixupRelativeColumn(formulaColumn,areaNAPtg.getFirstColumn(),areaNAPtg.isFirstColRelative()),
|
|
|
|
fixupRelativeColumn(formulaColumn,areaNAPtg.getLastColumn(),areaNAPtg.isLastColRelative()),
|
2006-07-27 10:15:11 -04:00
|
|
|
areaNAPtg.isFirstRowRelative(),
|
|
|
|
areaNAPtg.isLastRowRelative(),
|
|
|
|
areaNAPtg.isFirstColRelative(),
|
|
|
|
areaNAPtg.isLastColRelative());
|
|
|
|
}
|
|
|
|
newPtgStack.add(ptg);
|
2008-03-19 08:28:56 -04:00
|
|
|
}
|
|
|
|
return newPtgStack;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a non shared formula from the shared formula
|
|
|
|
* counter part
|
|
|
|
*/
|
|
|
|
public void convertSharedFormulaRecord(FormulaRecord formula) {
|
|
|
|
//Sanity checks
|
|
|
|
final int formulaRow = formula.getRow();
|
|
|
|
final int formulaColumn = formula.getColumn();
|
|
|
|
if (isFormulaInShared(formula)) {
|
|
|
|
formula.setExpressionLength(getExpressionLength());
|
|
|
|
|
|
|
|
Stack newPtgStack =
|
|
|
|
convertSharedFormulas(field_7_parsed_expr, formulaRow, formulaColumn);
|
2006-07-25 10:30:34 -04:00
|
|
|
formula.setParsedExpression(newPtgStack);
|
2006-08-26 04:52:24 -04:00
|
|
|
//Now its not shared!
|
|
|
|
formula.setSharedFormula(false);
|
2006-07-25 10:30:34 -04:00
|
|
|
} else {
|
|
|
|
throw new RuntimeException("Shared Formula Conversion: Coding Error");
|
|
|
|
}
|
2003-05-07 20:02:03 -04:00
|
|
|
}
|
2008-05-05 10:20:39 -04:00
|
|
|
|
2008-03-19 08:28:56 -04:00
|
|
|
private static int fixupRelativeColumn(int currentcolumn, int column, boolean relative) {
|
2008-03-07 07:06:18 -05:00
|
|
|
if(relative) {
|
|
|
|
// mask out upper bits to produce 'wrapping' at column 256 ("IV")
|
|
|
|
return (column + currentcolumn) & 0x00FF;
|
|
|
|
}
|
|
|
|
return column;
|
|
|
|
}
|
|
|
|
|
2008-03-19 08:28:56 -04:00
|
|
|
private static int fixupRelativeRow(int currentrow, int row, boolean relative) {
|
2008-03-07 07:06:18 -05:00
|
|
|
if(relative) {
|
|
|
|
// mask out upper bits to produce 'wrapping' at row 65536
|
|
|
|
return (row+currentrow) & 0x00FFFF;
|
|
|
|
}
|
|
|
|
return row;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mirroring formula records so it is registered in the ValueRecordsAggregate
|
|
|
|
*/
|
|
|
|
public boolean isInValueSection()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register it in the ValueRecordsAggregate so it can go into the FormulaRecordAggregate
|
|
|
|
*/
|
|
|
|
public boolean isValue() {
|
|
|
|
return true;
|
|
|
|
}
|
2003-05-07 20:02:03 -04:00
|
|
|
|
|
|
|
public Object clone() {
|
2008-03-07 07:06:18 -05:00
|
|
|
//Because this record is converted to individual Formula records, this method is not required.
|
|
|
|
throw new UnsupportedOperationException("Cannot clone a SharedFormulaRecord");
|
2003-05-07 20:02:03 -04:00
|
|
|
}
|
|
|
|
}
|