package jxl.write.biff; import common.Assert; import common.Logger; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import jxl.Range; import jxl.Sheet; import jxl.Workbook; import jxl.WorkbookSettings; import jxl.biff.ByteData; import jxl.biff.CellReferenceHelper; import jxl.biff.CountryCode; import jxl.biff.Fonts; import jxl.biff.FormattingRecords; import jxl.biff.IndexMapping; import jxl.biff.IntegerHelper; import jxl.biff.RangeImpl; import jxl.biff.WorkbookMethods; import jxl.biff.drawing.Drawing; import jxl.biff.drawing.DrawingGroup; import jxl.biff.drawing.DrawingGroupObject; import jxl.biff.drawing.Origin; import jxl.biff.formula.ExternalSheet; import jxl.format.Colour; import jxl.format.RGB; import jxl.read.biff.BOFRecord; import jxl.read.biff.NameRecord; import jxl.read.biff.SupbookRecord; import jxl.read.biff.WorkbookParser; import jxl.write.WritableCell; import jxl.write.WritableSheet; import jxl.write.WritableWorkbook; public class WritableWorkbookImpl extends WritableWorkbook implements ExternalSheet, WorkbookMethods { private static Logger logger = Logger.getLogger(WritableWorkbookImpl.class); private FormattingRecords formatRecords; private File outputFile; private ArrayList sheets; private Fonts fonts; private ExternalSheetRecord externSheet; private ArrayList supbooks; private ArrayList names; private HashMap nameRecords; private SharedStrings sharedStrings; private boolean closeStream; private boolean wbProtected; private WorkbookSettings settings; private ArrayList rcirCells; private DrawingGroup drawingGroup; private Styles styles; private boolean containsMacros; private ButtonPropertySetRecord buttonPropertySet; private CountryRecord countryRecord; public WritableWorkbookImpl(OutputStream os, boolean cs, WorkbookSettings ws) throws IOException { this.outputFile = new File(os, ws, null); this.sheets = new ArrayList(); this.sharedStrings = new SharedStrings(); this.nameRecords = new HashMap(); this.closeStream = cs; this.wbProtected = false; this.containsMacros = false; this.settings = ws; this.rcirCells = new ArrayList(); this.styles = new Styles(); WritableWorkbook.ARIAL_10_PT.uninitialize(); WritableWorkbook.HYPERLINK_FONT.uninitialize(); WritableWorkbook.NORMAL_STYLE.uninitialize(); WritableWorkbook.HYPERLINK_STYLE.uninitialize(); WritableWorkbook.HIDDEN_STYLE.uninitialize(); DateRecord.defaultDateFormat.uninitialize(); WritableFonts wf = new WritableFonts(this); this.fonts = wf; WritableFormattingRecords wfr = new WritableFormattingRecords(this.fonts, this.styles); this.formatRecords = wfr; } public WritableWorkbookImpl(OutputStream os, Workbook w, boolean cs, WorkbookSettings ws) throws IOException { WorkbookParser wp = (WorkbookParser)w; WritableWorkbook.ARIAL_10_PT.uninitialize(); WritableWorkbook.HYPERLINK_FONT.uninitialize(); WritableWorkbook.NORMAL_STYLE.uninitialize(); WritableWorkbook.HYPERLINK_STYLE.uninitialize(); WritableWorkbook.HIDDEN_STYLE.uninitialize(); DateRecord.defaultDateFormat.uninitialize(); this.closeStream = cs; this.sheets = new ArrayList(); this.sharedStrings = new SharedStrings(); this.nameRecords = new HashMap(); this.fonts = wp.getFonts(); this.formatRecords = wp.getFormattingRecords(); this.wbProtected = false; this.settings = ws; this.rcirCells = new ArrayList(); this.styles = new Styles(); this.outputFile = new File(os, ws, wp.getCompoundFile()); this.containsMacros = false; if (!ws.getPropertySetsDisabled()) this.containsMacros = wp.containsMacros(); if (wp.getCountryRecord() != null) this.countryRecord = new CountryRecord(wp.getCountryRecord()); if (wp.getExternalSheetRecord() != null) { this.externSheet = new ExternalSheetRecord(wp.getExternalSheetRecord()); SupbookRecord[] readsr = wp.getSupbookRecords(); this.supbooks = new ArrayList(readsr.length); for (int i = 0; i < readsr.length; i++) this.supbooks.add(new SupbookRecord(readsr[i], this.settings)); } if (wp.getDrawingGroup() != null) this.drawingGroup = new DrawingGroup(wp.getDrawingGroup()); if (this.containsMacros && wp.getButtonPropertySet() != null) this.buttonPropertySet = new ButtonPropertySetRecord(wp.getButtonPropertySet()); if (!this.settings.getNamesDisabled()) { NameRecord[] na = wp.getNameRecords(); this.names = new ArrayList(na.length); for (int i = 0; i < na.length; i++) { if (na[i].isBiff8()) { NameRecord n = new NameRecord(na[i], i); this.names.add(n); String name = n.getName(); this.nameRecords.put(name, n); } else { logger.warn("Cannot copy Biff7 name records - ignoring"); } } } copyWorkbook(w); if (this.drawingGroup != null) this.drawingGroup.updateData(wp.getDrawingGroup()); } public WritableSheet[] getSheets() { WritableSheet[] sheetArray = new WritableSheet[getNumberOfSheets()]; for (int i = 0; i < getNumberOfSheets(); i++) sheetArray[i] = getSheet(i); return sheetArray; } public String[] getSheetNames() { String[] sheetNames = new String[getNumberOfSheets()]; for (int i = 0; i < sheetNames.length; i++) sheetNames[i] = getSheet(i).getName(); return sheetNames; } public Sheet getReadSheet(int index) { return (Sheet)getSheet(index); } public WritableSheet getSheet(int index) { return this.sheets.get(index); } public WritableSheet getSheet(String name) { boolean found = false; Iterator i = this.sheets.iterator(); WritableSheet s = null; while (i.hasNext() && !found) { s = i.next(); if (s.getName().equals(name)) found = true; } return found ? s : null; } public int getNumberOfSheets() { return this.sheets.size(); } public void close() throws IOException, JxlWriteException { this.outputFile.close(this.closeStream); } public void setOutputFile(File fileName) throws IOException { FileOutputStream fos = new FileOutputStream(fileName); this.outputFile.setOutputFile(fos); } private WritableSheet createSheet(String name, int index, boolean handleRefs) { WritableSheet w = new WritableSheetImpl(name, this.outputFile, this.formatRecords, this.sharedStrings, this.settings, this); int pos = index; if (index <= 0) { pos = 0; this.sheets.add(0, w); } else if (index > this.sheets.size()) { pos = this.sheets.size(); this.sheets.add(w); } else { this.sheets.add(index, w); } if (handleRefs && this.externSheet != null) this.externSheet.sheetInserted(pos); if (this.supbooks != null && this.supbooks.size() > 0) { SupbookRecord supbook = this.supbooks.get(0); if (supbook.getType() == SupbookRecord.INTERNAL) supbook.adjustInternal(this.sheets.size()); } return w; } public WritableSheet createSheet(String name, int index) { return createSheet(name, index, true); } public void removeSheet(int index) { int pos = index; if (index <= 0) { pos = 0; this.sheets.remove(0); } else if (index >= this.sheets.size()) { pos = this.sheets.size() - 1; this.sheets.remove(this.sheets.size() - 1); } else { this.sheets.remove(index); } if (this.externSheet != null) this.externSheet.sheetRemoved(pos); if (this.supbooks != null && this.supbooks.size() > 0) { SupbookRecord supbook = this.supbooks.get(0); if (supbook.getType() == SupbookRecord.INTERNAL) supbook.adjustInternal(this.sheets.size()); } if (this.names != null && this.names.size() > 0) for (int i = 0; i < this.names.size(); i++) { NameRecord n = this.names.get(i); int oldRef = n.getSheetRef(); if (oldRef == pos + 1) { n.setSheetRef(0); } else if (oldRef > pos + 1) { if (oldRef < 1) oldRef = 1; n.setSheetRef(oldRef - 1); } } } public WritableSheet moveSheet(int fromIndex, int toIndex) { fromIndex = Math.max(fromIndex, 0); fromIndex = Math.min(fromIndex, this.sheets.size() - 1); toIndex = Math.max(toIndex, 0); toIndex = Math.min(toIndex, this.sheets.size() - 1); WritableSheet sheet = this.sheets.remove(fromIndex); this.sheets.add(toIndex, sheet); return sheet; } public void write() throws IOException { WritableSheetImpl wsi = null; for (int i = 0; i < getNumberOfSheets(); i++) { wsi = (WritableSheetImpl)getSheet(i); wsi.checkMergedBorders(); } if (!this.settings.getRationalizationDisabled()) rationalize(); BOFRecord bof = new BOFRecord(BOFRecord.workbookGlobals); this.outputFile.write((ByteData)bof); InterfaceHeaderRecord ihr = new InterfaceHeaderRecord(); this.outputFile.write((ByteData)ihr); MMSRecord mms = new MMSRecord(0, 0); this.outputFile.write((ByteData)mms); InterfaceEndRecord ier = new InterfaceEndRecord(); this.outputFile.write((ByteData)ier); WriteAccessRecord wr = new WriteAccessRecord(); this.outputFile.write((ByteData)wr); CodepageRecord cp = new CodepageRecord(); this.outputFile.write((ByteData)cp); DSFRecord dsf = new DSFRecord(); this.outputFile.write((ByteData)dsf); TabIdRecord tabid = new TabIdRecord(getNumberOfSheets()); this.outputFile.write((ByteData)tabid); if (this.containsMacros) { ObjProjRecord objproj = new ObjProjRecord(); this.outputFile.write((ByteData)objproj); } if (this.buttonPropertySet != null) this.outputFile.write((ByteData)this.buttonPropertySet); FunctionGroupCountRecord fgcr = new FunctionGroupCountRecord(); this.outputFile.write((ByteData)fgcr); WindowProtectRecord wpr = new WindowProtectRecord(false); this.outputFile.write((ByteData)wpr); ProtectRecord pr = new ProtectRecord(this.wbProtected); this.outputFile.write((ByteData)pr); PasswordRecord pw = new PasswordRecord(null); this.outputFile.write((ByteData)pw); Prot4RevRecord p4r = new Prot4RevRecord(false); this.outputFile.write((ByteData)p4r); Prot4RevPassRecord p4rp = new Prot4RevPassRecord(); this.outputFile.write((ByteData)p4rp); Window1Record w1r = new Window1Record(); this.outputFile.write((ByteData)w1r); BackupRecord bkr = new BackupRecord(false); this.outputFile.write((ByteData)bkr); HideobjRecord ho = new HideobjRecord(false); this.outputFile.write((ByteData)ho); NineteenFourRecord nf = new NineteenFourRecord(false); this.outputFile.write((ByteData)nf); PrecisionRecord pc = new PrecisionRecord(false); this.outputFile.write((ByteData)pc); RefreshAllRecord rar = new RefreshAllRecord(false); this.outputFile.write((ByteData)rar); BookboolRecord bb = new BookboolRecord(true); this.outputFile.write((ByteData)bb); this.fonts.write(this.outputFile); this.formatRecords.write(this.outputFile); if (this.formatRecords.getPalette() != null) this.outputFile.write((ByteData)this.formatRecords.getPalette()); UsesElfsRecord uer = new UsesElfsRecord(); this.outputFile.write((ByteData)uer); int[] boundsheetPos = new int[getNumberOfSheets()]; Sheet sheet = null; int j; for (j = 0; j < getNumberOfSheets(); j++) { boundsheetPos[j] = this.outputFile.getPos(); WritableSheet writableSheet = getSheet(j); BoundsheetRecord br = new BoundsheetRecord(writableSheet.getName()); if (writableSheet.getSettings().isHidden()) br.setHidden(); if (((WritableSheetImpl)this.sheets.get(j)).isChartOnly()) br.setChartOnly(); this.outputFile.write((ByteData)br); } if (this.countryRecord == null) { CountryCode lang = CountryCode.getCountryCode(this.settings.getExcelDisplayLanguage()); if (lang == CountryCode.UNKNOWN) { logger.warn("Unknown country code " + this.settings.getExcelDisplayLanguage() + " using " + CountryCode.USA.getCode()); lang = CountryCode.USA; } CountryCode region = CountryCode.getCountryCode(this.settings.getExcelRegionalSettings()); this.countryRecord = new CountryRecord(lang, region); if (region == CountryCode.UNKNOWN) { logger.warn("Unknown country code " + this.settings.getExcelDisplayLanguage() + " using " + CountryCode.UK.getCode()); region = CountryCode.UK; } } this.outputFile.write((ByteData)this.countryRecord); if (this.externSheet != null) { for (j = 0; j < this.supbooks.size(); j++) { SupbookRecord supbook = this.supbooks.get(j); this.outputFile.write((ByteData)supbook); } this.outputFile.write((ByteData)this.externSheet); } if (this.names != null) for (j = 0; j < this.names.size(); j++) { NameRecord n = this.names.get(j); this.outputFile.write((ByteData)n); } if (this.drawingGroup != null) this.drawingGroup.write(this.outputFile); this.sharedStrings.write(this.outputFile); EOFRecord eof = new EOFRecord(); this.outputFile.write((ByteData)eof); boolean sheetSelected = false; WritableSheetImpl wsheet = null; int k; for (k = 0; k < getNumberOfSheets() && !sheetSelected; k++) { wsheet = (WritableSheetImpl)getSheet(k); if (wsheet.getSettings().isSelected()) sheetSelected = true; } if (!sheetSelected) { wsheet = (WritableSheetImpl)getSheet(0); wsheet.getSettings().setSelected(true); } for (k = 0; k < getNumberOfSheets(); k++) { this.outputFile.setData(IntegerHelper.getFourBytes(this.outputFile.getPos()), boundsheetPos[k] + 4); wsheet = (WritableSheetImpl)getSheet(k); wsheet.write(); } } private void copyWorkbook(Workbook w) { int numSheets = w.getNumberOfSheets(); this.wbProtected = w.isProtected(); Sheet s = null; WritableSheetImpl ws = null; for (int i = 0; i < numSheets; i++) { s = w.getSheet(i); ws = (WritableSheetImpl)createSheet(s.getName(), i, false); ws.copy(s); } } public void copySheet(int s, String name, int index) { WritableSheet sheet = getSheet(s); WritableSheetImpl ws = (WritableSheetImpl)createSheet(name, index); ws.copy(sheet); } public void copySheet(String s, String name, int index) { WritableSheet sheet = getSheet(s); WritableSheetImpl ws = (WritableSheetImpl)createSheet(name, index); ws.copy(sheet); } public void setProtected(boolean prot) { this.wbProtected = prot; } private void rationalize() { IndexMapping fontMapping = this.formatRecords.rationalizeFonts(); IndexMapping formatMapping = this.formatRecords.rationalizeDisplayFormats(); IndexMapping xfMapping = this.formatRecords.rationalize(fontMapping, formatMapping); WritableSheetImpl wsi = null; for (int i = 0; i < this.sheets.size(); i++) { wsi = this.sheets.get(i); wsi.rationalize(xfMapping, fontMapping, formatMapping); } } public String getExternalSheetName(int index) { int supbookIndex = this.externSheet.getSupbookIndex(index); SupbookRecord sr = this.supbooks.get(supbookIndex); int firstTab = this.externSheet.getFirstTabIndex(index); if (sr.getType() == SupbookRecord.INTERNAL) { WritableSheet ws = getSheet(firstTab); return ws.getName(); } if (sr.getType() == SupbookRecord.EXTERNAL) { String name = sr.getFileName() + sr.getSheetName(firstTab); return name; } return "[UNKNOWN]"; } public String getLastExternalSheetName(int index) { int supbookIndex = this.externSheet.getSupbookIndex(index); SupbookRecord sr = this.supbooks.get(supbookIndex); int lastTab = this.externSheet.getLastTabIndex(index); if (sr.getType() == SupbookRecord.INTERNAL) { WritableSheet ws = getSheet(lastTab); return ws.getName(); } if (sr.getType() == SupbookRecord.EXTERNAL) Assert.verify(false); return "[UNKNOWN]"; } public BOFRecord getWorkbookBof() { return null; } public int getExternalSheetIndex(int index) { if (this.externSheet == null) return index; Assert.verify((this.externSheet != null)); int firstTab = this.externSheet.getFirstTabIndex(index); return firstTab; } public int getLastExternalSheetIndex(int index) { if (this.externSheet == null) return index; Assert.verify((this.externSheet != null)); int lastTab = this.externSheet.getLastTabIndex(index); return lastTab; } public int getExternalSheetIndex(String sheetName) { if (this.externSheet == null) { this.externSheet = new ExternalSheetRecord(); this.supbooks = new ArrayList(); this.supbooks.add(new SupbookRecord(getNumberOfSheets(), this.settings)); } boolean found = false; Iterator i = this.sheets.iterator(); int sheetpos = 0; WritableSheetImpl s = null; while (i.hasNext() && !found) { s = i.next(); if (s.getName().equals(sheetName)) { found = true; continue; } sheetpos++; } if (found) { SupbookRecord supbook = this.supbooks.get(0); Assert.verify((supbook.getType() == SupbookRecord.INTERNAL && supbook.getNumberOfSheets() == getNumberOfSheets())); return this.externSheet.getIndex(0, sheetpos); } int closeSquareBracketsIndex = sheetName.lastIndexOf(']'); int openSquareBracketsIndex = sheetName.lastIndexOf('['); if (closeSquareBracketsIndex == -1 || openSquareBracketsIndex == -1) return -1; String worksheetName = sheetName.substring(closeSquareBracketsIndex + 1); String workbookName = sheetName.substring(openSquareBracketsIndex + 1, closeSquareBracketsIndex); String path = sheetName.substring(0, openSquareBracketsIndex); String fileName = path + workbookName; boolean supbookFound = false; SupbookRecord externalSupbook = null; int supbookIndex = -1; for (int ind = 0; ind < this.supbooks.size() && !supbookFound; ind++) { externalSupbook = this.supbooks.get(ind); if (externalSupbook.getType() == SupbookRecord.EXTERNAL && externalSupbook.getFileName().equals(fileName)) { supbookFound = true; supbookIndex = ind; } } if (!supbookFound) { externalSupbook = new SupbookRecord(fileName, this.settings); supbookIndex = this.supbooks.size(); this.supbooks.add(externalSupbook); } int sheetIndex = externalSupbook.getSheetIndex(worksheetName); return this.externSheet.getIndex(supbookIndex, sheetIndex); } public int getLastExternalSheetIndex(String sheetName) { if (this.externSheet == null) { this.externSheet = new ExternalSheetRecord(); this.supbooks = new ArrayList(); this.supbooks.add(new SupbookRecord(getNumberOfSheets(), this.settings)); } boolean found = false; Iterator i = this.sheets.iterator(); int sheetpos = 0; WritableSheetImpl s = null; while (i.hasNext() && !found) { s = i.next(); if (s.getName().equals(sheetName)) { found = true; continue; } sheetpos++; } if (!found) return -1; SupbookRecord supbook = this.supbooks.get(0); Assert.verify((supbook.getType() == SupbookRecord.INTERNAL && supbook.getNumberOfSheets() == getNumberOfSheets())); return this.externSheet.getIndex(0, sheetpos); } public void setColourRGB(Colour c, int r, int g, int b) { this.formatRecords.setColourRGB(c, r, g, b); } public RGB getColourRGB(Colour c) { return this.formatRecords.getColourRGB(c); } public String getName(int index) { Assert.verify((index >= 0 && index < this.names.size())); NameRecord n = this.names.get(index); return n.getName(); } public int getNameIndex(String name) { NameRecord nr = (NameRecord)this.nameRecords.get(name); return (nr != null) ? nr.getIndex() : -1; } void addRCIRCell(CellValue cv) { this.rcirCells.add(cv); } void columnInserted(WritableSheetImpl s, int col) { int externalSheetIndex = getExternalSheetIndex(s.getName()); for (Iterator i = this.rcirCells.iterator(); i.hasNext(); ) { CellValue cv = i.next(); cv.columnInserted((Sheet)s, externalSheetIndex, col); } } void columnRemoved(WritableSheetImpl s, int col) { int externalSheetIndex = getExternalSheetIndex(s.getName()); for (Iterator i = this.rcirCells.iterator(); i.hasNext(); ) { CellValue cv = i.next(); cv.columnRemoved((Sheet)s, externalSheetIndex, col); } } void rowInserted(WritableSheetImpl s, int row) { int externalSheetIndex = getExternalSheetIndex(s.getName()); for (Iterator i = this.rcirCells.iterator(); i.hasNext(); ) { CellValue cv = i.next(); cv.rowInserted((Sheet)s, externalSheetIndex, row); } } void rowRemoved(WritableSheetImpl s, int row) { int externalSheetIndex = getExternalSheetIndex(s.getName()); for (Iterator i = this.rcirCells.iterator(); i.hasNext(); ) { CellValue cv = i.next(); cv.rowRemoved((Sheet)s, externalSheetIndex, row); } } public WritableCell findCellByName(String name) { NameRecord nr = (NameRecord)this.nameRecords.get(name); if (nr == null) return null; NameRecord.NameRange[] ranges = nr.getRanges(); int sheetIndex = getExternalSheetIndex(ranges[0].getExternalSheet()); WritableSheet s = getSheet(sheetIndex); WritableCell cell = s.getWritableCell(ranges[0].getFirstColumn(), ranges[0].getFirstRow()); return cell; } public Range[] findByName(String name) { NameRecord nr = (NameRecord)this.nameRecords.get(name); if (nr == null) return null; NameRecord.NameRange[] ranges = nr.getRanges(); Range[] cellRanges = new Range[ranges.length]; for (int i = 0; i < ranges.length; i++) cellRanges[i] = (Range)new RangeImpl(this, getExternalSheetIndex(ranges[i].getExternalSheet()), ranges[i].getFirstColumn(), ranges[i].getFirstRow(), getLastExternalSheetIndex(ranges[i].getExternalSheet()), ranges[i].getLastColumn(), ranges[i].getLastRow()); return cellRanges; } void addDrawing(DrawingGroupObject d) { if (this.drawingGroup == null) this.drawingGroup = new DrawingGroup(Origin.WRITE); this.drawingGroup.add(d); } void removeDrawing(Drawing d) { Assert.verify((this.drawingGroup != null)); this.drawingGroup.remove((DrawingGroupObject)d); } DrawingGroup getDrawingGroup() { return this.drawingGroup; } public String[] getRangeNames() { String[] n = new String[this.names.size()]; for (int i = 0; i < this.names.size(); i++) { NameRecord nr = this.names.get(i); n[i] = nr.getName(); } return n; } Styles getStyles() { return this.styles; } public void addNameArea(String name, WritableSheet sheet, int firstCol, int firstRow, int lastCol, int lastRow) { if (this.names == null) this.names = new ArrayList(); int externalSheetIndex = getExternalSheetIndex(sheet.getName()); NameRecord nr = new NameRecord(name, this.names.size(), externalSheetIndex, firstRow, lastRow, firstCol, lastCol); this.names.add(nr); this.nameRecords.put(name, nr); } WorkbookSettings getSettings() { return this.settings; } public WritableCell getWritableCell(String loc) { WritableSheet s = getSheet(CellReferenceHelper.getSheet(loc)); return s.getWritableCell(loc); } }