package jxl.read.biff; import common.Assert; import common.Logger; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import jxl.Cell; import jxl.Range; import jxl.Sheet; import jxl.Workbook; import jxl.WorkbookSettings; import jxl.biff.CellReferenceHelper; import jxl.biff.DisplayFormat; import jxl.biff.FontRecord; import jxl.biff.Fonts; import jxl.biff.FormatRecord; import jxl.biff.FormattingRecords; import jxl.biff.NumFormatRecordsException; import jxl.biff.PaletteRecord; import jxl.biff.RangeImpl; import jxl.biff.Type; import jxl.biff.WorkbookMethods; import jxl.biff.XFRecord; import jxl.biff.drawing.DrawingGroup; import jxl.biff.drawing.MsoDrawingGroupRecord; import jxl.biff.drawing.Origin; import jxl.biff.formula.ExternalSheet; public class WorkbookParser extends Workbook implements ExternalSheet, WorkbookMethods { private static Logger logger = Logger.getLogger(WorkbookParser.class); private File excelFile; private int bofs; private boolean nineteenFour; private SSTRecord sharedStrings; private ArrayList boundsheets; private FormattingRecords formattingRecords; private Fonts fonts; private ArrayList sheets; private SheetImpl lastSheet; private int lastSheetIndex; private HashMap namedRecords; private ArrayList nameTable; private ExternalSheetRecord externSheet; private ArrayList supbooks; private BOFRecord workbookBof; private MsoDrawingGroupRecord msoDrawingGroup; private ButtonPropertySetRecord buttonPropertySet; private boolean wbProtected; private boolean containsMacros; private WorkbookSettings settings; private DrawingGroup drawingGroup; private CountryRecord countryRecord; public WorkbookParser(File f, WorkbookSettings s) { this.excelFile = f; this.boundsheets = new ArrayList(10); this.fonts = new Fonts(); this.formattingRecords = new FormattingRecords(this.fonts); this.sheets = new ArrayList(10); this.supbooks = new ArrayList(10); this.namedRecords = new HashMap(); this.lastSheetIndex = -1; this.wbProtected = false; this.containsMacros = false; this.settings = s; } public Sheet[] getSheets() { Sheet[] sheetArray = new Sheet[getNumberOfSheets()]; return (Sheet[])this.sheets.toArray((Object[])sheetArray); } public Sheet getReadSheet(int index) { return getSheet(index); } public Sheet getSheet(int index) { if (this.lastSheet != null && this.lastSheetIndex == index) return this.lastSheet; if (this.lastSheet != null) { this.lastSheet.clear(); if (!this.settings.getGCDisabled()) System.gc(); } this.lastSheet = this.sheets.get(index); this.lastSheetIndex = index; this.lastSheet.readSheet(); return this.lastSheet; } public Sheet getSheet(String name) { int pos = 0; boolean found = false; Iterator i = this.boundsheets.iterator(); BoundsheetRecord br = null; while (i.hasNext() && !found) { br = i.next(); if (br.getName().equals(name)) { found = true; continue; } pos++; } return found ? getSheet(pos) : null; } public String[] getSheetNames() { String[] names = new String[this.boundsheets.size()]; BoundsheetRecord br = null; for (int i = 0; i < names.length; i++) { br = this.boundsheets.get(i); names[i] = br.getName(); } return names; } public int getExternalSheetIndex(int index) { if (this.workbookBof.isBiff7()) return index; Assert.verify((this.externSheet != null)); int firstTab = this.externSheet.getFirstTabIndex(index); return firstTab; } public int getLastExternalSheetIndex(int index) { if (this.workbookBof.isBiff7()) return index; Assert.verify((this.externSheet != null)); int lastTab = this.externSheet.getLastTabIndex(index); return lastTab; } public String getExternalSheetName(int index) { if (this.workbookBof.isBiff7()) { BoundsheetRecord br = this.boundsheets.get(index); return br.getName(); } int supbookIndex = this.externSheet.getSupbookIndex(index); SupbookRecord sr = this.supbooks.get(supbookIndex); int firstTab = this.externSheet.getFirstTabIndex(index); if (sr.getType() == SupbookRecord.INTERNAL) { BoundsheetRecord br = this.boundsheets.get(firstTab); return br.getName(); } if (sr.getType() == SupbookRecord.EXTERNAL) { StringBuffer sb = new StringBuffer(); sb.append('['); sb.append(sr.getFileName()); sb.append(']'); sb.append(sr.getSheetName(firstTab)); return sb.toString(); } return "[UNKNOWN]"; } public String getLastExternalSheetName(int index) { if (this.workbookBof.isBiff7()) { BoundsheetRecord br = this.boundsheets.get(index); return br.getName(); } int supbookIndex = this.externSheet.getSupbookIndex(index); SupbookRecord sr = this.supbooks.get(supbookIndex); int lastTab = this.externSheet.getLastTabIndex(index); if (sr.getType() == SupbookRecord.INTERNAL) { BoundsheetRecord br = this.boundsheets.get(lastTab); return br.getName(); } if (sr.getType() == SupbookRecord.EXTERNAL) { StringBuffer sb = new StringBuffer(); sb.append('['); sb.append(sr.getFileName()); sb.append(']'); sb.append(sr.getSheetName(lastTab)); return sb.toString(); } return "[UNKNOWN]"; } public int getNumberOfSheets() { return this.sheets.size(); } public void close() { if (this.lastSheet != null) this.lastSheet.clear(); this.excelFile.clear(); if (!this.settings.getGCDisabled()) System.gc(); } final void addSheet(Sheet s) { this.sheets.add(s); } protected void parse() throws BiffException, PasswordException { Record r = null; BOFRecord bof = new BOFRecord(this.excelFile.next()); this.workbookBof = bof; this.bofs++; if (!bof.isBiff8() && !bof.isBiff7()) throw new BiffException(BiffException.unrecognizedBiffVersion); if (!bof.isWorkbookGlobals()) throw new BiffException(BiffException.expectedGlobals); ArrayList continueRecords = new ArrayList(); this.nameTable = new ArrayList(); while (this.bofs == 1) { r = this.excelFile.next(); if (r.getType() == Type.SST) { continueRecords.clear(); Record nextrec = this.excelFile.peek(); while (nextrec.getType() == Type.CONTINUE) { continueRecords.add(this.excelFile.next()); nextrec = this.excelFile.peek(); } Record[] records = new Record[continueRecords.size()]; records = continueRecords.toArray(records); this.sharedStrings = new SSTRecord(r, records, this.settings); continue; } if (r.getType() == Type.FILEPASS) throw new PasswordException(); if (r.getType() == Type.NAME) { NameRecord nr = null; if (bof.isBiff8()) { nr = new NameRecord(r, this.settings, this.namedRecords.size()); } else { nr = new NameRecord(r, this.settings, this.namedRecords.size(), NameRecord.biff7); } this.namedRecords.put(nr.getName(), nr); this.nameTable.add(nr); continue; } if (r.getType() == Type.FONT) { FontRecord fr = null; if (bof.isBiff8()) { fr = new FontRecord(r, this.settings); } else { fr = new FontRecord(r, this.settings, FontRecord.biff7); } this.fonts.addFont(fr); continue; } if (r.getType() == Type.PALETTE) { PaletteRecord palette = new PaletteRecord(r); this.formattingRecords.setPalette(palette); continue; } if (r.getType() == Type.NINETEENFOUR) { NineteenFourRecord nr = new NineteenFourRecord(r); this.nineteenFour = nr.is1904(); continue; } if (r.getType() == Type.FORMAT) { FormatRecord fr = null; if (bof.isBiff8()) { fr = new FormatRecord(r, this.settings, FormatRecord.biff8); } else { fr = new FormatRecord(r, this.settings, FormatRecord.biff7); } try { this.formattingRecords.addFormat((DisplayFormat)fr); } catch (NumFormatRecordsException e) { e.printStackTrace(); Assert.verify(false, e.getMessage()); } continue; } if (r.getType() == Type.XF) { XFRecord xfr = null; if (bof.isBiff8()) { xfr = new XFRecord(r, this.settings, XFRecord.biff8); } else { xfr = new XFRecord(r, this.settings, XFRecord.biff7); } try { this.formattingRecords.addStyle(xfr); } catch (NumFormatRecordsException e) { Assert.verify(false, e.getMessage()); } continue; } if (r.getType() == Type.BOUNDSHEET) { BoundsheetRecord br = null; if (bof.isBiff8()) { br = new BoundsheetRecord(r); } else { br = new BoundsheetRecord(r, BoundsheetRecord.biff7); } if (br.isSheet()) { this.boundsheets.add(br); continue; } if (br.isChart() && !this.settings.getDrawingsDisabled()) this.boundsheets.add(br); continue; } if (r.getType() == Type.EXTERNSHEET) { if (bof.isBiff8()) { this.externSheet = new ExternalSheetRecord(r, this.settings); continue; } this.externSheet = new ExternalSheetRecord(r, this.settings, ExternalSheetRecord.biff7); continue; } if (r.getType() == Type.CODEPAGE) { CodepageRecord cr = new CodepageRecord(r); this.settings.setCharacterSet(cr.getCharacterSet()); continue; } if (r.getType() == Type.SUPBOOK) { Record nextrec = this.excelFile.peek(); while (nextrec.getType() == Type.CONTINUE) { r.addContinueRecord(this.excelFile.next()); nextrec = this.excelFile.peek(); } SupbookRecord sr = new SupbookRecord(r, this.settings); this.supbooks.add(sr); continue; } if (r.getType() == Type.PROTECT) { ProtectRecord pr = new ProtectRecord(r); this.wbProtected = pr.isProtected(); continue; } if (r.getType() == Type.OBJPROJ) { this.containsMacros = true; continue; } if (r.getType() == Type.COUNTRY) { this.countryRecord = new CountryRecord(r); continue; } if (r.getType() == Type.MSODRAWINGGROUP) { if (!this.settings.getDrawingsDisabled()) { this.msoDrawingGroup = new MsoDrawingGroupRecord(r); if (this.drawingGroup == null) this.drawingGroup = new DrawingGroup(Origin.READ); this.drawingGroup.add(this.msoDrawingGroup); Record nextrec = this.excelFile.peek(); while (nextrec.getType() == Type.CONTINUE) { this.drawingGroup.add(this.excelFile.next()); nextrec = this.excelFile.peek(); } } continue; } if (r.getType() == Type.BUTTONPROPERTYSET) { this.buttonPropertySet = new ButtonPropertySetRecord(r); continue; } if (r.getType() == Type.EOF) this.bofs--; } bof = null; if (this.excelFile.hasNext()) { r = this.excelFile.next(); if (r.getType() == Type.BOF) bof = new BOFRecord(r); } while (bof != null && getNumberOfSheets() < this.boundsheets.size()) { if (!bof.isBiff8() && !bof.isBiff7()) throw new BiffException(BiffException.unrecognizedBiffVersion); if (bof.isWorksheet()) { SheetImpl s = new SheetImpl(this.excelFile, this.sharedStrings, this.formattingRecords, bof, this.workbookBof, this.nineteenFour, this); BoundsheetRecord br = this.boundsheets.get(getNumberOfSheets()); s.setName(br.getName()); s.setHidden(br.isHidden()); addSheet(s); } else if (bof.isChart()) { SheetImpl s = new SheetImpl(this.excelFile, this.sharedStrings, this.formattingRecords, bof, this.workbookBof, this.nineteenFour, this); BoundsheetRecord br = this.boundsheets.get(getNumberOfSheets()); s.setName(br.getName()); s.setHidden(br.isHidden()); addSheet(s); } else { logger.warn("BOF is unrecognized"); while (this.excelFile.hasNext() && r.getType() != Type.EOF) r = this.excelFile.next(); } bof = null; if (this.excelFile.hasNext()) { r = this.excelFile.next(); if (r.getType() == Type.BOF) bof = new BOFRecord(r); } } } public FormattingRecords getFormattingRecords() { return this.formattingRecords; } public ExternalSheetRecord getExternalSheetRecord() { return this.externSheet; } public MsoDrawingGroupRecord getMsoDrawingGroupRecord() { return this.msoDrawingGroup; } public SupbookRecord[] getSupbookRecords() { SupbookRecord[] sr = new SupbookRecord[this.supbooks.size()]; return (SupbookRecord[])this.supbooks.toArray((Object[])sr); } public NameRecord[] getNameRecords() { NameRecord[] na = new NameRecord[this.nameTable.size()]; return (NameRecord[])this.nameTable.toArray((Object[])na); } public Fonts getFonts() { return this.fonts; } public Cell getCell(String loc) { Sheet s = getSheet(CellReferenceHelper.getSheet(loc)); return s.getCell(loc); } public Cell findCellByName(String name) { NameRecord nr = (NameRecord)this.namedRecords.get(name); if (nr == null) return null; NameRecord.NameRange[] ranges = nr.getRanges(); Sheet s = getSheet(ranges[0].getExternalSheet()); Cell cell = s.getCell(ranges[0].getFirstColumn(), ranges[0].getFirstRow()); return cell; } public Range[] findByName(String name) { NameRecord nr = (NameRecord)this.namedRecords.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; } public String[] getRangeNames() { Object[] keys = this.namedRecords.keySet().toArray(); String[] names = new String[keys.length]; System.arraycopy(keys, 0, names, 0, keys.length); return names; } public BOFRecord getWorkbookBof() { return this.workbookBof; } public boolean isProtected() { return this.wbProtected; } public WorkbookSettings getSettings() { return this.settings; } public int getExternalSheetIndex(String sheetName) { return 0; } public int getLastExternalSheetIndex(String sheetName) { return 0; } public String getName(int index) { Assert.verify((index >= 0 && index < this.nameTable.size())); return ((NameRecord)this.nameTable.get(index)).getName(); } public int getNameIndex(String name) { NameRecord nr = (NameRecord)this.namedRecords.get(name); return (nr != null) ? nr.getIndex() : 0; } public DrawingGroup getDrawingGroup() { return this.drawingGroup; } public CompoundFile getCompoundFile() { return this.excelFile.getCompoundFile(); } public boolean containsMacros() { return this.containsMacros; } public ButtonPropertySetRecord getButtonPropertySet() { return this.buttonPropertySet; } public CountryRecord getCountryRecord() { return this.countryRecord; } }