From 77324268871d294c7d6c5c60efceb00e194afbd0 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Fri, 16 Jul 2010 16:02:09 +0000 Subject: [PATCH] Patch from Andrew Shirley from bug #49185 - Support for HSSFNames where the comment is stored in a NameCommentRecord git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@964845 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 1 + .../org/apache/poi/hssf/dev/BiffViewer.java | 1 + .../poi/hssf/model/InternalWorkbook.java | 43 ++++- .../org/apache/poi/hssf/model/LinkTable.java | 23 ++- .../poi/hssf/record/NameCommentRecord.java | 150 ++++++++++++++++++ .../apache/poi/hssf/record/RecordFactory.java | 1 + .../apache/poi/hssf/usermodel/HSSFName.java | 39 ++++- .../poi/hssf/usermodel/HSSFWorkbook.java | 6 +- .../apache/poi/hssf/model/TestLinkTable.java | 33 +++- .../hssf/record/TestNameCommentRecord.java | 42 +++++ .../apache/poi/hssf/usermodel/TestBugs.java | 27 ++++ test-data/spreadsheet/49185.xls | Bin 0 -> 16896 bytes 12 files changed, 354 insertions(+), 12 deletions(-) create mode 100644 src/java/org/apache/poi/hssf/record/NameCommentRecord.java create mode 100644 src/testcases/org/apache/poi/hssf/record/TestNameCommentRecord.java create mode 100644 test-data/spreadsheet/49185.xls diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 21336b6ae..b7b6e6c42 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 49185 - Support for HSSFNames where the comment is stored in a NameCommentRecord 49599 - Correct writing of NoteRecord author text when switching between ASCII and Unicode HWPF: Improve reading of auto-saved ("complex") documents Paragraph level as well as whole-file text extraction for Word 6/95 files through HWPF diff --git a/src/java/org/apache/poi/hssf/dev/BiffViewer.java b/src/java/org/apache/poi/hssf/dev/BiffViewer.java index ba79aa80d..ee6e159c4 100644 --- a/src/java/org/apache/poi/hssf/dev/BiffViewer.java +++ b/src/java/org/apache/poi/hssf/dev/BiffViewer.java @@ -197,6 +197,7 @@ public final class BiffViewer { case MulBlankRecord.sid: return new MulBlankRecord(in); case MulRKRecord.sid: return new MulRKRecord(in); case NameRecord.sid: return new NameRecord(in); + case NameCommentRecord.sid: return new NameCommentRecord(in); case NoteRecord.sid: return new NoteRecord(in); case NumberRecord.sid: return new NumberRecord(in); case ObjRecord.sid: return new ObjRecord(in); diff --git a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java index 567b986e7..f2719bba5 100644 --- a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java +++ b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java @@ -20,8 +20,11 @@ package org.apache.poi.hssf.model; import java.security.AccessControlException; import java.util.ArrayList; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; import org.apache.poi.ddf.EscherBSERecord; import org.apache.poi.ddf.EscherBoolProperty; @@ -57,6 +60,7 @@ import org.apache.poi.hssf.record.HyperlinkRecord; import org.apache.poi.hssf.record.InterfaceEndRecord; import org.apache.poi.hssf.record.InterfaceHdrRecord; import org.apache.poi.hssf.record.MMSRecord; +import org.apache.poi.hssf.record.NameCommentRecord; import org.apache.poi.hssf.record.NameRecord; import org.apache.poi.hssf.record.PaletteRecord; import org.apache.poi.hssf.record.PasswordRecord; @@ -165,6 +169,11 @@ public final class InternalWorkbook { private WriteAccessRecord writeAccess; private WriteProtectRecord writeProtect; + /** + * Hold the {@link NameCommentRecord}s indexed by the name of the {@link NameRecord} to which they apply. + */ + private final Map commentRecords; + private InternalWorkbook() { records = new WorkbookRecordList(); @@ -176,6 +185,7 @@ public final class InternalWorkbook { maxformatid = -1; uses1904datewindowing = false; escherBSERecords = new ArrayList(); + commentRecords = new LinkedHashMap(); } /** @@ -261,7 +271,7 @@ public final class InternalWorkbook { // 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.linkTable = new LinkTable(recs, k, retval.records, retval.commentRecords); k+=retval.linkTable.getRecordCount() - 1; continue; case FormatRecord.sid : @@ -299,6 +309,13 @@ public final class InternalWorkbook { if (log.check( POILogger.DEBUG )) log.log(DEBUG, "found FileSharing at " + k); retval.fileShare = (FileSharingRecord) rec; + break; + + case NameCommentRecord.sid: + final NameCommentRecord ncr = (NameCommentRecord) rec; + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found NameComment at " + k); + retval.commentRecords.put(ncr.getNameText(), ncr); default : } records.add(rec); @@ -1823,6 +1840,14 @@ public final class InternalWorkbook { return linkTable.getNameRecord(index); } + /** gets the name comment record + * @param nameRecord name record who's comment is required. + * @return name comment record or null if there isn't one for the given name. + */ + public NameCommentRecord getNameCommentRecord(final NameRecord nameRecord){ + return commentRecords.get(nameRecord.getNameText()); + } + /** creates new name * @return new name record */ @@ -1880,6 +1905,22 @@ public final class InternalWorkbook { } } + /** + * If a {@link NameCommentRecord} is added or the name it references + * is renamed, then this will update the lookup cache for it. + */ + public void updateNameCommentRecordCache(final NameCommentRecord commentRecord) { + if(commentRecords.containsValue(commentRecord)) { + for(Entry entry : commentRecords.entrySet()) { + if(entry.getValue().equals(commentRecord)) { + commentRecords.remove(entry.getKey()); + break; + } + } + } + commentRecords.put(commentRecord.getNameText(), commentRecord); + } + /** * Returns a format index that matches the passed in format. It does not tie into HSSFDataFormat. * @param format the format string diff --git a/src/java/org/apache/poi/hssf/model/LinkTable.java b/src/java/org/apache/poi/hssf/model/LinkTable.java index 10a6b3063..fd3b7a6e6 100644 --- a/src/java/org/apache/poi/hssf/model/LinkTable.java +++ b/src/java/org/apache/poi/hssf/model/LinkTable.java @@ -20,12 +20,14 @@ package org.apache.poi.hssf.model; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; import org.apache.poi.hssf.record.CRNCountRecord; import org.apache.poi.hssf.record.CRNRecord; import org.apache.poi.hssf.record.CountryRecord; import org.apache.poi.hssf.record.ExternSheetRecord; import org.apache.poi.hssf.record.ExternalNameRecord; +import org.apache.poi.hssf.record.NameCommentRecord; import org.apache.poi.hssf.record.NameRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.SupBookRecord; @@ -149,7 +151,7 @@ final class LinkTable { private final int _recordCount; private final WorkbookRecordList _workbookRecordList; // TODO - would be nice to remove this - public LinkTable(List inputList, int startIndex, WorkbookRecordList workbookRecordList) { + public LinkTable(List inputList, int startIndex, WorkbookRecordList workbookRecordList, Map commentRecords) { _workbookRecordList = workbookRecordList; RecordStream rs = new RecordStream(inputList, startIndex); @@ -176,10 +178,21 @@ final class LinkTable { } _definedNames = new ArrayList(); - // collect zero or more DEFINEDNAMEs id=0x18 - while(rs.peekNextClass() == NameRecord.class) { - NameRecord nr = (NameRecord)rs.getNext(); - _definedNames.add(nr); + // collect zero or more DEFINEDNAMEs id=0x18, + // with their comments if present + while(true) { + Class nextClass = rs.peekNextClass(); + if (nextClass == NameRecord.class) { + NameRecord nr = (NameRecord)rs.getNext(); + _definedNames.add(nr); + } + else if (nextClass == NameCommentRecord.class) { + NameCommentRecord ncr = (NameCommentRecord)rs.getNext(); + commentRecords.put(ncr.getNameText(), ncr); + } + else { + break; + } } _recordCount = rs.getCountRead(); diff --git a/src/java/org/apache/poi/hssf/record/NameCommentRecord.java b/src/java/org/apache/poi/hssf/record/NameCommentRecord.java new file mode 100644 index 000000000..36d6132d0 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/NameCommentRecord.java @@ -0,0 +1,150 @@ +/* ==================================================================== + 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.record; + +import org.apache.poi.util.HexDump; +import org.apache.poi.util.LittleEndianInput; +import org.apache.poi.util.LittleEndianOutput; +import org.apache.poi.util.StringUtil; + +/** + * Title: NAMECMT Record (0x0894) + *

