diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 4fdeaefb0..cc52e793b 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + 45046 - allowed EXTERNALBOOK(0x01AE) to be optional in the LinkTable 45066 - fixed sheet encoding size mismatch problems 45003 - Support embeded HDGF visio documents 45001 - Partial fix for HWPF Range.insertBefore() and Range.delete() with unicode characters diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index bc57530d2..85f5b8963 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 45046 - allowed EXTERNALBOOK(0x01AE) to be optional in the LinkTable 45066 - fixed sheet encoding size mismatch problems 45003 - Support embeded HDGF visio documents 45001 - Partial fix for HWPF Range.insertBefore() and Range.delete() with unicode characters diff --git a/src/java/org/apache/poi/hssf/model/LinkTable.java b/src/java/org/apache/poi/hssf/model/LinkTable.java index 88c94a61e..a19971b7d 100755 --- a/src/java/org/apache/poi/hssf/model/LinkTable.java +++ b/src/java/org/apache/poi/hssf/model/LinkTable.java @@ -41,7 +41,7 @@ import org.apache.poi.hssf.record.SupBookRecord; * * In BIFF8 the Link Table consists of *
    - *
  • one or more EXTERNALBOOK Blocks

    + *

  • zero or more EXTERNALBOOK Blocks

    * each consisting of *

      *
    • exactly one EXTERNALBOOK (0x01AE) record
    • @@ -55,7 +55,7 @@ import org.apache.poi.hssf.record.SupBookRecord; * *
    *
  • - *
  • exactly one EXTERNSHEET (0x0017) record
  • + *
  • zero or one EXTERNSHEET (0x0017) record
  • *
  • zero or more DEFINEDNAME (0x0018) records
  • *
