jxl.write.biff.FormulaRecord Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jxl Show documentation
Show all versions of jxl Show documentation
JExcelApi is a java library which provides the ability to read, write, and modify Microsoft Excel spreadsheets.
The newest version!
/*********************************************************************
*
* 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();
}
}