+ * Description: Defines a comment associated with a specified name. + *

+ * REFERENCE: + *

+ * + * @author Andrew Shirley (aks at corefiling.co.uk) + */ +public final class NameCommentRecord extends StandardRecord { + public final static short sid = 0x0894; + + private final short field_1_record_type; + private final short field_2_frt_cell_ref_flag; + private final long field_3_reserved; + //private short field_4_name_length; + //private short field_5_comment_length; + private String field_6_name_text; + private String field_7_comment_text; + + public NameCommentRecord(final String name, final String comment) { + field_1_record_type = 0; + field_2_frt_cell_ref_flag = 0; + field_3_reserved = 0; + field_6_name_text = name; + field_7_comment_text = comment; + } + + @Override + public void serialize(final LittleEndianOutput out) { + final int field_4_name_length = field_6_name_text.length(); + final int field_5_comment_length = field_7_comment_text.length(); + + out.writeShort(field_1_record_type); + out.writeShort(field_2_frt_cell_ref_flag); + out.writeLong(field_3_reserved); + out.writeShort(field_4_name_length); + out.writeShort(field_5_comment_length); + + out.writeByte(0); + out.write(field_6_name_text.getBytes()); + out.writeByte(0); + out.write(field_7_comment_text.getBytes()); + } + + @Override + protected int getDataSize() { + return 18 // 4 shorts + 1 long + 2 spurious 'nul's + + field_6_name_text.length() + + field_7_comment_text.length(); + } + + /** + * @param ris the RecordInputstream to read the record from + */ + public NameCommentRecord(final RecordInputStream ris) { + final LittleEndianInput in = ris; + field_1_record_type = in.readShort(); + field_2_frt_cell_ref_flag = in.readShort(); + field_3_reserved = in.readLong(); + final int field_4_name_length = in.readShort(); + final int field_5_comment_length = in.readShort(); + + in.readByte(); //spurious NUL + field_6_name_text = StringUtil.readCompressedUnicode(in, field_4_name_length); + in.readByte(); //spurious NUL + field_7_comment_text = StringUtil.readCompressedUnicode(in, field_5_comment_length); + } + + /** + * return the non static version of the id for this record. + */ + @Override + public short getSid() { + return sid; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer(); + + sb.append("[NAMECMT]\n"); + sb.append(" .record type = ").append(HexDump.shortToHex(field_1_record_type)).append("\n"); + sb.append(" .frt cell ref flag = ").append(HexDump.byteToHex(field_2_frt_cell_ref_flag)).append("\n"); + sb.append(" .reserved = ").append(field_3_reserved).append("\n"); + sb.append(" .name length = ").append(field_6_name_text.length()).append("\n"); + sb.append(" .comment length = ").append(field_7_comment_text.length()).append("\n"); + sb.append(" .name = ").append(field_6_name_text).append("\n"); + sb.append(" .comment = ").append(field_7_comment_text).append("\n"); + sb.append("[/NAMECMT]\n"); + + return sb.toString(); + } + + /** + * @return the name of the NameRecord to which this comment applies. + */ + public String getNameText() { + return field_6_name_text; + } + + /** + * Updates the name we're associated with, normally used + * when renaming that Name + */ + public void setNameText(String newName) { + field_6_name_text = newName; + } + + /** + * @return the text of the comment. + */ + public String getCommentText() { + return field_7_comment_text; + } + + public void setCommentText(String comment) { + field_7_comment_text = comment; + } + + public short getRecordType() { + return field_1_record_type; + } + +} diff --git a/src/java/org/apache/poi/hssf/record/RecordFactory.java b/src/java/org/apache/poi/hssf/record/RecordFactory.java index e99534ce6..07db07133 100644 --- a/src/java/org/apache/poi/hssf/record/RecordFactory.java +++ b/src/java/org/apache/poi/hssf/record/RecordFactory.java @@ -173,6 +173,7 @@ public final class RecordFactory { MulBlankRecord.class, MulRKRecord.class, NameRecord.class, + NameCommentRecord.class, NoteRecord.class, NumberRecord.class, ObjectProtectRecord.class, diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java index 579d3494e..e88a672be 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFName.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFName.java @@ -19,6 +19,7 @@ package org.apache.poi.hssf.usermodel; import org.apache.poi.hssf.model.HSSFFormulaParser; import org.apache.poi.hssf.model.InternalWorkbook; +import org.apache.poi.hssf.record.NameCommentRecord; import org.apache.poi.hssf.record.NameRecord; import org.apache.poi.hssf.record.formula.Ptg; import org.apache.poi.ss.formula.FormulaType; @@ -33,8 +34,10 @@ import org.apache.poi.ss.usermodel.Name; public final class HSSFName implements Name { private HSSFWorkbook _book; private NameRecord _definedNameRec; + private NameCommentRecord _commentRec; - /** Creates new HSSFName - called by HSSFWorkbook to create a sheet from + /** + * Creates new HSSFName - called by HSSFWorkbook to create a name from * scratch. * * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createName() @@ -42,8 +45,21 @@ public final class HSSFName implements Name { * @param book workbook object associated with the sheet. */ /* package */ HSSFName(HSSFWorkbook book, NameRecord name) { + this(book, name, null); + } + /** + * Creates new HSSFName - called by HSSFWorkbook to create a name from + * scratch. + * + * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#createName() + * @param name the Name Record + * @param comment the Name Comment Record, optional. + * @param book workbook object associated with the sheet. + */ + /* package */ HSSFName(final HSSFWorkbook book, final NameRecord name, final NameCommentRecord comment) { _book = book; _definedNameRec = name; + _commentRec = comment; } /** Get the sheets name which this named range is referenced to @@ -131,6 +147,13 @@ public final class HSSFName implements Name { } } } + + // Update our comment, if there is one + if(_commentRec != null) { + String oldName = _commentRec.getNameText(); + _commentRec.setNameText(nameName); + _book.getWorkbook().updateNameCommentRecordCache(_commentRec); + } } private static void validateName(String name){ @@ -230,7 +253,14 @@ public final class HSSFName implements Name { * * @return the user comment for this named range */ - public String getComment(){ + public String getComment() { + if(_commentRec != null) { + // Prefer the comment record if it has text in it + if(_commentRec.getCommentText() != null && + _commentRec.getCommentText().length() > 0) { + return _commentRec.getCommentText(); + } + } return _definedNameRec.getDescriptionText(); } @@ -240,7 +270,12 @@ public final class HSSFName implements Name { * @param comment the user comment for this named range */ public void setComment(String comment){ + // Update the main record _definedNameRec.setDescriptionText(comment); + // If we have a comment record too, update that as well + if(_commentRec != null) { + _commentRec.setCommentText(comment); + } } /** diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 141f1a9db..de1cb5055 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -67,7 +67,6 @@ import org.apache.poi.hssf.util.CellReference; import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.CreationHelper; -import org.apache.poi.ss.usermodel.PictureData; import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.formula.FormulaType; import org.apache.poi.util.POILogFactory; @@ -276,7 +275,8 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss } for (int i = 0 ; i < workbook.getNumNames() ; ++i){ - HSSFName name = new HSSFName(this, workbook.getNameRecord(i)); + NameRecord nameRecord = workbook.getNameRecord(i); + HSSFName name = new HSSFName(this, nameRecord, workbook.getNameCommentRecord(nameRecord)); names.add(name); } } @@ -970,7 +970,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss if (isNewRecord) { - HSSFName newName = new HSSFName(this, nameRecord); + HSSFName newName = new HSSFName(this, nameRecord, nameRecord.isBuiltInName() ? null : workbook.getNameCommentRecord(nameRecord)); names.add(newName); } diff --git a/src/testcases/org/apache/poi/hssf/model/TestLinkTable.java b/src/testcases/org/apache/poi/hssf/model/TestLinkTable.java index f88cd4860..72097b2a6 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestLinkTable.java +++ b/src/testcases/org/apache/poi/hssf/model/TestLinkTable.java @@ -18,12 +18,17 @@ package org.apache.poi.hssf.model; import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.record.NameCommentRecord; +import org.apache.poi.hssf.record.NameRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.SSTRecord; import org.apache.poi.hssf.record.SupBookRecord; @@ -138,7 +143,7 @@ public final class TestLinkTable extends TestCase { LinkTable lt; try { - lt = new LinkTable(recList, 0, wrl); + lt = new LinkTable(recList, 0, wrl, Collections.emptyMap()); } 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"); @@ -148,4 +153,30 @@ public final class TestLinkTable extends TestCase { } assertNotNull(lt); } + + /** + * + */ + public void testNameCommentRecordBetweenNameRecords() { + + final Record[] recs = { + new NameRecord(), + new NameCommentRecord("name1", "comment1"), + new NameRecord(), + new NameCommentRecord("name2", "comment2"), + + }; + final List recList = Arrays.asList(recs); + final WorkbookRecordList wrl = new WorkbookRecordList(); + final Map commentRecords = new LinkedHashMap(); + + final LinkTable lt = new LinkTable(recList, 0, wrl, commentRecords); + assertNotNull(lt); + + assertEquals(2, commentRecords.size()); + assertTrue(recs[1] == commentRecords.get("name1")); //== is intentionally not .equals()! + assertTrue(recs[3] == commentRecords.get("name2")); //== is intentionally not .equals()! + + assertEquals(2, lt.getNumNames()); + } } diff --git a/src/testcases/org/apache/poi/hssf/record/TestNameCommentRecord.java b/src/testcases/org/apache/poi/hssf/record/TestNameCommentRecord.java new file mode 100644 index 000000000..f7a29eb46 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/TestNameCommentRecord.java @@ -0,0 +1,42 @@ +/* ==================================================================== + 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.record; + +import junit.framework.TestCase; + +import org.apache.poi.util.HexRead; + +/** + * Tests the NameCommentRecord serializes/deserializes correctly + * + * @author Andrew Shirley (aks at corefiling.co.uk) + */ +public final class TestNameCommentRecord extends TestCase { + public void testReserialize() { + final byte[] data = HexRead + .readFromString("" + + "94 08 00 00 00 00 00 00 00 00 00 00 04 00 07 00 00 6E 61 6D 65 00 63 6F 6D 6D 65 6E 74]"); + final RecordInputStream in = TestcaseRecordInputStream.create(NameCommentRecord.sid, data); + final NameCommentRecord ncr = new NameCommentRecord(in); + assertEquals(0x0894, ncr.getRecordType()); + assertEquals("name", ncr.getNameText()); + assertEquals("comment", ncr.getCommentText()); + final byte[] data2 = ncr.serialize(); + TestcaseRecordInputStream.confirmRecordEncoding(NameCommentRecord.sid, data, data2); + } +} diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index 5901f5312..706ccbfa0 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -1734,4 +1734,31 @@ if(1==2) { assertEquals(234.0, row.getCell(1).getNumericCellValue()); } } + + /** + * Test for a file with NameRecord with NameCommentRecord comments + */ + public void test49185() throws Exception { + HSSFWorkbook wb = openSample("49185.xls"); + Name name = wb.getName("foobarName"); + assertEquals("This is a comment", name.getComment()); + + // Rename the name, comment comes with it + name.setNameName("ChangedName"); + assertEquals("This is a comment", name.getComment()); + + // Save and re-check + wb = writeOutAndReadBack(wb); + name = wb.getName("ChangedName"); + assertEquals("This is a comment", name.getComment()); + + // Now try to change it + name.setComment("Changed Comment"); + assertEquals("Changed Comment", name.getComment()); + + // Save and re-check + wb = writeOutAndReadBack(wb); + name = wb.getName("ChangedName"); + assertEquals("Changed Comment", name.getComment()); + } } diff --git a/test-data/spreadsheet/49185.xls b/test-data/spreadsheet/49185.xls new file mode 100644 index 0000000000000000000000000000000000000000..f40a837899a0acf8c9afb9e34f2017c131d2ec60 GIT binary patch literal 16896 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