jxl.write.biff.WritableSheetCopier 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) 2006 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 java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeSet;
import java.util.Iterator;
import jxl.common.Assert;
import jxl.common.Logger;
import jxl.BooleanCell;
import jxl.Cell;
import jxl.CellType;
import jxl.CellView;
import jxl.DateCell;
import jxl.HeaderFooter;
import jxl.Hyperlink;
import jxl.Image;
import jxl.LabelCell;
import jxl.NumberCell;
import jxl.Range;
import jxl.Sheet;
import jxl.SheetSettings;
import jxl.WorkbookSettings;
import jxl.biff.AutoFilter;
import jxl.biff.CellReferenceHelper;
import jxl.biff.ConditionalFormat;
import jxl.biff.DataValidation;
import jxl.biff.FormattingRecords;
import jxl.biff.FormulaData;
import jxl.biff.IndexMapping;
import jxl.biff.NumFormatRecordsException;
import jxl.biff.SheetRangeImpl;
import jxl.biff.WorkspaceInformationRecord;
import jxl.biff.XFRecord;
import jxl.biff.drawing.Chart;
import jxl.biff.drawing.ComboBox;
import jxl.biff.drawing.Drawing;
import jxl.biff.drawing.DrawingGroupObject;
import jxl.format.CellFormat;
import jxl.biff.formula.FormulaException;
import jxl.read.biff.SheetImpl;
import jxl.read.biff.NameRecord;
import jxl.read.biff.WorkbookParser;
import jxl.write.Blank;
import jxl.write.Boolean;
import jxl.write.DateTime;
import jxl.write.Formula;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.WritableCell;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableHyperlink;
import jxl.write.WritableImage;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
/**
* A transient utility object used to copy sheets. This
* functionality has been farmed out to a different class
* in order to reduce the bloat of the WritableSheetImpl
*/
class WritableSheetCopier
{
private static Logger logger = Logger.getLogger(SheetCopier.class);
private WritableSheetImpl fromSheet;
private WritableSheetImpl toSheet;
private WorkbookSettings workbookSettings;
// Objects used by the sheet
private TreeSet fromColumnFormats;
private TreeSet toColumnFormats;
private MergedCells fromMergedCells;
private MergedCells toMergedCells;
private RowRecord[] fromRows;
private ArrayList fromRowBreaks;
private ArrayList fromColumnBreaks;
private ArrayList toRowBreaks;
private ArrayList toColumnBreaks;
private DataValidation fromDataValidation;
private DataValidation toDataValidation;
private SheetWriter sheetWriter;
private ArrayList fromDrawings;
private ArrayList toDrawings;
private ArrayList toImages;
private WorkspaceInformationRecord fromWorkspaceOptions;
private PLSRecord fromPLSRecord;
private PLSRecord toPLSRecord;
private ButtonPropertySetRecord fromButtonPropertySet;
private ButtonPropertySetRecord toButtonPropertySet;
private ArrayList fromHyperlinks;
private ArrayList toHyperlinks;
private ArrayList validatedCells;
private int numRows;
private int maxRowOutlineLevel;
private int maxColumnOutlineLevel;
private boolean chartOnly;
private FormattingRecords formatRecords;
// Objects used to maintain state during the copy process
private HashMap xfRecords;
private HashMap fonts;
private HashMap formats;
public WritableSheetCopier(WritableSheet f, WritableSheet t)
{
fromSheet = (WritableSheetImpl) f;
toSheet = (WritableSheetImpl) t;
workbookSettings = toSheet.getWorkbook().getSettings();
chartOnly = false;
}
void setColumnFormats(TreeSet fcf, TreeSet tcf)
{
fromColumnFormats = fcf;
toColumnFormats = tcf;
}
void setMergedCells(MergedCells fmc, MergedCells tmc)
{
fromMergedCells = fmc;
toMergedCells = tmc;
}
void setRows(RowRecord[] r)
{
fromRows = r;
}
void setValidatedCells(ArrayList vc)
{
validatedCells = vc;
}
void setRowBreaks(ArrayList frb, ArrayList trb)
{
fromRowBreaks = frb;
toRowBreaks = trb;
}
void setColumnBreaks(ArrayList fcb, ArrayList tcb)
{
fromColumnBreaks = fcb;
toColumnBreaks = tcb;
}
void setDrawings(ArrayList fd, ArrayList td, ArrayList ti)
{
fromDrawings = fd;
toDrawings = td;
toImages = ti;
}
void setHyperlinks(ArrayList fh, ArrayList th)
{
fromHyperlinks = fh;
toHyperlinks = th;
}
void setWorkspaceOptions(WorkspaceInformationRecord wir)
{
fromWorkspaceOptions = wir;
}
void setDataValidation(DataValidation dv)
{
fromDataValidation = dv;
}
void setPLSRecord(PLSRecord plsr)
{
fromPLSRecord = plsr;
}
void setButtonPropertySetRecord(ButtonPropertySetRecord bpsr)
{
fromButtonPropertySet = bpsr;
}
void setSheetWriter(SheetWriter sw)
{
sheetWriter = sw;
}
DataValidation getDataValidation()
{
return toDataValidation;
}
PLSRecord getPLSRecord()
{
return toPLSRecord;
}
boolean isChartOnly()
{
return chartOnly;
}
ButtonPropertySetRecord getButtonPropertySet()
{
return toButtonPropertySet;
}
/**
* Copies a sheet from a read-only version to the writable version.
* Performs shallow copies
*/
public void copySheet()
{
shallowCopyCells();
// Copy the column formats
Iterator cfit = fromColumnFormats.iterator();
while (cfit.hasNext())
{
ColumnInfoRecord cv = new ColumnInfoRecord
((ColumnInfoRecord) cfit.next());
toColumnFormats.add(cv);
}
// Copy the merged cells
Range[] merged = fromMergedCells.getMergedCells();
for (int i = 0; i < merged.length; i++)
{
toMergedCells.add(new SheetRangeImpl((SheetRangeImpl)merged[i],
toSheet));
}
try
{
RowRecord row = null;
RowRecord newRow = null;
for (int i = 0; i < fromRows.length ; i++)
{
row = fromRows[i];
if (row != null &&
(!row.isDefaultHeight() ||
row.isCollapsed()))
{
newRow = toSheet.getRowRecord(i);
newRow.setRowDetails(row.getRowHeight(),
row.matchesDefaultFontHeight(),
row.isCollapsed(),
row.getOutlineLevel(),
row.getGroupStart(),
row.getStyle());
}
}
}
catch (RowsExceededException e)
{
// Handle the rows exceeded exception - this cannot occur since
// the sheet we are copying from will have a valid number of rows
Assert.verify(false);
}
// Copy the horizontal page breaks
toRowBreaks = new ArrayList(fromRowBreaks);
// Copy the vertical page breaks
toColumnBreaks = new ArrayList(fromColumnBreaks);
// Copy the data validations
if (fromDataValidation != null)
{
toDataValidation = new DataValidation
(fromDataValidation,
toSheet.getWorkbook(),
toSheet.getWorkbook(),
toSheet.getWorkbook().getSettings());
}
// Copy the charts
sheetWriter.setCharts(fromSheet.getCharts());
// Copy the drawings
for (Iterator i = fromDrawings.iterator(); i.hasNext(); )
{
Object o = i.next();
if (o instanceof jxl.biff.drawing.Drawing)
{
WritableImage wi = new WritableImage
((jxl.biff.drawing.Drawing) o,
toSheet.getWorkbook().getDrawingGroup());
toDrawings.add(wi);
toImages.add(wi);
}
// Not necessary to copy the comments, as they will be handled by
// the deep copy of the individual cells
}
// Copy the workspace options
sheetWriter.setWorkspaceOptions(fromWorkspaceOptions);
// Copy the environment specific print record
if (fromPLSRecord != null)
{
toPLSRecord = new PLSRecord(fromPLSRecord);
}
// Copy the button property set
if (fromButtonPropertySet != null)
{
toButtonPropertySet = new ButtonPropertySetRecord(fromButtonPropertySet);
}
// Copy the hyperlinks
for (Iterator i = fromHyperlinks.iterator(); i.hasNext();)
{
WritableHyperlink hr = new WritableHyperlink
((WritableHyperlink) i.next(), toSheet);
toHyperlinks.add(hr);
}
}
/**
* Performs a shallow copy of the specified cell
*/
private WritableCell shallowCopyCell(Cell cell)
{
CellType ct = cell.getType();
WritableCell newCell = null;
if (ct == CellType.LABEL)
{
newCell = new Label((LabelCell) cell);
}
else if (ct == CellType.NUMBER)
{
newCell = new Number((NumberCell) cell);
}
else if (ct == CellType.DATE)
{
newCell = new DateTime((DateCell) cell);
}
else if (ct == CellType.BOOLEAN)
{
newCell = new Boolean((BooleanCell) cell);
}
else if (ct == CellType.NUMBER_FORMULA)
{
newCell = new ReadNumberFormulaRecord((FormulaData) cell);
}
else if (ct == CellType.STRING_FORMULA)
{
newCell = new ReadStringFormulaRecord((FormulaData) cell);
}
else if( ct == CellType.BOOLEAN_FORMULA)
{
newCell = new ReadBooleanFormulaRecord((FormulaData) cell);
}
else if (ct == CellType.DATE_FORMULA)
{
newCell = new ReadDateFormulaRecord((FormulaData) cell);
}
else if(ct == CellType.FORMULA_ERROR)
{
newCell = new ReadErrorFormulaRecord((FormulaData) cell);
}
else if (ct == CellType.EMPTY)
{
if (cell.getCellFormat() != null)
{
// It is a blank cell, rather than an empty cell, so
// it may have formatting information, so
// it must be copied
newCell = new Blank(cell);
}
}
return newCell;
}
/**
* Performs a deep copy of the specified cell, handling the cell format
*
* @param cell the cell to copy
*/
private WritableCell deepCopyCell(Cell cell)
{
WritableCell c = shallowCopyCell(cell);
if (c == null)
{
return c;
}
if (c instanceof ReadFormulaRecord)
{
ReadFormulaRecord rfr = (ReadFormulaRecord) c;
boolean crossSheetReference = !rfr.handleImportedCellReferences
(fromSheet.getWorkbook(),
fromSheet.getWorkbook(),
workbookSettings);
if (crossSheetReference)
{
try
{
logger.warn("Formula " + rfr.getFormula() +
" in cell " +
CellReferenceHelper.getCellReference(cell.getColumn(),
cell.getRow()) +
" cannot be imported because it references another " +
" sheet from the source workbook");
}
catch (FormulaException e)
{
logger.warn("Formula in cell " +
CellReferenceHelper.getCellReference(cell.getColumn(),
cell.getRow()) +
" cannot be imported: " + e.getMessage());
}
// Create a new error formula and add it instead
c = new Formula(cell.getColumn(), cell.getRow(), "\"ERROR\"");
}
}
// Copy the cell format
CellFormat cf = c.getCellFormat();
int index = ( (XFRecord) cf).getXFIndex();
WritableCellFormat wcf = (WritableCellFormat)
xfRecords.get(new Integer(index));
if (wcf == null)
{
wcf = copyCellFormat(cf);
}
c.setCellFormat(wcf);
return c;
}
/**
* Perform a shallow copy of the cells from the specified sheet into this one
*/
void shallowCopyCells()
{
// Copy the cells
int cells = fromSheet.getRows();
Cell[] row = null;
Cell cell = null;
for (int i = 0; i < cells; i++)
{
row = fromSheet.getRow(i);
for (int j = 0; j < row.length; j++)
{
cell = row[j];
WritableCell c = shallowCopyCell(cell);
// Encase the calls to addCell in a try-catch block
// These should not generate any errors, because we are
// copying from an existing spreadsheet. In the event of
// errors, catch the exception and then bomb out with an
// assertion
try
{
if (c != null)
{
toSheet.addCell(c);
// Cell.setCellFeatures short circuits when the cell is copied,
// so make sure the copy logic handles the validated cells
if (c.getCellFeatures() != null &
c.getCellFeatures().hasDataValidation())
{
validatedCells.add(c);
}
}
}
catch (WriteException e)
{
Assert.verify(false);
}
}
}
numRows = toSheet.getRows();
}
/**
* Perform a deep copy of the cells from the specified sheet into this one
*/
void deepCopyCells()
{
// Copy the cells
int cells = fromSheet.getRows();
Cell[] row = null;
Cell cell = null;
for (int i = 0; i < cells; i++)
{
row = fromSheet.getRow(i);
for (int j = 0; j < row.length; j++)
{
cell = row[j];
WritableCell c = deepCopyCell(cell);
// Encase the calls to addCell in a try-catch block
// These should not generate any errors, because we are
// copying from an existing spreadsheet. In the event of
// errors, catch the exception and then bomb out with an
// assertion
try
{
if (c != null)
{
toSheet.addCell(c);
// Cell.setCellFeatures short circuits when the cell is copied,
// so make sure the copy logic handles the validated cells
if (c.getCellFeatures() != null &
c.getCellFeatures().hasDataValidation())
{
validatedCells.add(c);
}
}
}
catch (WriteException e)
{
Assert.verify(false);
}
}
}
}
/**
* Returns an initialized copy of the cell format
*
* @param cf the cell format to copy
* @return a deep copy of the cell format
*/
private WritableCellFormat copyCellFormat(CellFormat cf)
{
try
{
// just do a deep copy of the cell format for now. This will create
// a copy of the format and font also - in the future this may
// need to be sorted out
XFRecord xfr = (XFRecord) cf;
WritableCellFormat f = new WritableCellFormat(xfr);
formatRecords.addStyle(f);
// Maintain the local list of formats
int xfIndex = xfr.getXFIndex();
xfRecords.put(new Integer(xfIndex), f);
int fontIndex = xfr.getFontIndex();
fonts.put(new Integer(fontIndex), new Integer(f.getFontIndex()));
int formatIndex = xfr.getFormatRecord();
formats.put(new Integer(formatIndex), new Integer(f.getFormatRecord()));
return f;
}
catch (NumFormatRecordsException e)
{
logger.warn("Maximum number of format records exceeded. Using " +
"default format.");
return WritableWorkbook.NORMAL_STYLE;
}
}
/**
* Accessor for the maximum column outline level
*
* @return the maximum column outline level, or 0 if no outlines/groups
*/
public int getMaxColumnOutlineLevel()
{
return maxColumnOutlineLevel;
}
/**
* Accessor for the maximum row outline level
*
* @return the maximum row outline level, or 0 if no outlines/groups
*/
public int getMaxRowOutlineLevel()
{
return maxRowOutlineLevel;
}
}