<changes>
<release version="3.7-SNAPSHOT" date="2010-??-??">
+ <action dev="POI-DEVELOPERS" type="add">added Ant target to install artifacts in local repository </action>
+ <action dev="POI-DEVELOPERS" type="fix">48026 - fixed PageSettingsBlock to allow multiple HeaderFooterRecord records </action>
+ <action dev="POI-DEVELOPERS" type="fix">48202 - fixed CellRangeUtil.mergeCellRanges to work for adjacent cell regions </action>
<action dev="POI-DEVELOPERS" type="fix">48339 - fixed ExternalNameRecord to properly distinguish DDE data from OLE data items </action>
<action dev="POI-DEVELOPERS" type="fix">47920 - allow editing workbooks embedded into PowerPoint files</action>
<action dev="POI-DEVELOPERS" type="add">48343 - added implementation of SUBTOTAL function</action>
// one or more PSB records found after some intervening non-PSB records
_psBlock.addLateRecords(rs);
}
+ // YK: in some cases records can be moved to the preceding
+ // CustomViewSettingsRecordAggregate blocks
+ _psBlock.positionRecords(records);
continue;
}
--- /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.record;
+
+import org.apache.poi.hssf.record.aggregates.PageSettingsBlock;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndianOutput;
+
+import java.util.Arrays;
+
+/**
+ * The HEADERFOOTER record stores information added in Office Excel 2007 for headers/footers.
+ *
+ * @author Yegor Kozlov
+ */
+public final class HeaderFooterRecord extends StandardRecord {
+
+ private static final byte[] BLANK_GUID = new byte[16];
+
+ public final static short sid = 0x089C;
+ private byte[] _rawData;
+
+ public HeaderFooterRecord(byte[] data) {
+ _rawData = data;
+ }
+
+ /**
+ * construct a HeaderFooterRecord record. No fields are interpreted and the record will
+ * be serialized in its original form more or less
+ * @param in the RecordInputstream to read the record from
+ */
+ public HeaderFooterRecord(RecordInputStream in) {
+ _rawData = in.readRemainder();
+ }
+
+ /**
+ * spit the record out AS IS. no interpretation or identification
+ */
+ public void serialize(LittleEndianOutput out) {
+ out.write(_rawData);
+ }
+
+ protected int getDataSize() {
+ return _rawData.length;
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ /**
+ * If this header belongs to a specific sheet view , the sheet view?s GUID will be saved here.
+ * <p>
+ * If it is zero, it means the current sheet. Otherwise, this field MUST match the guid field
+ * of the preceding {@link UserSViewBegin} record.
+ *
+ * @return the sheet view?s GUID
+ */
+ public byte[] getGuid(){
+ byte[] guid = new byte[16];
+ System.arraycopy(_rawData, 12, guid, 0, guid.length);
+ return guid;
+ }
+
+ /**
+ * @return whether this record belongs to the current sheet
+ */
+ public boolean isCurrentSheet(){
+ return Arrays.equals(getGuid(), BLANK_GUID);
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("[").append("HEADERFOOTER").append("] (0x");
+ sb.append(Integer.toHexString(sid).toUpperCase() + ")\n");
+ sb.append(" rawData=").append(HexDump.toHex(_rawData)).append("\n");
+ sb.append("[/").append("HEADERFOOTER").append("]\n");
+ return sb.toString();
+ }
+
+}
\ No newline at end of file
GutsRecord.class,
HCenterRecord.class,
HeaderRecord.class,
+ HeaderFooterRecord.class,
HideObjRecord.class,
HorizontalPageBreakRecord.class,
HyperlinkRecord.class,
TopMarginRecord.class,
UncalcedRecord.class,
UseSelFSRecord.class,
+ UserSViewBegin.class,
+ UserSViewEnd.class,
VCenterRecord.class,
VerticalPageBreakRecord.class,
WindowOneRecord.class,
public static final int BITMAP_00E9 = 0x00E9;
public static final int PHONETICPR_00EF = 0x00EF;
public static final int LABELRANGES_015F = 0x015F;
- public static final int USERSVIEWBEGIN_01AA = 0x01AA;
- public static final int USERSVIEWEND_01AB = 0x01AB;
public static final int QUICKTIP_0800 = 0x0800;
public static final int SHEETEXT_0862 = 0x0862; // OOO calls this SHEETLAYOUT
public static final int SHEETPROTECTION_0867 = 0x0867;
case LABELRANGES_015F: return "LABELRANGES";
case 0x01BA: return "CODENAME";
case 0x01A9: return "USERBVIEW";
- case USERSVIEWBEGIN_01AA: return "USERSVIEWBEGIN";
- case USERSVIEWEND_01AB: return "USERSVIEWEND";
case 0x01AD: return "QSI";
case 0x01C0: return "EXCEL9FILE";
--- /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.record;
+
+import org.apache.poi.hssf.record.aggregates.PageSettingsBlock;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndianOutput;
+
+import java.util.Arrays;
+
+/**
+ * The UserSViewBegin record specifies settings for a custom view associated with the sheet.
+ * This record also marks the start of custom view records, which save custom view settings.
+ * Records between {@link UserSViewBegin} and {@link UserSViewEnd} contain settings for the custom view,
+ * not settings for the sheet itself.
+ *
+ * @author Yegor Kozlov
+ */
+public final class UserSViewBegin extends StandardRecord {
+
+ public final static short sid = 0x01AA;
+ private byte[] _rawData;
+
+ public UserSViewBegin(byte[] data) {
+ _rawData = data;
+ }
+
+ /**
+ * construct an UserSViewBegin record. No fields are interpreted and the record will
+ * be serialized in its original form more or less
+ * @param in the RecordInputstream to read the record from
+ */
+ public UserSViewBegin(RecordInputStream in) {
+ _rawData = in.readRemainder();
+ }
+
+ /**
+ * spit the record out AS IS. no interpretation or identification
+ */
+ public void serialize(LittleEndianOutput out) {
+ out.write(_rawData);
+ }
+
+ protected int getDataSize() {
+ return _rawData.length;
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ /**
+ * @return Globally unique identifier for the custom view
+ */
+ public byte[] getGuid(){
+ byte[] guid = new byte[16];
+ System.arraycopy(_rawData, 0, guid, 0, guid.length);
+ return guid;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("[").append("USERSVIEWBEGIN").append("] (0x");
+ sb.append(Integer.toHexString(sid).toUpperCase() + ")\n");
+ sb.append(" rawData=").append(HexDump.toHex(_rawData)).append("\n");
+ sb.append("[/").append("USERSVIEWBEGIN").append("]\n");
+ return sb.toString();
+ }
+
+}
\ No newline at end of file
--- /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.record;
+
+import org.apache.poi.hssf.record.aggregates.PageSettingsBlock;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndianOutput;
+
+import java.util.Arrays;
+
+/**
+ * The UserSViewEnd record marks the end of the settings for a custom view associated with the sheet
+ *
+ * @author Yegor Kozlov
+ */
+public final class UserSViewEnd extends StandardRecord {
+
+ public final static short sid = 0x01AB;
+ private byte[] _rawData;
+
+ public UserSViewEnd(byte[] data) {
+ _rawData = data;
+ }
+
+ /**
+ * construct an UserSViewEnd record. No fields are interpreted and the record will
+ * be serialized in its original form more or less
+ * @param in the RecordInputstream to read the record from
+ */
+ public UserSViewEnd(RecordInputStream in) {
+ _rawData = in.readRemainder();
+ }
+
+ /**
+ * spit the record out AS IS. no interpretation or identification
+ */
+ public void serialize(LittleEndianOutput out) {
+ out.write(_rawData);
+ }
+
+ protected int getDataSize() {
+ return _rawData.length;
+ }
+
+ public short getSid()
+ {
+ return sid;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("[").append("USERSVIEWEND").append("] (0x");
+ sb.append(Integer.toHexString(sid).toUpperCase() + ")\n");
+ sb.append(" rawData=").append(HexDump.toHex(_rawData)).append("\n");
+ sb.append("[/").append("USERSVIEWEND").append("]\n");
+ return sb.toString();
+ }
+
+}
\ No newline at end of file
import java.util.List;
import org.apache.poi.hssf.model.RecordStream;
-import org.apache.poi.hssf.record.BOFRecord;
-import org.apache.poi.hssf.record.EOFRecord;
-import org.apache.poi.hssf.record.Record;
-import org.apache.poi.hssf.record.RecordBase;
-import org.apache.poi.hssf.record.UnknownRecord;
+import org.apache.poi.hssf.record.*;
/**
* Manages the all the records associated with a chart sub-stream.<br/>
while (rs.peekNextClass() != EOFRecord.class) {
if (PageSettingsBlock.isComponentRecord(rs.peekNextSid())) {
if (_psBlock != null) {
- if (rs.peekNextSid() == UnknownRecord.HEADER_FOOTER_089C) {
+ if (rs.peekNextSid() == HeaderFooterRecord.sid) {
// test samples: 45538_classic_Footer.xls, 45538_classic_Header.xls
- _psBlock.addLateHeaderFooter(rs.getNext());
+ _psBlock.addLateHeaderFooter((HeaderFooterRecord)rs.getNext());
continue;
}
throw new IllegalStateException(
import java.util.List;
import org.apache.poi.hssf.model.RecordStream;
-import org.apache.poi.hssf.record.Record;
-import org.apache.poi.hssf.record.RecordBase;
-import org.apache.poi.hssf.record.UnknownRecord;
+import org.apache.poi.hssf.record.*;
/**
* Manages the all the records associated with a 'Custom View Settings' sub-stream.<br/>
public CustomViewSettingsRecordAggregate(RecordStream rs) {
_begin = rs.getNext();
- if (_begin.getSid() != UnknownRecord.USERSVIEWBEGIN_01AA) {
+ if (_begin.getSid() != UserSViewBegin.sid) {
throw new IllegalStateException("Bad begin record");
}
List<RecordBase> temp = new ArrayList<RecordBase>();
- while (rs.peekNextSid() != UnknownRecord.USERSVIEWEND_01AB) {
+ while (rs.peekNextSid() != UserSViewEnd.sid) {
if (PageSettingsBlock.isComponentRecord(rs.peekNextSid())) {
if (_psBlock != null) {
throw new IllegalStateException(
}
_recs = temp;
_end = rs.getNext(); // no need to save EOF in field
- if (_end.getSid() != UnknownRecord.USERSVIEWEND_01AB) {
+ if (_end.getSid() != UserSViewEnd.sid) {
throw new IllegalStateException("Bad custom view settings end record");
}
}
}
public static boolean isBeginRecord(int sid) {
- return sid == UnknownRecord.USERSVIEWBEGIN_01AA;
+ return sid == UserSViewBegin.sid;
}
+
+ public void append(RecordBase r){
+ _recs.add(r);
+ }
}
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Arrays;
import org.apache.poi.hssf.model.RecordStream;
import org.apache.poi.hssf.model.Sheet;
-import org.apache.poi.hssf.record.BottomMarginRecord;
-import org.apache.poi.hssf.record.ContinueRecord;
-import org.apache.poi.hssf.record.FooterRecord;
-import org.apache.poi.hssf.record.HCenterRecord;
-import org.apache.poi.hssf.record.HeaderRecord;
-import org.apache.poi.hssf.record.HorizontalPageBreakRecord;
-import org.apache.poi.hssf.record.LeftMarginRecord;
-import org.apache.poi.hssf.record.Margin;
-import org.apache.poi.hssf.record.PageBreakRecord;
-import org.apache.poi.hssf.record.PrintSetupRecord;
-import org.apache.poi.hssf.record.Record;
-import org.apache.poi.hssf.record.RecordFormatException;
-import org.apache.poi.hssf.record.RightMarginRecord;
-import org.apache.poi.hssf.record.TopMarginRecord;
-import org.apache.poi.hssf.record.UnknownRecord;
-import org.apache.poi.hssf.record.VCenterRecord;
-import org.apache.poi.hssf.record.VerticalPageBreakRecord;
+import org.apache.poi.hssf.record.*;
/**
* Groups the page settings records for a worksheet.<p/>
private final List<PLSAggregate> _plsRecords;
private PrintSetupRecord _printSetup;
private Record _bitmap;
- private Record _headerFooter;
- private Record _printSize;
+ private HeaderFooterRecord _headerFooter;
+ /**
+ * HeaderFooterRecord records belonging to preceding CustomViewSettingsRecordAggregates.
+ * The indicator of such records is a non-zero GUID,
+ * see {@link org.apache.poi.hssf.record.HeaderFooterRecord#getGuid()}
+ */
+ private List<HeaderFooterRecord> _sviewHeaderFooters = new ArrayList<HeaderFooterRecord>();
+ private Record _printSize;
public PageSettingsBlock(RecordStream rs) {
_plsRecords = new ArrayList<PLSAggregate>();
_hCenter = createHCenter();
_vCenter = createVCenter();
_printSetup = createPrintSetup();
- }
+ }
/**
* @return <code>true</code> if the specified Record sid is one belonging to the
case PrintSetupRecord.sid:
case UnknownRecord.BITMAP_00E9:
case UnknownRecord.PRINTSIZE_0033:
- case UnknownRecord.HEADER_FOOTER_089C: // extra header/footer settings supported by Excel 2007
+ case HeaderFooterRecord.sid: // extra header/footer settings supported by Excel 2007
return true;
}
return false;
checkNotPresent(_printSize);
_printSize = rs.getNext();
break;
- case UnknownRecord.HEADER_FOOTER_089C:
- checkNotPresent(_headerFooter);
- _headerFooter = rs.getNext();
- break;
+ case HeaderFooterRecord.sid:
+ //there can be multiple HeaderFooterRecord records belonging to different sheet views
+ HeaderFooterRecord hf = (HeaderFooterRecord)rs.getNext();
+ if(hf.isCurrentSheet()) _headerFooter = hf;
+ else {
+ _sviewHeaderFooters.add(hf);
+ }
+ break;
default:
// all other record types are not part of the PageSettingsBlock
return false;
* HEADERFOOTER is new in 2007. Some apps seem to have scattered this record long after
* the {@link PageSettingsBlock} where it belongs.
*/
- public void addLateHeaderFooter(Record rec) {
+ public void addLateHeaderFooter(HeaderFooterRecord rec) {
if (_headerFooter != null) {
throw new IllegalStateException("This page settings block already has a header/footer record");
}
- if (rec.getSid() != UnknownRecord.HEADER_FOOTER_089C) {
+ if (rec.getSid() != HeaderFooterRecord.sid) {
throw new RecordFormatException("Unexpected header-footer record sid: 0x" + Integer.toHexString(rec.getSid()));
}
_headerFooter = rec;
}
}
}
+
+ /**
+ * Some apps can define multiple HeaderFooterRecord records for a sheet.
+ * When saving such a file Excel 2007 re-positiones them according to the followig rules:
+ * - take a HeaderFooterRecord and read 16-byte GUID at offset 12. If it is zero,
+ * it means the current sheet and the given HeaderFooterRecord belongs to this PageSettingsBlock
+ * - If GUID is not zero then search in preceding CustomViewSettingsRecordAggregates.
+ * Compare first 16 bytes of UserSViewBegin with the HeaderFooterRecord's GUID. If match,
+ * then append the HeaderFooterRecord to this CustomViewSettingsRecordAggregates
+ *
+ * @param sheetRecords the list of sheet records read so far
+ */
+ public void positionRecords(List<RecordBase> sheetRecords) {
+ // loop through HeaderFooterRecord records having not-empty GUID and match them with
+ // CustomViewSettingsRecordAggregate blocks having UserSViewBegin with the same GUID
+ for (final Iterator<HeaderFooterRecord> it = _sviewHeaderFooters.iterator(); it.hasNext(); ) {
+ final HeaderFooterRecord hf = it.next();
+ for (RecordBase rb : sheetRecords) {
+ if (rb instanceof CustomViewSettingsRecordAggregate) {
+ final CustomViewSettingsRecordAggregate cv = (CustomViewSettingsRecordAggregate) rb;
+ cv.visitContainedRecords(new RecordVisitor() {
+ public void visitRecord(Record r) {
+ if (r.getSid() == UserSViewBegin.sid) {
+ byte[] guid1 = ((UserSViewBegin) r).getGuid();
+ byte[] guid2 = hf.getGuid();
+ if (Arrays.equals(guid1, guid2)) {
+ cv.append(hf);
+ it.remove();
+ }
+ }
+ }
+ });
+ }
+ }
+ }
+ }
}
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.model.RecordStream;
import org.apache.poi.hssf.model.Sheet;
-import org.apache.poi.hssf.record.BOFRecord;
-import org.apache.poi.hssf.record.BottomMarginRecord;
-import org.apache.poi.hssf.record.ContinueRecord;
-import org.apache.poi.hssf.record.DimensionsRecord;
-import org.apache.poi.hssf.record.EOFRecord;
-import org.apache.poi.hssf.record.FooterRecord;
-import org.apache.poi.hssf.record.HCenterRecord;
-import org.apache.poi.hssf.record.HeaderRecord;
-import org.apache.poi.hssf.record.IndexRecord;
-import org.apache.poi.hssf.record.NumberRecord;
-import org.apache.poi.hssf.record.Record;
-import org.apache.poi.hssf.record.RecordFormatException;
-import org.apache.poi.hssf.record.UnknownRecord;
-import org.apache.poi.hssf.record.VCenterRecord;
-import org.apache.poi.hssf.record.WindowTwoRecord;
+import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.usermodel.HSSFPrintSetup;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
BOFRecord.createSheetBOF(),
new HeaderRecord("&LSales Figures"),
new FooterRecord("&LJanuary"),
- ur(UnknownRecord.HEADER_FOOTER_089C, "9C 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 60 00 00 00 00 00 00 00 00"),
+ new HeaderFooterRecord(HexRead.readFromString("9C 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 60 00 00 00 00 00 00 00 00")),
new DimensionsRecord(),
new WindowTwoRecord(),
- ur(UnknownRecord.USERSVIEWBEGIN_01AA, "ED 77 3B 86 BC 3F 37 4C A9 58 60 23 43 68 54 4B 01 00 00 00 64 00 00 00 40 00 00 00 02 00 00 00 3D 80 04 00 00 00 00 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 3F FF FF 01 00"),
+ new UserSViewBegin(HexRead.readFromString("ED 77 3B 86 BC 3F 37 4C A9 58 60 23 43 68 54 4B 01 00 00 00 64 00 00 00 40 00 00 00 02 00 00 00 3D 80 04 00 00 00 00 00 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 3F FF FF 01 00")),
new HeaderRecord("&LSales Figures"),
new FooterRecord("&LJanuary"),
- ur(UnknownRecord.HEADER_FOOTER_089C, "9C 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 60 00 00 00 00 00 00 00 00"),
- ur(UnknownRecord.USERSVIEWEND_01AB, "01, 00"),
+ new HeaderFooterRecord(HexRead.readFromString("9C 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 60 00 00 00 00 00 00 00 00")),
+ new UserSViewEnd(HexRead.readFromString("01, 00")),
EOFRecord.instance,
};
new FooterRecord("&LJanuary"),
new DimensionsRecord(),
new WindowTwoRecord(),
- ur(UnknownRecord.HEADER_FOOTER_089C, "9C 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 60 00 00 00 00 00 00 00 00"),
+ new HeaderFooterRecord(HexRead.readFromString("9C 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C4 60 00 00 00 00 00 00 00 00")),
EOFRecord.instance,
};
RecordStream rs = new RecordStream(Arrays.asList(recs), 0);
assertEquals(IndexRecord.class, outRecs[1].getClass());
assertEquals(HeaderRecord.class, outRecs[2].getClass());
assertEquals(FooterRecord.class, outRecs[3].getClass());
- assertEquals(UnknownRecord.HEADER_FOOTER_089C, outRecs[4].getSid());
+ assertEquals(HeaderFooterRecord.class, outRecs[4].getClass());
assertEquals(DimensionsRecord.class, outRecs[5].getClass());
assertEquals(WindowTwoRecord.class, outRecs[6].getClass());
assertEquals(EOFRecord.instance, outRecs[7]);
// records were assembled in standard order, so this simple check is OK
assertTrue(Arrays.equals(recs, outRecs));
}
+
+ public void testDuplicateHeaderFooter_bug48026() {
+
+ Record[] recs = {
+ BOFRecord.createSheetBOF(),
+ new IndexRecord(),
+
+ //PageSettingsBlock
+ new HeaderRecord("&LDecember"),
+ new FooterRecord("&LJanuary"),
+ new DimensionsRecord(),
+
+ new WindowTwoRecord(),
+
+ //CustomViewSettingsRecordAggregate
+ new UserSViewBegin(HexRead.readFromString("53 CE BD CC DE 38 44 45 97 C1 5C 89 F9 37 32 1B 01 00 00 00 64 00 00 00 40 00 00 00 03 00 00 00 7D 00 00 20 00 00 34 00 00 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF")),
+ new SelectionRecord(0, 0),
+ new UserSViewEnd(HexRead.readFromString("01 00")),
+
+ // two HeaderFooterRecord records, the first one has zero GUID (16 bytes at offset 12) and belongs to the PSB,
+ // the other is matched with a CustomViewSettingsRecordAggregate having UserSViewBegin with the same GUID
+ new HeaderFooterRecord(HexRead.readFromString("9C 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 34 33 00 00 00 00 00 00 00 00")),
+ new HeaderFooterRecord(HexRead.readFromString("9C 08 00 00 00 00 00 00 00 00 00 00 53 CE BD CC DE 38 44 45 97 C1 5C 89 F9 37 32 1B 34 33 00 00 00 00 00 00 00 00")),
+
+ EOFRecord.instance,
+ };
+ RecordStream rs = new RecordStream(Arrays.asList(recs), 0);
+ Sheet sheet;
+ try {
+ sheet = Sheet.createSheet(rs);
+ } catch (RuntimeException e) {
+ if (e.getMessage().equals("Duplicate PageSettingsBlock record (sid=0x89c)")) {
+ throw new AssertionFailedError("Identified bug 48026");
+ }
+ throw e;
+ }
+
+ RecordCollector rv = new RecordCollector();
+ sheet.visitContainedRecords(rv, 0);
+ Record[] outRecs = rv.getRecords();
+
+ assertEquals(recs.length, outRecs.length);
+ //expected order of records:
+ Record[] expectedRecs = {
+ recs[0], //BOFRecord
+ recs[1], //IndexRecord
+
+ //PageSettingsBlock
+ recs[2], //HeaderRecord
+ recs[3], //FooterRecord
+ recs[9], //HeaderFooterRecord
+ recs[4], // DimensionsRecord
+ recs[5], // WindowTwoRecord
+
+ //CustomViewSettingsRecordAggregate
+ recs[6], // UserSViewBegin
+ recs[7], // SelectionRecord
+ recs[10], // HeaderFooterRecord
+ recs[8], // UserSViewEnd
+
+ recs[11], //EOFRecord
+ };
+ for(int i=0; i < expectedRecs.length; i++){
+ assertEquals("Record mismatch at index " + i, expectedRecs[i].getClass(), outRecs[i].getClass());
+ }
+ HeaderFooterRecord hd1 = (HeaderFooterRecord)expectedRecs[4];
+ //GUID is zero
+ assertTrue(Arrays.equals(new byte[16], hd1.getGuid()));
+ assertTrue(hd1.isCurrentSheet());
+
+ UserSViewBegin svb = (UserSViewBegin)expectedRecs[7];
+ HeaderFooterRecord hd2 = (HeaderFooterRecord)expectedRecs[9];
+ assertFalse(hd2.isCurrentSheet());
+ //GUIDs of HeaderFooterRecord and UserSViewBegin must be the same
+ assertTrue(Arrays.equals(svb.getGuid(), hd2.getGuid()));
+ }
+
}