/*********************************************************************
*
* Copyright (C) 2002 Andrew Khan
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
***************************************************************************/
package jxl.write.biff;
import jxl.common.Assert;
import jxl.common.Logger;
import jxl.CellReferenceHelper;
import jxl.CellType;
import jxl.Sheet;
import jxl.WorkbookSettings;
import jxl.biff.FormattingRecords;
import jxl.biff.FormulaData;
import jxl.biff.IntegerHelper;
import jxl.biff.Type;
import jxl.biff.WorkbookMethods;
import jxl.biff.formula.ExternalSheet;
import jxl.biff.formula.FormulaException;
import jxl.biff.formula.FormulaParser;
import jxl.format.CellFormat;
import jxl.write.WritableCell;
/**
* A formula record. Parses the string passed in to deduce the set of
* formula records
*/
public class FormulaRecord extends CellValue implements FormulaData
{
/**
* The logger
*/
private static Logger logger = Logger.getLogger(FormulaRecord.class);
/**
* The formula to parse
*/
private String formulaToParse;
/**
* The formula parser
*/
private FormulaParser parser;
/**
* The parsed formula string
*/
private String formulaString;
/**
* The parsed formula bytes
*/
private byte[] formulaBytes;
/**
* The location where this formula was copied from. It is used subsequently
* to adjust relative cell references
*/
private CellValue copiedFrom;
/**
* Constructor
*
* @param f the formula to copy
*/
public FormulaRecord(int c, int r, String f)
{
super(Type.FORMULA2, c, r);
formulaToParse = f;
copiedFrom = null;
}
/**
* Constructor
*
* @param f the formula to copy
*/
public FormulaRecord(int c, int r, String f, CellFormat st)
{
super(Type.FORMULA, c, r, st);
formulaToParse = f;
copiedFrom = null;
}
/**
* Copy constructor for writable formulas
*
* @param c the column
* @param r the row
* @param fr the record to copy
*/
protected FormulaRecord(int c, int r, FormulaRecord fr)
{
super(Type.FORMULA, c, r, fr);
copiedFrom = fr;
formulaBytes = new byte[fr.formulaBytes.length];
System.arraycopy(fr.formulaBytes, 0, formulaBytes, 0, formulaBytes.length);
}
/**
* Copy constructor for formulas read in - invoked from writable formulas
*
* @param c the column
* @param r the row
* @param rfr the formula data to copy
*/
protected FormulaRecord(int c, int r, ReadFormulaRecord rfr)
{
super(Type.FORMULA, c, r, rfr);
try
{
copiedFrom = rfr;
formulaBytes = rfr.getFormulaBytes();
}
catch (FormulaException e)
{
// Fail silently
logger.error("", e);
}
}
/**
* Initializes the string and the formula bytes. In order to get
* access to the workbook settings, the object is not initialized until
* it is added to the sheet
*
* @param ws the workbook settings
* @param es the external sheet
* @param nt the name table
*/
private void initialize(WorkbookSettings ws, ExternalSheet es,
WorkbookMethods nt)
{
if (copiedFrom != null)
{
initializeCopiedFormula(ws, es, nt);
return;
}
parser = new FormulaParser(formulaToParse, es, nt, ws);
try
{
parser.parse();
formulaString = parser.getFormula();
formulaBytes = parser.getBytes();
}
catch (FormulaException e)
{
logger.warn
(e.getMessage() +
" when parsing formula " + formulaToParse + " in cell " +
getSheet().getName() + "!" +
CellReferenceHelper.getCellReference(getColumn(), getRow()));
try
{
// try again, with an error formula
formulaToParse = "ERROR(1)";
parser = new FormulaParser(formulaToParse, es, nt, ws);
parser.parse();
formulaString = parser.getFormula();
formulaBytes = parser.getBytes();
}
catch (FormulaException e2)
{
// fail silently
logger.error("",e2);
}
}
}
/**
* This formula was copied from a formula already present in the writable
* workbook. Requires special handling to sort out the cell references
* @param ws the workbook settings
* @param es the external sheet
* @param nt the name table
*/
private void initializeCopiedFormula(WorkbookSettings ws,
ExternalSheet es, WorkbookMethods nt)
{
try
{
parser = new FormulaParser(formulaBytes, this, es, nt, ws);
parser.parse();
parser.adjustRelativeCellReferences
(getColumn() - copiedFrom.getColumn(),
getRow() - copiedFrom.getRow());
formulaString = parser.getFormula();
formulaBytes = parser.getBytes();
}
catch (FormulaException e)
{
try
{
// try again, with an error formula
formulaToParse = "ERROR(1)";
parser = new FormulaParser(formulaToParse, es, nt, ws);
parser.parse();
formulaString = parser.getFormula();
formulaBytes = parser.getBytes();
}
catch (FormulaException e2)
{
// fail silently
logger.error("", e2);
}
}
}
/**
* Called when the cell is added to the worksheet. Overrides the
* method in the base class in order to get a handle to the
* WorkbookSettings so that this formula may be initialized
*
* @param fr the formatting records
* @param ss the shared strings used within the workbook
* @param s the sheet this is being added to
*/
void setCellDetails(FormattingRecords fr, SharedStrings ss,
WritableSheetImpl s)
{
super.setCellDetails(fr, ss, s);
initialize(s.getWorkbookSettings(), s.getWorkbook(), s.getWorkbook());
s.getWorkbook().addRCIRCell(this);
}
/**
* Gets the binary data for output to file
*
* @return the binary data
*/
public byte[] getData()
{
byte[] celldata = super.getData();
byte[] formulaData = getFormulaData();
byte[] data = new byte[formulaData.length + celldata.length];
System.arraycopy(celldata, 0, data, 0, celldata.length);
System.arraycopy(formulaData, 0, data, celldata.length,
formulaData.length);
return data;
}
/**
* Returns the content type of this cell
*
* @return the content type for this cell
*/
public CellType getType()
{
return CellType.ERROR;
}
/**
* Quick and dirty function to return the contents of this cell as a string.
* For more complex manipulation of the contents, it is necessary to cast
* this interface to correct subinterface
*
* @return the contents of this cell as a string
*/
public String getContents()
{
return formulaString;
}
/**
* Gets the raw bytes for the formula. This will include the
* parsed tokens array
*
* @return the raw record data
*/
public byte[] getFormulaData()
{
byte[] data = new byte[formulaBytes.length + 16];
System.arraycopy(formulaBytes, 0, data, 16, formulaBytes.length);
data[6] = (byte) 0x10;
data[7] = (byte) 0x40;
data[12] = (byte) 0xe0;
data[13] = (byte) 0xfc;
// Set the recalculate on load bit
data[8] |= 0x02;
// Set the length of the rpn array
IntegerHelper.getTwoBytes(formulaBytes.length, data, 14);
return data;
}
/**
* A dummy implementation to keep the compiler quiet. This object needs
* to be instantiated from ReadFormulaRecord
*
* @param col the column which the new cell will occupy
* @param row the row which the new cell will occupy
* @return NOTHING
*/
public WritableCell copyTo(int col, int row)
{
Assert.verify(false);
return null;
}
/**
* Called when a column is inserted on the specified sheet. Notifies all
* RCIR cells of this change. The default implementation here does nothing
*
* @param s the sheet on which the column was inserted
* @param sheetIndex the sheet index on which the column was inserted
* @param col the column number which was inserted
*/
void columnInserted(Sheet s, int sheetIndex, int col)
{
parser.columnInserted(sheetIndex, col, s == getSheet());
formulaBytes = parser.getBytes();
}
/**
* Called when a column is removed on the specified sheet. Notifies all
* RCIR cells of this change. The default implementation here does nothing
*
* @param s the sheet on which the column was inserted
* @param sheetIndex the sheet index on which the column was inserted
* @param col the column number which was inserted
*/
void columnRemoved(Sheet s, int sheetIndex, int col)
{
parser.columnRemoved(sheetIndex, col, s == getSheet());
formulaBytes = parser.getBytes();
}
/**
* Called when a row is inserted on the specified sheet. Notifies all
* RCIR cells of this change. The default implementation here does nothing
*
* @param s the sheet on which the column was inserted
* @param sheetIndex the sheet index on which the column was inserted
* @param row the column number which was inserted
*/
void rowInserted(Sheet s, int sheetIndex, int row)
{
parser.rowInserted(sheetIndex, row, s == getSheet());
formulaBytes = parser.getBytes();
}
/**
* Called when a row is inserted on the specified sheet. Notifies all
* RCIR cells of this change. The default implementation here does nothing
*
* @param s the sheet on which the row was removed
* @param sheetIndex the sheet index on which the column was removed
* @param row the column number which was removed
*/
void rowRemoved(Sheet s, int sheetIndex, int row)
{
parser.rowRemoved(sheetIndex, row, s == getSheet());
formulaBytes = parser.getBytes();
}
}
|