* @@ -63,6 +63,7 @@ import org.apache.poi.hssf.record.SupBookRecord; * @author Josh Micich */ final class LinkTable { + // TODO make this class into a record aggregate private static final class CRNBlock { @@ -79,8 +80,8 @@ final class LinkTable { _crns = crns; } public CRNRecord[] getCrns() { - return (CRNRecord[]) _crns.clone(); - } + return (CRNRecord[]) _crns.clone(); + } } private static final class ExternalBookBlock { @@ -136,16 +137,19 @@ final class LinkTable { while(rs.peekNextClass() == SupBookRecord.class) { temp.add(new ExternalBookBlock(rs)); } - if(temp.size() < 1) { - throw new RuntimeException("Need at least one EXTERNALBOOK blocks"); - } + _externalBookBlocks = new ExternalBookBlock[temp.size()]; temp.toArray(_externalBookBlocks); temp.clear(); - - // If link table is present, there is always 1 of ExternSheetRecord - Record next = rs.getNext(); - _externSheetRecord = (ExternSheetRecord)next; + + if (_externalBookBlocks.length > 0) { + // If any ExternalBookBlock present, there is always 1 of ExternSheetRecord + Record next = rs.getNext(); + _externSheetRecord = (ExternSheetRecord) next; + } else { + _externSheetRecord = null; + } + _definedNames = new ArrayList(); // collect zero or more DEFINEDNAMEs id=0x18 while(rs.peekNextClass() == NameRecord.class) { @@ -222,7 +226,7 @@ final class LinkTable { public void addName(NameRecord name) { _definedNames.add(name); - // TODO - this is messy + // TODO - this is messy // Not the most efficient way but the other way was causing too many bugs int idx = findFirstRecordLocBySid(ExternSheetRecord.sid); if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid); @@ -242,8 +246,8 @@ final class LinkTable { public int getSheetIndexFromExternSheetIndex(int externSheetNumber) { if (externSheetNumber >= _externSheetRecord.getNumOfREFStructures()) { - return -1; - } + return -1; + } return _externSheetRecord.getREFRecordAt(externSheetNumber).getIndexToFirstSupBook(); } @@ -265,7 +269,7 @@ final class LinkTable { ExternSheetSubRecord esr = _externSheetRecord.getREFRecordAt(i); if (esr.getIndexToFirstSupBook() == sheetNumber - && esr.getIndexToLastSupBook() == sheetNumber){ + && esr.getIndexToLastSupBook() == sheetNumber){ return i; } } diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java index 08f226318..d87fc2c63 100644 --- a/src/java/org/apache/poi/hssf/model/Workbook.java +++ b/src/java/org/apache/poi/hssf/model/Workbook.java @@ -191,12 +191,11 @@ public class Workbook implements Model case ExternSheetRecord.sid : throw new RuntimeException("Extern sheet is part of LinkTable"); case NameRecord.sid : - throw new RuntimeException("DEFINEDNAME is part of LinkTable"); case SupBookRecord.sid : + // LinkTable can start with either of these if (log.check( POILogger.DEBUG )) log.log(DEBUG, "found SupBook record at " + k); retval.linkTable = new LinkTable(recs, k, retval.records); - // retval.records.supbookpos = k; k+=retval.linkTable.getRecordCount() - 1; continue; case FormatRecord.sid : diff --git a/src/testcases/org/apache/poi/hssf/data/ex45046-21984.xls b/src/testcases/org/apache/poi/hssf/data/ex45046-21984.xls new file mode 100644 index 000000000..96ef3ee7c Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/ex45046-21984.xls differ diff --git a/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java b/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java index 0e18e21e5..363e58c14 100755 --- a/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java @@ -28,7 +28,7 @@ import junit.framework.TestSuite; public class AllUserModelTests { public static Test suite() { - TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.usermodel"); + TestSuite result = new TestSuite(AllUserModelTests.class.getName()); result.addTestSuite(TestBugs.class); result.addTestSuite(TestCellStyle.class); @@ -58,6 +58,7 @@ public class AllUserModelTests { result.addTestSuite(TestHSSFSheetSetOrder.class); result.addTestSuite(TestHSSFTextbox.class); result.addTestSuite(TestHSSFWorkbook.class); + result.addTestSuite(TestLinkTable.class); result.addTestSuite(TestNamedRange.class); result.addTestSuite(TestOLE2Embeding.class); result.addTestSuite(TestPOIFSProperties.class); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestLinkTable.java b/src/testcases/org/apache/poi/hssf/usermodel/TestLinkTable.java new file mode 100644 index 000000000..7d1082e86 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestLinkTable.java @@ -0,0 +1,44 @@ +package org.apache.poi.hssf.usermodel; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +import org.apache.poi.hssf.HSSFTestDataSamples; +/** + * Tests for LinkTable + * + * @author Josh Micich + */ +public final class TestLinkTable extends TestCase { + + /** + * The example file attached to bugzilla 45046 is a clear example of Name records being present + * without an External Book (SupBook) record. Excel has no trouble reading this file.
+ * TODO get OOO documentation updated to reflect this (that EXTERNALBOOK is optional). + * + * It's not clear what exact steps need to be taken in Excel to create such a workbook + */ + public void testLinkTableWithoutExternalBookRecord_bug45046() { + HSSFWorkbook wb; + + try { + wb = HSSFTestDataSamples.openSampleWorkbook("ex45046-21984.xls"); + } catch (RuntimeException e) { + if ("DEFINEDNAME is part of LinkTable".equals(e.getMessage())) { + throw new AssertionFailedError("Identified bug 45046 b"); + } + throw e; + } + // some other sanity checks + assertEquals(3, wb.getNumberOfSheets()); + String formula = wb.getSheetAt(0).getRow(4).getCell(13).getCellFormula(); + + if ("ipcSummenproduktIntern($P5,N$6,$A$9,N$5)".equals(formula)) { + // The reported symptom of this bugzilla is an earlier bug (already fixed) + throw new AssertionFailedError("Identified bug 41726"); + // This is observable in version 3.0 + } + + assertEquals("ipcSummenproduktIntern($C5,N$2,$A$9,N$1)", formula); + } +}