From 65e23c7ea96037f05422c810b60b35b7328a16e0 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Thu, 31 Jul 2014 13:31:01 +0000 Subject: [PATCH] Fix bug #53984 - Support the ColInfoRecord coming after the cells, rather than before as is normal, and also handle things so that if an unsupported BofRecord is found for a sheet, warn and skip rather than breaking git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1614884 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/hssf/model/InternalSheet.java | 69 +++++++++++++++++- .../apache/poi/hssf/model/RecordOrderer.java | 3 +- .../poi/hssf/record/EscherAggregate.java | 18 ++++- .../poi/hssf/usermodel/HSSFWorkbook.java | 10 ++- .../apache/poi/hssf/usermodel/TestBugs.java | 11 +++ test-data/spreadsheet/53984.xls | Bin 0 -> 38331 bytes 6 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 test-data/spreadsheet/53984.xls diff --git a/src/java/org/apache/poi/hssf/model/InternalSheet.java b/src/java/org/apache/poi/hssf/model/InternalSheet.java index 277fdbd72..183ca2c41 100644 --- a/src/java/org/apache/poi/hssf/model/InternalSheet.java +++ b/src/java/org/apache/poi/hssf/model/InternalSheet.java @@ -21,7 +21,42 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import org.apache.poi.hssf.record.*; +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.CFHeaderRecord; +import org.apache.poi.hssf.record.CalcCountRecord; +import org.apache.poi.hssf.record.CalcModeRecord; +import org.apache.poi.hssf.record.CellValueRecordInterface; +import org.apache.poi.hssf.record.ColumnInfoRecord; +import org.apache.poi.hssf.record.DVALRecord; +import org.apache.poi.hssf.record.DefaultColWidthRecord; +import org.apache.poi.hssf.record.DefaultRowHeightRecord; +import org.apache.poi.hssf.record.DeltaRecord; +import org.apache.poi.hssf.record.DimensionsRecord; +import org.apache.poi.hssf.record.DrawingRecord; +import org.apache.poi.hssf.record.EOFRecord; +import org.apache.poi.hssf.record.EscherAggregate; +import org.apache.poi.hssf.record.FeatHdrRecord; +import org.apache.poi.hssf.record.FeatRecord; +import org.apache.poi.hssf.record.GridsetRecord; +import org.apache.poi.hssf.record.GutsRecord; +import org.apache.poi.hssf.record.IndexRecord; +import org.apache.poi.hssf.record.IterationRecord; +import org.apache.poi.hssf.record.MergeCellsRecord; +import org.apache.poi.hssf.record.NoteRecord; +import org.apache.poi.hssf.record.PaneRecord; +import org.apache.poi.hssf.record.PrintGridlinesRecord; +import org.apache.poi.hssf.record.PrintHeadersRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.RecordBase; +import org.apache.poi.hssf.record.RecordFormatException; +import org.apache.poi.hssf.record.RefModeRecord; +import org.apache.poi.hssf.record.RowRecord; +import org.apache.poi.hssf.record.SCLRecord; +import org.apache.poi.hssf.record.SaveRecalcRecord; +import org.apache.poi.hssf.record.SelectionRecord; +import org.apache.poi.hssf.record.UncalcedRecord; +import org.apache.poi.hssf.record.WSBoolRecord; +import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.hssf.record.aggregates.ChartSubstreamRecordAggregate; import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate; import org.apache.poi.hssf.record.aggregates.ConditionalFormattingTable; @@ -131,11 +166,27 @@ public final class InternalSheet { if (rs.peekNextSid() != BOFRecord.sid) { throw new RuntimeException("BOF record expected"); } + BOFRecord bof = (BOFRecord) rs.getNext(); - if (bof.getType() != BOFRecord.TYPE_WORKSHEET) { - // TODO - fix junit tests throw new RuntimeException("Bad BOF record type"); + if (bof.getType() == BOFRecord.TYPE_WORKSHEET) { + // Good, well supported + } else if (bof.getType() == BOFRecord.TYPE_CHART || + bof.getType() == BOFRecord.TYPE_EXCEL_4_MACRO) { + // These aren't really typical sheets... Let it go though, + // we can handle them roughly well enough as a "normal" one + } else { + // Not a supported type + // Skip onto the EOF, then complain + while (rs.hasNext()) { + Record rec = rs.getNext(); + if (rec instanceof EOFRecord) { + break; + } + } + throw new UnsupportedBOFType(bof.getType()); } records.add(bof); + while (rs.hasNext()) { int recSid = rs.peekNextSid(); @@ -318,6 +369,18 @@ public final class InternalSheet { recs.add(r); }}); } + + public static class UnsupportedBOFType extends RecordFormatException { + private final int type; + protected UnsupportedBOFType(int type) { + super("BOF not of a supported type, found " + type); + this.type = type; + } + + public int getType() { + return type; + } + } private static final class RecordCloner implements RecordVisitor { diff --git a/src/java/org/apache/poi/hssf/model/RecordOrderer.java b/src/java/org/apache/poi/hssf/model/RecordOrderer.java index 69e9971fa..9f1871cad 100644 --- a/src/java/org/apache/poi/hssf/model/RecordOrderer.java +++ b/src/java/org/apache/poi/hssf/model/RecordOrderer.java @@ -25,6 +25,7 @@ import org.apache.poi.hssf.record.BlankRecord; import org.apache.poi.hssf.record.BoolErrRecord; import org.apache.poi.hssf.record.CalcCountRecord; import org.apache.poi.hssf.record.CalcModeRecord; +import org.apache.poi.hssf.record.ColumnInfoRecord; import org.apache.poi.hssf.record.DVALRecord; import org.apache.poi.hssf.record.DateWindow1904Record; import org.apache.poi.hssf.record.DefaultColWidthRecord; @@ -415,7 +416,7 @@ final class RecordOrderer { case DrawingSelectionRecord.sid: case ObjRecord.sid: case TextObjectRecord.sid: - + case ColumnInfoRecord.sid: // See Bugzilla 53984 case GutsRecord.sid: // see Bugzilla 50426 case WindowOneRecord.sid: // should really be part of workbook stream, but some apps seem to put this before WINDOW2 diff --git a/src/java/org/apache/poi/hssf/record/EscherAggregate.java b/src/java/org/apache/poi/hssf/record/EscherAggregate.java index 9ca4fefc0..21c4db454 100644 --- a/src/java/org/apache/poi/hssf/record/EscherAggregate.java +++ b/src/java/org/apache/poi/hssf/record/EscherAggregate.java @@ -19,7 +19,12 @@ package org.apache.poi.hssf.record; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import org.apache.poi.ddf.DefaultEscherRecordFactory; import org.apache.poi.ddf.EscherClientDataRecord; @@ -754,8 +759,15 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { * @param loc - location of the record which sid must be returned * @return sid of the record with selected location */ - private static short sid(List records, int loc) { - return ((Record) records.get(loc)).getSid(); + private static short sid(List records, int loc) { + RecordBase record = records.get(loc); + if (record instanceof Record) { + return ((Record)record).getSid(); + } else { + // Aggregates don't have a sid + // We could step into them, but for these needs we don't care + return -1; + } } /** diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 7a1479fdd..51d4b48ba 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -46,6 +46,7 @@ import org.apache.poi.hssf.OldExcelFormatException; import org.apache.poi.hssf.model.DrawingManager2; import org.apache.poi.hssf.model.HSSFFormulaParser; import org.apache.poi.hssf.model.InternalSheet; +import org.apache.poi.hssf.model.InternalSheet.UnsupportedBOFType; import org.apache.poi.hssf.model.InternalWorkbook; import org.apache.poi.hssf.model.RecordStream; import org.apache.poi.hssf.record.AbstractEscherHolderRecord; @@ -321,8 +322,13 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss convertLabelRecords(records, recOffset); RecordStream rs = new RecordStream(records, recOffset); while (rs.hasNext()) { - InternalSheet sheet = InternalSheet.createSheet(rs); - _sheets.add(new HSSFSheet(this, sheet)); + try { + InternalSheet sheet = InternalSheet.createSheet(rs); + _sheets.add(new HSSFSheet(this, sheet)); + } catch (UnsupportedBOFType eb) { + // Hopefully there's a supported one after this! + log.log(POILogger.WARN, "Unsupported BOF found of type " + eb.getType()); + } } for (int i = 0 ; i < workbook.getNumNames() ; ++i){ diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index 543ec99eb..f0cc68101 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -2731,4 +2731,15 @@ public final class TestBugs extends BaseTestBugzillaIssues { assertEquals("A$1+B$1", s.getRow(row).getCell(2).getCellFormula()); } } + + /** + * Unexpected record type (org.apache.poi.hssf.record.ColumnInfoRecord) + */ + @Test + public void bug53984() { + Workbook wb = openSample("53984.xls"); + Sheet s = wb.getSheetAt(0); + assertEquals("International Communication Services SA", s.getRow(2).getCell(0).getStringCellValue()); + assertEquals("Saudi Arabia-Riyadh", s.getRow(210).getCell(0).getStringCellValue()); + } } diff --git a/test-data/spreadsheet/53984.xls b/test-data/spreadsheet/53984.xls new file mode 100644 index 0000000000000000000000000000000000000000..07d3e8337979dbe55b504c1ef4f62ccc76b114dd GIT binary patch literal 38331 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 literal 0 HcmV?d00001