From: Josh Micich Date: Thu, 28 Aug 2008 02:54:47 +0000 (+0000) Subject: Fix for bug 45698 - allow LinkTable to read EXTERNSHEET records X-Git-Tag: REL_3_2_FINAL~131 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=05e4c39b9e989ee187257b0ac760b6913d018762;p=poi.git Fix for bug 45698 - allow LinkTable to read EXTERNSHEET records git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@689704 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 1fe905a686..87b3468994 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + 45698 - Fix LinkTable to tolerate multiple EXTERNSHEET records 45682 - Fix for cloning of CFRecordsAggregate Initial support for evaluating external add-in functions like YEARFRAC 45672 - Fix for MissingRecordAwareHSSFListener to prevent multiple LastCellOfRowDummyRecords when shared formulas are present diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 6233176ed2..127c12e8cb 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 45698 - Fix LinkTable to tolerate multiple EXTERNSHEET records 45682 - Fix for cloning of CFRecordsAggregate Initial support for evaluating external add-in functions like YEARFRAC 45672 - Fix for MissingRecordAwareHSSFListener to prevent multiple LastCellOfRowDummyRecords when shared formulas are present diff --git a/src/java/org/apache/poi/hssf/model/LinkTable.java b/src/java/org/apache/poi/hssf/model/LinkTable.java index 9d1707558d..a64f134fed 100755 --- a/src/java/org/apache/poi/hssf/model/LinkTable.java +++ b/src/java/org/apache/poi/hssf/model/LinkTable.java @@ -159,8 +159,7 @@ final class LinkTable { if (_externalBookBlocks.length > 0) { // If any ExternalBookBlock present, there is always 1 of ExternSheetRecord - Record next = rs.getNext(); - _externSheetRecord = (ExternSheetRecord) next; + _externSheetRecord = readExtSheetRecord(rs); } else { _externSheetRecord = null; } @@ -176,6 +175,28 @@ final class LinkTable { _workbookRecordList.getRecords().addAll(inputList.subList(startIndex, startIndex + _recordCount)); } + private static ExternSheetRecord readExtSheetRecord(RecordStream rs) { + List temp = new ArrayList(2); + while(rs.peekNextClass() == ExternSheetRecord.class) { + temp.add(rs.getNext()); + } + + int nItems = temp.size(); + if (nItems < 1) { + throw new RuntimeException("Expected an EXTERNSHEET record but got (" + + rs.peekNextClass().getName() + ")"); + } + if (nItems == 1) { + // this is the normal case. There should be just one ExternSheetRecord + return (ExternSheetRecord) temp.get(0); + } + // Some apps generate multiple ExternSheetRecords (see bug 45698). + // It seems like the best thing to do might be to combine these into one + ExternSheetRecord[] esrs = new ExternSheetRecord[nItems]; + temp.toArray(esrs); + return ExternSheetRecord.combine(esrs); + } + public LinkTable(short numberOfSheets, WorkbookRecordList workbookRecordList) { _workbookRecordList = workbookRecordList; _definedNames = new ArrayList(); diff --git a/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java b/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java index 2b0744a91e..c4f00581b2 100644 --- a/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java +++ b/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java @@ -29,212 +29,212 @@ import org.apache.poi.util.LittleEndian; * @author Libin Roman (Vista Portal LDT. Developer) */ public class ExternSheetRecord extends Record { - public final static short sid = 0x0017; - private List _list; - - private final class RefSubRecord { - public static final int ENCODED_SIZE = 6; + public final static short sid = 0x0017; + private List _list; + + private final class RefSubRecord { + public static final int ENCODED_SIZE = 6; - /** index to External Book Block (which starts with a EXTERNALBOOK record) */ - private int _extBookIndex; - private int _firstSheetIndex; // may be -1 (0xFFFF) - private int _lastSheetIndex; // may be -1 (0xFFFF) - - - /** a Constructor for making new sub record - */ - public RefSubRecord(int extBookIndex, int firstSheetIndex, int lastSheetIndex) { - _extBookIndex = extBookIndex; - _firstSheetIndex = firstSheetIndex; - _lastSheetIndex = lastSheetIndex; - } - - /** - * @param in the RecordInputstream to read the record from - */ - public RefSubRecord(RecordInputStream in) { - this(in.readShort(), in.readShort(), in.readShort()); - } - public int getExtBookIndex(){ - return _extBookIndex; - } - public int getFirstSheetIndex(){ - return _firstSheetIndex; - } - public int getLastSheetIndex(){ - return _lastSheetIndex; - } - - public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append("extBook=").append(_extBookIndex); - buffer.append(" firstSheet=").append(_firstSheetIndex); - buffer.append(" lastSheet=").append(_lastSheetIndex); - return buffer.toString(); - } - - /** - * called by the class that is responsible for writing this sucker. - * Subclasses should implement this so that their data is passed back in a - * byte array. - * - * @param offset to begin writing at - * @param data byte array containing instance data - * @return number of bytes written - */ - public void serialize(int offset, byte [] data) { - LittleEndian.putUShort(data, 0 + offset, _extBookIndex); - LittleEndian.putUShort(data, 2 + offset, _firstSheetIndex); - LittleEndian.putUShort(data, 4 + offset, _lastSheetIndex); - } - } - - - - public ExternSheetRecord() { - _list = new ArrayList(); - } - - /** - * Constructs a Extern Sheet record and sets its fields appropriately. - * @param in the RecordInputstream to read the record from - */ - - public ExternSheetRecord(RecordInputStream in) { - super(in); - } - - /** - * called by constructor, should throw runtime exception in the event of a - * record passed with a differing ID. - * - * @param id alleged id for this record - */ - protected void validateSid(short id) { - if (id != sid) { - throw new RecordFormatException("NOT An ExternSheet RECORD"); - } - } - - /** - * called by the constructor, should set class level fields. Should throw - * runtime exception for bad/icomplete data. - * - * @param in the RecordInputstream to read the record from - */ - protected void fillFields(RecordInputStream in) { - _list = new ArrayList(); - - int nItems = in.readShort(); - - for (int i = 0 ; i < nItems ; ++i) { - RefSubRecord rec = new RefSubRecord(in); - - _list.add( rec); - } - } - + /** index to External Book Block (which starts with a EXTERNALBOOK record) */ + private int _extBookIndex; + private int _firstSheetIndex; // may be -1 (0xFFFF) + private int _lastSheetIndex; // may be -1 (0xFFFF) + + + /** a Constructor for making new sub record + */ + public RefSubRecord(int extBookIndex, int firstSheetIndex, int lastSheetIndex) { + _extBookIndex = extBookIndex; + _firstSheetIndex = firstSheetIndex; + _lastSheetIndex = lastSheetIndex; + } + + /** + * @param in the RecordInputstream to read the record from + */ + public RefSubRecord(RecordInputStream in) { + this(in.readShort(), in.readShort(), in.readShort()); + } + public int getExtBookIndex(){ + return _extBookIndex; + } + public int getFirstSheetIndex(){ + return _firstSheetIndex; + } + public int getLastSheetIndex(){ + return _lastSheetIndex; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("extBook=").append(_extBookIndex); + buffer.append(" firstSheet=").append(_firstSheetIndex); + buffer.append(" lastSheet=").append(_lastSheetIndex); + return buffer.toString(); + } + + /** + * called by the class that is responsible for writing this sucker. + * Subclasses should implement this so that their data is passed back in a + * byte array. + * + * @param offset to begin writing at + * @param data byte array containing instance data + * @return number of bytes written + */ + public void serialize(int offset, byte [] data) { + LittleEndian.putUShort(data, 0 + offset, _extBookIndex); + LittleEndian.putUShort(data, 2 + offset, _firstSheetIndex); + LittleEndian.putUShort(data, 4 + offset, _lastSheetIndex); + } + } + + + + public ExternSheetRecord() { + _list = new ArrayList(); + } + + /** + * Constructs a Extern Sheet record and sets its fields appropriately. + * @param in the RecordInputstream to read the record from + */ + + public ExternSheetRecord(RecordInputStream in) { + super(in); + } + + /** + * called by constructor, should throw runtime exception in the event of a + * record passed with a differing ID. + * + * @param id alleged id for this record + */ + protected void validateSid(short id) { + if (id != sid) { + throw new RecordFormatException("NOT An ExternSheet RECORD"); + } + } + + /** + * called by the constructor, should set class level fields. Should throw + * runtime exception for bad/icomplete data. + * + * @param in the RecordInputstream to read the record from + */ + protected void fillFields(RecordInputStream in) { + _list = new ArrayList(); + + int nItems = in.readShort(); + + for (int i = 0 ; i < nItems ; ++i) { + RefSubRecord rec = new RefSubRecord(in); + + _list.add( rec); + } + } + - /** - * @return number of REF structures - */ - public int getNumOfRefs() { - return _list.size(); - } - - /** - * adds REF struct (ExternSheetSubRecord) - * @param rec REF struct - */ - public void addREFRecord(RefSubRecord rec) { - _list.add(rec); - } - - /** returns the number of REF Records, which is in model - * @return number of REF records - */ - public int getNumOfREFRecords() { - return _list.size(); - } - - - public String toString() { - StringBuffer sb = new StringBuffer(); - int nItems = _list.size(); - sb.append("[EXTERNSHEET]\n"); - sb.append(" numOfRefs = ").append(nItems).append("\n"); - for (int i=0; i < nItems; i++) { - sb.append("refrec #").append(i).append(": "); - sb.append(getRef(i).toString()); - sb.append('\n'); - } - sb.append("[/EXTERNSHEET]\n"); - - - return sb.toString(); - } - - - private int getDataSize() { - return 2 + _list.size() * RefSubRecord.ENCODED_SIZE; - } - - /** - * called by the class that is responsible for writing this sucker. - * Subclasses should implement this so that their data is passed back in a - * byte array. - * - * @param offset to begin writing at - * @param data byte array containing instance data - * @return number of bytes written - */ - public int serialize(int offset, byte [] data) { - int dataSize = getDataSize(); - - int nItems = _list.size(); + /** + * @return number of REF structures + */ + public int getNumOfRefs() { + return _list.size(); + } + + /** + * adds REF struct (ExternSheetSubRecord) + * @param rec REF struct + */ + public void addREFRecord(RefSubRecord rec) { + _list.add(rec); + } + + /** returns the number of REF Records, which is in model + * @return number of REF records + */ + public int getNumOfREFRecords() { + return _list.size(); + } + + + public String toString() { + StringBuffer sb = new StringBuffer(); + int nItems = _list.size(); + sb.append("[EXTERNSHEET]\n"); + sb.append(" numOfRefs = ").append(nItems).append("\n"); + for (int i=0; i < nItems; i++) { + sb.append("refrec #").append(i).append(": "); + sb.append(getRef(i).toString()); + sb.append('\n'); + } + sb.append("[/EXTERNSHEET]\n"); + + + return sb.toString(); + } + + + private int getDataSize() { + return 2 + _list.size() * RefSubRecord.ENCODED_SIZE; + } + + /** + * called by the class that is responsible for writing this sucker. + * Subclasses should implement this so that their data is passed back in a + * byte array. + * + * @param offset to begin writing at + * @param data byte array containing instance data + * @return number of bytes written + */ + public int serialize(int offset, byte [] data) { + int dataSize = getDataSize(); + + int nItems = _list.size(); - LittleEndian.putShort(data, 0 + offset, sid); + LittleEndian.putShort(data, 0 + offset, sid); LittleEndian.putUShort(data, 2 + offset, dataSize); - LittleEndian.putUShort(data, 4 + offset, nItems); - - int pos = 6 ; - - for (int i = 0; i < nItems; i++) { - getRef(i).serialize(offset + pos, data); - pos +=6; - } - return dataSize + 4; - } + LittleEndian.putUShort(data, 4 + offset, nItems); + + int pos = 6 ; + + for (int i = 0; i < nItems; i++) { + getRef(i).serialize(offset + pos, data); + pos +=6; + } + return dataSize + 4; + } private RefSubRecord getRef(int i) { return (RefSubRecord) _list.get(i); } - - public int getRecordSize() { - return 4 + getDataSize(); - } - - /** - * return the non static version of the id for this record. - */ - public short getSid() { - return sid; - } + + public int getRecordSize() { + return 4 + getDataSize(); + } + + /** + * return the non static version of the id for this record. + */ + public short getSid() { + return sid; + } public int getExtbookIndexFromRefIndex(int refIndex) { - return getRef(refIndex).getExtBookIndex(); + return getRef(refIndex).getExtBookIndex(); } /** * @return -1 if not found */ public int findRefIndexFromExtBookIndex(int extBookIndex) { - int nItems = _list.size(); - for (int i = 0; i < nItems; i++) { - if (getRef(i).getExtBookIndex() == extBookIndex) { - return i; - } - } + int nItems = _list.size(); + for (int i = 0; i < nItems; i++) { + if (getRef(i).getExtBookIndex() == extBookIndex) { + return i; + } + } return -1; } @@ -251,13 +251,25 @@ public class ExternSheetRecord extends Record { } public int getRefIxForSheet(int sheetIndex) { - int nItems = _list.size(); - for (int i = 0; i < nItems; i++) { - RefSubRecord ref = getRef(i); + int nItems = _list.size(); + for (int i = 0; i < nItems; i++) { + RefSubRecord ref = getRef(i); if (ref.getFirstSheetIndex() == sheetIndex && ref.getLastSheetIndex() == sheetIndex) { - return i; - } - } + return i; + } + } return -1; } + + public static ExternSheetRecord combine(ExternSheetRecord[] esrs) { + ExternSheetRecord result = new ExternSheetRecord(); + for (int i = 0; i < esrs.length; i++) { + ExternSheetRecord esr = esrs[i]; + int nRefs = esr.getNumOfREFRecords(); + for (int j=0; j