<!-- Don't forget to update status.xml too! -->
<release version="3.5-beta6" date="2009-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">47001 - Fixed WriteAccessRecord and LinkTable to handle unusual format written by Google Docs</action>
<action dev="POI-DEVELOPERS" type="fix">46973 - Fixed defined names to behave better when refersToFormula is unset</action>
<action dev="POI-DEVELOPERS" type="fix">46832 - Allow merged regions with columns greater than 255 or rows bigger than 65536 in XSSF</action>
<action dev="POI-DEVELOPERS" type="fix">46951 - Fixed formula parser to better handle range operators and whole row/column refs.</action>
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.5-beta6" date="2009-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">47001 - Fixed WriteAccessRecord and LinkTable to handle unusual format written by Google Docs</action>
<action dev="POI-DEVELOPERS" type="fix">46973 - Fixed defined names to behave better when refersToFormula is unset</action>
<action dev="POI-DEVELOPERS" type="fix">46832 - Allow merged regions with columns greater than 255 or rows bigger than 65536 in XSSF</action>
<action dev="POI-DEVELOPERS" type="fix">46951 - Fixed formula parser to better handle range operators and whole row/column refs.</action>
if (_externalBookBlocks.length > 0) {
// If any ExternalBookBlock present, there is always 1 of ExternSheetRecord
- _externSheetRecord = readExtSheetRecord(rs);
+ if (rs.peekNextClass() != ExternSheetRecord.class) {
+ // not quite - if written by google docs
+ _externSheetRecord = null;
+ } else {
+ _externSheetRecord = readExtSheetRecord(rs);
+ }
} else {
_externSheetRecord = null;
}
import java.util.Arrays;
+import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianOutput;
import org.apache.poi.util.StringUtil;
private static final int DATA_SIZE = 112;
private String field_1_username;
/** this record is always padded to a constant length */
- private byte[] padding;
+ private static final byte[] PADDING = new byte[DATA_SIZE];
+ static {
+ Arrays.fill(PADDING, PAD_CHAR);
+ }
public WriteAccessRecord() {
setUsername("");
- padding = new byte[DATA_SIZE - 3];
}
public WriteAccessRecord(RecordInputStream in) {
int nChars = in.readUShort();
int is16BitFlag = in.readUByte();
- int expectedPadSize = DATA_SIZE - 3;
+ if (nChars > DATA_SIZE || (is16BitFlag & 0xFE) != 0) {
+ // String header looks wrong (probably missing)
+ // OOO doc says this is optional anyway.
+ // reconstruct data
+ byte[] data = new byte[3 + in.remaining()];
+ LittleEndian.putUShort(data, 0, nChars);
+ LittleEndian.putByte(data, 2, is16BitFlag);
+ in.readFully(data, 3, data.length-3);
+ String rawValue = new String(data);
+ setUsername(rawValue.trim());
+ return;
+ }
+
+ String rawText;
if ((is16BitFlag & 0x01) == 0x00) {
- field_1_username = StringUtil.readCompressedUnicode(in, nChars);
- expectedPadSize -= nChars;
+ rawText = StringUtil.readCompressedUnicode(in, nChars);
} else {
- field_1_username = StringUtil.readUnicodeLE(in, nChars);
- expectedPadSize -= nChars * 2;
+ rawText = StringUtil.readUnicodeLE(in, nChars);
}
- padding = new byte[expectedPadSize];
+ field_1_username = rawText.trim();
+
+ // consume padding
int padSize = in.remaining();
- in.readFully(padding, 0, padSize);
- if (padSize < expectedPadSize) {
- // this occurs in a couple of test examples: "42564.xls",
- // "bug_42794.xls"
- Arrays.fill(padding, padSize, expectedPadSize, PAD_CHAR);
+ while (padSize > 0) {
+ // in some cases this seems to be garbage (non spaces)
+ in.readUByte();
+ padSize--;
}
}
if (paddingSize < 0) {
throw new IllegalArgumentException("Name is too long: " + username);
}
- padding = new byte[paddingSize];
- Arrays.fill(padding, PAD_CHAR);
field_1_username = username;
}
StringBuffer buffer = new StringBuffer();
buffer.append("[WRITEACCESS]\n");
- buffer.append(" .name = ").append(field_1_username.toString()).append("\n");
+ buffer.append(" .name = ").append(field_1_username.toString()).append("\n");
buffer.append("[/WRITEACCESS]\n");
return buffer.toString();
}
} else {
StringUtil.putCompressedUnicode(username, out);
}
- out.write(padding);
+ int encodedByteCount = 3 + username.length() * (is16bit ? 2 : 1);
+ int paddingSize = DATA_SIZE - encodedByteCount;
+ out.write(PADDING, 0, paddingSize);
}
protected int getDataSize() {
result.addTestSuite(TestFormulaParser.class);
result.addTestSuite(TestFormulaParserEval.class);
result.addTestSuite(TestFormulaParserIf.class);
+ result.addTestSuite(TestLinkTable.class);
result.addTestSuite(TestOperandClassTransformer.class);
result.addTestSuite(TestRowBlocksReader.class);
result.addTestSuite(TestRVA.class);
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.model;
+
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.SSTRecord;
+import org.apache.poi.hssf.record.SupBookRecord;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+/**
+ * Tests for {@link 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.<br/>
+ * 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);
+ }
+
+ public void testMultipleExternSheetRecords_bug45698() {
+ HSSFWorkbook wb;
+
+ try {
+ wb = HSSFTestDataSamples.openSampleWorkbook("ex45698-22488.xls");
+ } catch (RuntimeException e) {
+ if ("Extern sheet is part of LinkTable".equals(e.getMessage())) {
+ throw new AssertionFailedError("Identified bug 45698");
+ }
+ throw e;
+ }
+ // some other sanity checks
+ assertEquals(7, wb.getNumberOfSheets());
+ }
+
+ public void testExtraSheetRefs_bug45978() {
+ HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex45978-extraLinkTableSheets.xls");
+ /*
+ ex45978-extraLinkTableSheets.xls is a cut-down version of attachment 22561.
+ The original file produces the same error.
+
+ This bug was caused by a combination of invalid sheet indexes in the EXTERNSHEET
+ record, and eager initialisation of the extern sheet references. Note - the workbook
+ has 2 sheets, but the EXTERNSHEET record refers to sheet indexes 0, 1 and 2.
+
+ Offset 0x3954 (14676)
+ recordid = 0x17, size = 32
+ [EXTERNSHEET]
+ numOfRefs = 5
+ refrec #0: extBook=0 firstSheet=0 lastSheet=0
+ refrec #1: extBook=1 firstSheet=2 lastSheet=2
+ refrec #2: extBook=2 firstSheet=1 lastSheet=1
+ refrec #3: extBook=0 firstSheet=-1 lastSheet=-1
+ refrec #4: extBook=0 firstSheet=1 lastSheet=1
+ [/EXTERNSHEET]
+
+ As it turns out, the formula in question doesn't even use externSheetIndex #1 - it
+ uses #4, which resolves to sheetIndex 1 -> 'Data'.
+
+ It is not clear exactly what externSheetIndex #4 would refer to. Excel seems to
+ display such a formula as "''!$A2", but then complains of broken link errors.
+ */
+
+ HSSFCell cell = wb.getSheetAt(0).getRow(1).getCell(1);
+ String cellFormula;
+ try {
+ cellFormula = cell.getCellFormula();
+ } catch (IndexOutOfBoundsException e) {
+ if (e.getMessage().equals("Index: 2, Size: 2")) {
+ throw new AssertionFailedError("Identified bug 45798");
+ }
+ throw e;
+ }
+ assertEquals("Data!$A2", cellFormula);
+ }
+
+ /**
+ * This problem was visible in POI svn r763332
+ * when reading the workbook of attachment 23468 from bugzilla 47001
+ */
+ public void testMissingExternSheetRecord_bug47001b() {
+
+ Record[] recs = {
+ SupBookRecord.createAddInFunctions(),
+ new SSTRecord(),
+ };
+ List<Record> recList = Arrays.asList(recs);
+ WorkbookRecordList wrl = new WorkbookRecordList();
+
+ LinkTable lt;
+ try {
+ lt = new LinkTable(recList, 0, wrl);
+ } catch (RuntimeException e) {
+ if (e.getMessage().equals("Expected an EXTERNSHEET record but got (org.apache.poi.hssf.record.SSTRecord)")) {
+ throw new AssertionFailedError("Identified bug 47001b");
+ }
+
+ throw e;
+ }
+ assertNotNull(lt);
+ }
+}
result.addTestSuite(TestTextObjectRecord.class);
result.addTestSuite(TestUnicodeNameRecord.class);
result.addTestSuite(TestUnicodeString.class);
+ result.addTestSuite(TestWriteAccessRecord.class);
result.addTestSuite(TestCellRange.class);
result.addTestSuite(TestConstantValueParser.class);
return result;
--- /dev/null
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+ \r
+package org.apache.poi.hssf.record;\r
+\r
+import junit.framework.AssertionFailedError;\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.util.HexRead;\r
+\r
+/**\r
+ * Tests for {@link WriteAccessRecord}\r
+ *\r
+ * @author Josh Micich\r
+ */\r
+public final class TestWriteAccessRecord extends TestCase {\r
+ \r
+ private static final String HEX_SIXTYFOUR_SPACES = ""\r
+ + "20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 "\r
+ + "20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 "\r
+ + "20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 "\r
+ + "20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20";\r
+ \r
+ \r
+ public void testMissingStringHeader_bug47001a() {\r
+ /*\r
+ * Data taken from offset 0x0224 in\r
+ * attachment 23468 from bugzilla 47001\r
+ */\r
+ byte[] data = HexRead.readFromString(""\r
+ + "5C 00 70 00 "\r
+ + "4A 61 76 61 20 45 78 63 65 6C 20 41 50 49 20 76 "\r
+ + "32 2E 36 2E 34"\r
+ + "20 20 20 20 20 20 20 20 20 20 20 "\r
+ + "20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 "\r
+ + HEX_SIXTYFOUR_SPACES);\r
+\r
+ RecordInputStream in = TestcaseRecordInputStream.create(data);\r
+\r
+ WriteAccessRecord rec;\r
+ try {\r
+ rec = new WriteAccessRecord(in);\r
+ } catch (RecordFormatException e) {\r
+ if (e.getMessage().equals("Not enough data (0) to read requested (1) bytes")) {\r
+ throw new AssertionFailedError("Identified bug 47001a");\r
+ }\r
+ throw e;\r
+ }\r
+ assertEquals("Java Excel API v2.6.4", rec.getUsername());\r
+ \r
+ \r
+ byte[] expectedEncoding = HexRead.readFromString(""\r
+ + "15 00 00 4A 61 76 61 20 45 78 63 65 6C 20 41 50 "\r
+ + "49 20 76 32 2E 36 2E 34"\r
+ + "20 20 20 20 20 20 20 20 "\r
+ + "20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 "\r
+ + HEX_SIXTYFOUR_SPACES);\r
+ \r
+ TestcaseRecordInputStream.confirmRecordEncoding(WriteAccessRecord.sid, expectedEncoding, rec.serialize());\r
+ }\r
+ \r
+ public void testShortRecordWrittenByMSAccess() {\r
+ /*\r
+ * Data taken from two example files\r
+ * ex42564-21435.xls \r
+ * bug_42794.xls (from bug 42794 attachment 20429)\r
+ * In both cases, this data is found at offset 0x0C1C.\r
+ */\r
+ byte[] data = HexRead.readFromString(""\r
+ + "5C 00 39 00 "\r
+ + "36 00 00 41 20 73 61 74 69 73 66 69 65 64 20 4D "\r
+ + "69 63 72 6F 73 6F 66 74 20 4F 66 66 69 63 65 39 "\r
+ + "20 55 73 65 72"\r
+ + "20 20 20 20 20 20 20 20 20 20 20 "\r
+ + "20 20 20 20 20 20 20 20 20");\r
+\r
+ RecordInputStream in = TestcaseRecordInputStream.create(data);\r
+ WriteAccessRecord rec = new WriteAccessRecord(in);\r
+ assertEquals("A satisfied Microsoft Office9 User", rec.getUsername());\r
+ byte[] expectedEncoding = HexRead.readFromString(""\r
+ + "22 00 00 41 20 73 61 74 69 73 66 69 65 64 20 4D "\r
+ + "69 63 72 6F 73 6F 66 74 20 4F 66 66 69 63 65 39 "\r
+ + "20 55 73 65 72"\r
+ + "20 20 20 20 20 20 20 20 20 20 20 "\r
+ + HEX_SIXTYFOUR_SPACES);\r
+ \r
+ TestcaseRecordInputStream.confirmRecordEncoding(WriteAccessRecord.sid, expectedEncoding, rec.serialize());\r
+ }\r
+}\r
result.addTestSuite(TestHSSFSheet.class);
result.addTestSuite(TestHSSFTextbox.class);
result.addTestSuite(TestHSSFWorkbook.class);
- result.addTestSuite(TestLinkTable.class);
result.addTestSuite(TestHSSFName.class);
result.addTestSuite(TestOLE2Embeding.class);
result.addTestSuite(TestPOIFSProperties.class);
* when reading the BOFRecord
*/
public void test42564() {
- HSSFWorkbook wb = openSample("42564.xls");
+ HSSFWorkbook wb = openSample("ex42564-21435.xls");
writeOutAndReadBack(wb);
}
* issue.
*/
public void test42564Alt() {
- HSSFWorkbook wb = openSample("42564-2.xls");
+ HSSFWorkbook wb = openSample("ex42564-21503.xls");
writeOutAndReadBack(wb);
}
+++ /dev/null
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-
-package org.apache.poi.hssf.usermodel;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.TestCase;
-
-import org.apache.poi.hssf.HSSFTestDataSamples;
-/**
- * Tests for {@link 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.<br/>
- * 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);
- }
-
- public void testMultipleExternSheetRecords_bug45698() {
- HSSFWorkbook wb;
-
- try {
- wb = HSSFTestDataSamples.openSampleWorkbook("ex45698-22488.xls");
- } catch (RuntimeException e) {
- if ("Extern sheet is part of LinkTable".equals(e.getMessage())) {
- throw new AssertionFailedError("Identified bug 45698");
- }
- throw e;
- }
- // some other sanity checks
- assertEquals(7, wb.getNumberOfSheets());
- }
-
- public void testExtraSheetRefs_bug45978() {
- HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex45978-extraLinkTableSheets.xls");
- /*
- ex45978-extraLinkTableSheets.xls is a cut-down version of attachment 22561.
- The original file produces the same error.
-
- This bug was caused by a combination of invalid sheet indexes in the EXTERNSHEET
- record, and eager initialisation of the extern sheet references. Note - the worbook
- has 2 sheets, but the EXTERNSHEET record refers to sheet indexes 0, 1 and 2.
-
- Offset 0x3954 (14676)
- recordid = 0x17, size = 32
- [EXTERNSHEET]
- numOfRefs = 5
- refrec #0: extBook=0 firstSheet=0 lastSheet=0
- refrec #1: extBook=1 firstSheet=2 lastSheet=2
- refrec #2: extBook=2 firstSheet=1 lastSheet=1
- refrec #3: extBook=0 firstSheet=-1 lastSheet=-1
- refrec #4: extBook=0 firstSheet=1 lastSheet=1
- [/EXTERNSHEET]
-
- As it turns out, the formula in question doesn't even use externSheetIndex #1 - it
- uses #4, which resolves to sheetIndex 1 -> 'Data'.
-
- It is not clear exactly what externSheetIndex #4 would refer to. Excel seems to
- display such a formula as "''!$A2", but then complains of broken link errors.
- */
-
- HSSFCell cell = wb.getSheetAt(0).getRow(1).getCell(1);
- String cellFormula;
- try {
- cellFormula = cell.getCellFormula();
- } catch (IndexOutOfBoundsException e) {
- if (e.getMessage().equals("Index: 2, Size: 2")) {
- throw new AssertionFailedError("Identified bug 45798");
- }
- throw e;
- }
- assertEquals("Data!$A2", cellFormula);
- }
-}