/*************************************************************************
*
* 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.FormulaCell;
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.write.WritableCell;
/**
* A formula record. This is invoked when copying a formula from a
* read only spreadsheet
* This method implements the FormulaData interface to allow the copying
* of writable sheets
*/
class ReadFormulaRecord extends CellValue implements FormulaData
{
/**
* The logger
*/
private static Logger logger = Logger.getLogger(ReadFormulaRecord.class);
/**
* The underlying formula from the read sheet
*/
private FormulaData formula;
/**
* The formula parser
*/
private FormulaParser parser;
/**
* Constructor
*
* @param f the formula to copy
*/
protected ReadFormulaRecord(FormulaData f)
{
super(Type.FORMULA, f);
formula = f;
}
protected final byte[] getCellData()
{
return super.getData();
}
/**
* An exception has occurred, so produce some appropriate dummy
* cell contents. This may be overridden by subclasses
* if they require specific handling
*
* @return the bodged data
*/
protected byte[] handleFormulaException()
{
byte[] expressiondata = null;
byte[] celldata = super.getData();
// Generate an appropriate dummy formula
WritableWorkbookImpl w = getSheet().getWorkbook();
parser = new FormulaParser(getContents(), w, w,
w.getSettings());
// Get the bytes for the dummy formula
try
{
parser.parse();
}
catch(FormulaException e2)
{
logger.warn(e2.getMessage());
parser = new FormulaParser("\"ERROR\"", w, w, w.getSettings());
try {parser.parse();}
catch(FormulaException e3) {Assert.verify(false);}
}
byte[] formulaBytes = parser.getBytes();
expressiondata = new byte[formulaBytes.length + 16];
IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14);
System.arraycopy(formulaBytes, 0, expressiondata, 16,
formulaBytes.length);
// Set the recalculate on load bit
expressiondata[8] |= 0x02;
byte[] data = new byte[celldata.length +
expressiondata.length];
System.arraycopy(celldata, 0, data, 0, celldata.length);
System.arraycopy(expressiondata, 0, data,
celldata.length, expressiondata.length);
return data;
}
/**
* Gets the binary data for output to file
*
* @return the binary data
*/
public byte[] getData()
{
// Take the superclass cell data to take into account cell
// rationalization
byte[] celldata = super.getData();
byte[] expressiondata = null;
try
{
if (parser == null)
{
expressiondata = formula.getFormulaData();
}
else
{
byte[] formulaBytes = parser.getBytes();
expressiondata = new byte[formulaBytes.length + 16];
IntegerHelper.getTwoBytes(formulaBytes.length, expressiondata, 14);
System.arraycopy(formulaBytes, 0, expressiondata, 16,
formulaBytes.length);
}
// Set the recalculate on load bit
expressiondata[8] |= 0x02;
byte[] data = new byte[celldata.length +
expressiondata.length];
System.arraycopy(celldata, 0, data, 0, celldata.length);
System.arraycopy(expressiondata, 0, data,
celldata.length, expressiondata.length);
return data;
}
catch (FormulaException e)
{
// Something has gone wrong trying to read the formula data eg. it
// might be unsupported biff7 data
logger.warn
(CellReferenceHelper.getCellReference(getColumn(), getRow()) +
" " + e.getMessage());
return handleFormulaException();
}
}
/**
* Returns the content type of this cell
*
* @return the content type for this cell
*/
public CellType getType()
{
return formula.getType();
}
/**
* Quick and dirty function to return the contents of this cell as a string.
*
* @return the contents of this cell as a string
*/
public String getContents()
{
return formula.getContents();
}
/**
* Gets the raw bytes for the formula. This will include the
* parsed tokens array. Used when copying spreadsheets
*
* @return the raw record data
*/
public byte[] getFormulaData() throws FormulaException
{
byte[] d = formula.getFormulaData();
byte[] data = new byte[d.length];
System.arraycopy(d, 0, data, 0, d.length);
// Set the recalculate on load bit
data[8] |= 0x02;
return data;
}
/**
* Gets the formula bytes
*
* @return the formula bytes
*/
public byte[] getFormulaBytes() throws FormulaException
{
// If the formula has been parsed, then get the parsed bytes
if (parser != null)
{
return parser.getBytes();
}
// otherwise get the bytes from the original formula
byte[] readFormulaData = getFormulaData();
byte[] formulaBytes = new byte[readFormulaData.length - 16];
System.arraycopy(readFormulaData, 16, formulaBytes, 0,
formulaBytes.length);
return formulaBytes;
}
/**
* Implementation of the deep copy function
*
* @param col the column which the new cell will occupy
* @param row the row which the new cell will occupy
* @return a copy of this cell, which can then be added to the sheet
*/
public WritableCell copyTo(int col, int row)
{
return new FormulaRecord(col, row, this);
}
/**
* Overrides the method in the base class to add this to the Workbook's
* list of maintained formulas
*
* @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);
s.getWorkbook().addRCIRCell(this);
}
/**
* 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)
{
try
{
if (parser == null)
{
byte[] formulaData = formula.getFormulaData();
byte[] formulaBytes = new byte[formulaData.length - 16];
System.arraycopy(formulaData, 16,
formulaBytes, 0, formulaBytes.length);
parser = new FormulaParser(formulaBytes,
this,
getSheet().getWorkbook(),
getSheet().getWorkbook(),
getSheet().getWorkbookSettings());
parser.parse();
}
parser.columnInserted(sheetIndex, col, s == getSheet());
}
catch (FormulaException e)
{
logger.warn("cannot insert column within formula: " + e.getMessage());
}
}
/**
* 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)
{
try
{
if (parser == null)
{
byte[] formulaData = formula.getFormulaData();
byte[] formulaBytes = new byte[formulaData.length - 16];
System.arraycopy(formulaData, 16,
formulaBytes, 0, formulaBytes.length);
parser = new FormulaParser(formulaBytes,
this,
getSheet().getWorkbook(),
getSheet().getWorkbook(),
getSheet().getWorkbookSettings());
parser.parse();
}
parser.columnRemoved(sheetIndex, col, s == getSheet());
}
catch (FormulaException e)
{
logger.warn("cannot remove column within formula: " + e.getMessage());
}
}
/**
* 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)
{
try
{
if (parser == null)
{
byte[] formulaData = formula.getFormulaData();
byte[] formulaBytes = new byte[formulaData.length - 16];
System.arraycopy(formulaData, 16,
formulaBytes, 0, formulaBytes.length);
parser = new FormulaParser(formulaBytes,
this,
getSheet().getWorkbook(),
getSheet().getWorkbook(),
getSheet().getWorkbookSettings());
parser.parse();
}
parser.rowInserted(sheetIndex, row, s == getSheet());
}
catch (FormulaException e)
{
logger.warn("cannot insert row within formula: " + e.getMessage());
}
}
/**
* 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)
{
try
{
if (parser == null)
{
byte[] formulaData = formula.getFormulaData();
byte[] formulaBytes = new byte[formulaData.length - 16];
System.arraycopy(formulaData, 16,
formulaBytes, 0, formulaBytes.length);
parser = new FormulaParser(formulaBytes,
this,
getSheet().getWorkbook(),
getSheet().getWorkbook(),
getSheet().getWorkbookSettings());
parser.parse();
}
parser.rowRemoved(sheetIndex, row, s == getSheet());
}
catch (FormulaException e)
{
logger.warn("cannot remove row within formula: " + e.getMessage());
}
}
/**
* Accessor for the read formula
*
* @return the read formula
*/
protected FormulaData getReadFormula()
{
return formula;
}
/**
* Accessor for the read formula
*
* @return the read formula
*/
public String getFormula() throws FormulaException
{
return ( (FormulaCell) formula).getFormula();
}
/**
* If this formula was on an imported sheet, check that
* cell references to another sheet are warned appropriately
*
* @return TRUE if this formula was able to be imported, FALSE otherwise
*/
public boolean handleImportedCellReferences(ExternalSheet es,
WorkbookMethods mt,
WorkbookSettings ws)
{
try
{
if (parser == null)
{
byte[] formulaData = formula.getFormulaData();
byte[] formulaBytes = new byte[formulaData.length - 16];
System.arraycopy(formulaData, 16,
formulaBytes, 0, formulaBytes.length);
parser = new FormulaParser(formulaBytes,
this,
es, mt, ws);
parser.parse();
}
return parser.handleImportedCellReferences();
}
catch (FormulaException e)
{
logger.warn("cannot import formula: " + e.getMessage());
return false;
}
}
}
|