| /* ==================================================================== |
| 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.aggregates; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.poi.hssf.model.RecordStream; |
| import org.apache.poi.hssf.model.InternalSheet; |
| import org.apache.poi.hssf.record.*; |
| import org.apache.poi.util.HexDump; |
| |
| /** |
| * Groups the page settings records for a worksheet.<p> |
| * |
| * See OOO excelfileformat.pdf sec 4.4 'Page Settings Block' |
| */ |
| public final class PageSettingsBlock extends RecordAggregate { |
| |
| /** |
| * PLS is potentially a <i>continued</i> record, but is currently uninterpreted by POI |
| */ |
| private static final class PLSAggregate extends RecordAggregate { |
| private static final ContinueRecord[] EMPTY_CONTINUE_RECORD_ARRAY = { }; |
| private final Record _pls; |
| /** |
| * holds any continue records found after the PLS record.<br/> |
| * This would not be required if PLS was properly interpreted. |
| * Currently, PLS is an {@link UnknownRecord} and does not automatically |
| * include any trailing {@link ContinueRecord}s. |
| */ |
| private ContinueRecord[] _plsContinues; |
| |
| public PLSAggregate(RecordStream rs) { |
| _pls = rs.getNext(); |
| if (rs.peekNextSid()==ContinueRecord.sid) { |
| List<ContinueRecord> temp = new ArrayList<ContinueRecord>(); |
| while (rs.peekNextSid()==ContinueRecord.sid) { |
| temp.add((ContinueRecord)rs.getNext()); |
| } |
| _plsContinues = new ContinueRecord[temp.size()]; |
| temp.toArray(_plsContinues); |
| } else { |
| _plsContinues = EMPTY_CONTINUE_RECORD_ARRAY; |
| } |
| } |
| |
| @Override |
| public void visitContainedRecords(RecordVisitor rv) { |
| rv.visitRecord(_pls); |
| for (ContinueRecord _plsContinue : _plsContinues) { |
| rv.visitRecord(_plsContinue); |
| } |
| } |
| } |
| |
| // Every one of these component records is optional |
| // (The whole PageSettingsBlock may not be present) |
| private PageBreakRecord _rowBreaksRecord; |
| private PageBreakRecord _columnBreaksRecord; |
| private HeaderRecord _header; |
| private FooterRecord _footer; |
| private HCenterRecord _hCenter; |
| private VCenterRecord _vCenter; |
| private LeftMarginRecord _leftMargin; |
| private RightMarginRecord _rightMargin; |
| private TopMarginRecord _topMargin; |
| private BottomMarginRecord _bottomMargin; |
| private final List<PLSAggregate> _plsRecords; |
| private PrintSetupRecord _printSetup; |
| private Record _bitmap; |
| 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 final List<HeaderFooterRecord> _sviewHeaderFooters = new ArrayList<HeaderFooterRecord>(); |
| private Record _printSize; |
| |
| public PageSettingsBlock(RecordStream rs) { |
| _plsRecords = new ArrayList<PLSAggregate>(); |
| while(true) { |
| if (!readARecord(rs)) { |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Creates a PageSettingsBlock with default settings |
| */ |
| public PageSettingsBlock() { |
| _plsRecords = new ArrayList<PLSAggregate>(); |
| _rowBreaksRecord = new HorizontalPageBreakRecord(); |
| _columnBreaksRecord = new VerticalPageBreakRecord(); |
| _header = new HeaderRecord(""); |
| _footer = new FooterRecord(""); |
| _hCenter = createHCenter(); |
| _vCenter = createVCenter(); |
| _printSetup = createPrintSetup(); |
| } |
| |
| /** |
| * @param sid the record sid |
| * |
| * @return <code>true</code> if the specified Record sid is one belonging to the |
| * 'Page Settings Block'. |
| */ |
| public static boolean isComponentRecord(int sid) { |
| switch (sid) { |
| case HorizontalPageBreakRecord.sid: |
| case VerticalPageBreakRecord.sid: |
| case HeaderRecord.sid: |
| case FooterRecord.sid: |
| case HCenterRecord.sid: |
| case VCenterRecord.sid: |
| case LeftMarginRecord.sid: |
| case RightMarginRecord.sid: |
| case TopMarginRecord.sid: |
| case BottomMarginRecord.sid: |
| case UnknownRecord.PLS_004D: |
| case PrintSetupRecord.sid: |
| case UnknownRecord.BITMAP_00E9: |
| case UnknownRecord.PRINTSIZE_0033: |
| case HeaderFooterRecord.sid: // extra header/footer settings supported by Excel 2007 |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean readARecord(RecordStream rs) { |
| switch (rs.peekNextSid()) { |
| case HorizontalPageBreakRecord.sid: |
| checkNotPresent(_rowBreaksRecord); |
| _rowBreaksRecord = (PageBreakRecord) rs.getNext(); |
| break; |
| case VerticalPageBreakRecord.sid: |
| checkNotPresent(_columnBreaksRecord); |
| _columnBreaksRecord = (PageBreakRecord) rs.getNext(); |
| break; |
| case HeaderRecord.sid: |
| checkNotPresent(_header); |
| _header = (HeaderRecord) rs.getNext(); |
| break; |
| case FooterRecord.sid: |
| checkNotPresent(_footer); |
| _footer = (FooterRecord) rs.getNext(); |
| break; |
| case HCenterRecord.sid: |
| checkNotPresent(_hCenter); |
| _hCenter = (HCenterRecord) rs.getNext(); |
| break; |
| case VCenterRecord.sid: |
| checkNotPresent(_vCenter); |
| _vCenter = (VCenterRecord) rs.getNext(); |
| break; |
| case LeftMarginRecord.sid: |
| checkNotPresent(_leftMargin); |
| _leftMargin = (LeftMarginRecord) rs.getNext(); |
| break; |
| case RightMarginRecord.sid: |
| checkNotPresent(_rightMargin); |
| _rightMargin = (RightMarginRecord) rs.getNext(); |
| break; |
| case TopMarginRecord.sid: |
| checkNotPresent(_topMargin); |
| _topMargin = (TopMarginRecord) rs.getNext(); |
| break; |
| case BottomMarginRecord.sid: |
| checkNotPresent(_bottomMargin); |
| _bottomMargin = (BottomMarginRecord) rs.getNext(); |
| break; |
| case UnknownRecord.PLS_004D: |
| _plsRecords.add(new PLSAggregate(rs)); |
| break; |
| case PrintSetupRecord.sid: |
| checkNotPresent(_printSetup); |
| _printSetup = (PrintSetupRecord)rs.getNext(); |
| break; |
| case UnknownRecord.BITMAP_00E9: |
| checkNotPresent(_bitmap); |
| _bitmap = rs.getNext(); |
| break; |
| case UnknownRecord.PRINTSIZE_0033: |
| checkNotPresent(_printSize); |
| _printSize = 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; |
| } |
| return true; |
| } |
| |
| private void checkNotPresent(Record rec) { |
| if (rec != null) { |
| throw new RecordFormatException("Duplicate PageSettingsBlock record (sid=0x" |
| + Integer.toHexString(rec.getSid()) + ")"); |
| } |
| } |
| |
| private PageBreakRecord getRowBreaksRecord() { |
| if (_rowBreaksRecord == null) { |
| _rowBreaksRecord = new HorizontalPageBreakRecord(); |
| } |
| return _rowBreaksRecord; |
| } |
| |
| private PageBreakRecord getColumnBreaksRecord() { |
| if (_columnBreaksRecord == null) { |
| _columnBreaksRecord = new VerticalPageBreakRecord(); |
| } |
| return _columnBreaksRecord; |
| } |
| |
| |
| /** |
| * Sets a page break at the indicated column |
| * |
| * @param column the column to add page breaks to |
| * @param fromRow the starting row |
| * @param toRow the ending row |
| */ |
| public void setColumnBreak(short column, short fromRow, short toRow) { |
| getColumnBreaksRecord().addBreak(column, fromRow, toRow); |
| } |
| |
| /** |
| * Removes a page break at the indicated column |
| * |
| * @param column the column to check for page breaks |
| */ |
| public void removeColumnBreak(int column) { |
| getColumnBreaksRecord().removeBreak(column); |
| } |
| |
| |
| |
| |
| @Override |
| public void visitContainedRecords(RecordVisitor rv) { |
| // Replicates record order from Excel 2007, though this is not critical |
| |
| visitIfPresent(_rowBreaksRecord, rv); |
| visitIfPresent(_columnBreaksRecord, rv); |
| // Write out empty header / footer records if these are missing |
| if (_header == null) { |
| rv.visitRecord(new HeaderRecord("")); |
| } else { |
| rv.visitRecord(_header); |
| } |
| if (_footer == null) { |
| rv.visitRecord(new FooterRecord("")); |
| } else { |
| rv.visitRecord(_footer); |
| } |
| visitIfPresent(_hCenter, rv); |
| visitIfPresent(_vCenter, rv); |
| visitIfPresent(_leftMargin, rv); |
| visitIfPresent(_rightMargin, rv); |
| visitIfPresent(_topMargin, rv); |
| visitIfPresent(_bottomMargin, rv); |
| for (RecordAggregate pls : _plsRecords) { |
| pls.visitContainedRecords(rv); |
| } |
| visitIfPresent(_printSetup, rv); |
| visitIfPresent(_printSize, rv); |
| visitIfPresent(_headerFooter, rv); |
| visitIfPresent(_bitmap, rv); |
| } |
| private static void visitIfPresent(Record r, RecordVisitor rv) { |
| if (r != null) { |
| rv.visitRecord(r); |
| } |
| } |
| private static void visitIfPresent(PageBreakRecord r, RecordVisitor rv) { |
| if (r != null) { |
| if (r.isEmpty()) { |
| // its OK to not serialize empty page break records |
| return; |
| } |
| rv.visitRecord(r); |
| } |
| } |
| |
| /** |
| * creates the HCenter Record and sets it to false (don't horizontally center) |
| */ |
| private static HCenterRecord createHCenter() { |
| HCenterRecord retval = new HCenterRecord(); |
| |
| retval.setHCenter(false); |
| return retval; |
| } |
| |
| /** |
| * creates the VCenter Record and sets it to false (don't horizontally center) |
| */ |
| private static VCenterRecord createVCenter() { |
| VCenterRecord retval = new VCenterRecord(); |
| |
| retval.setVCenter(false); |
| return retval; |
| } |
| |
| /** |
| * creates the PrintSetup Record and sets it to defaults and marks it invalid |
| * @see org.apache.poi.hssf.record.PrintSetupRecord |
| * @see org.apache.poi.hssf.record.Record |
| * @return record containing a PrintSetupRecord |
| */ |
| private static PrintSetupRecord createPrintSetup() { |
| PrintSetupRecord retval = new PrintSetupRecord(); |
| |
| retval.setPaperSize(( short ) 1); |
| retval.setScale(( short ) 100); |
| retval.setPageStart(( short ) 1); |
| retval.setFitWidth(( short ) 1); |
| retval.setFitHeight(( short ) 1); |
| retval.setOptions(( short ) 2); |
| retval.setHResolution(( short ) 300); |
| retval.setVResolution(( short ) 300); |
| retval.setHeaderMargin( 0.5); |
| retval.setFooterMargin( 0.5); |
| retval.setCopies(( short ) 1); |
| return retval; |
| } |
| |
| |
| /** |
| * Returns the HeaderRecord. |
| * @return HeaderRecord for the sheet. |
| */ |
| public HeaderRecord getHeader () |
| { |
| return _header; |
| } |
| |
| /** |
| * Sets the HeaderRecord. |
| * @param newHeader The new HeaderRecord for the sheet. |
| */ |
| public void setHeader (HeaderRecord newHeader) |
| { |
| _header = newHeader; |
| } |
| |
| /** |
| * Returns the FooterRecord. |
| * @return FooterRecord for the sheet. |
| */ |
| public FooterRecord getFooter () |
| { |
| return _footer; |
| } |
| |
| /** |
| * Sets the FooterRecord. |
| * @param newFooter The new FooterRecord for the sheet. |
| */ |
| public void setFooter (FooterRecord newFooter) |
| { |
| _footer = newFooter; |
| } |
| |
| /** |
| * Returns the PrintSetupRecord. |
| * @return PrintSetupRecord for the sheet. |
| */ |
| public PrintSetupRecord getPrintSetup () |
| { |
| return _printSetup; |
| } |
| |
| /** |
| * Sets the PrintSetupRecord. |
| * @param newPrintSetup The new PrintSetupRecord for the sheet. |
| */ |
| public void setPrintSetup (PrintSetupRecord newPrintSetup) |
| { |
| _printSetup = newPrintSetup; |
| } |
| |
| |
| private Margin getMarginRec(int marginIndex) { |
| switch (marginIndex) { |
| case InternalSheet.LeftMargin: return _leftMargin; |
| case InternalSheet.RightMargin: return _rightMargin; |
| case InternalSheet.TopMargin: return _topMargin; |
| case InternalSheet.BottomMargin: return _bottomMargin; |
| } |
| throw new IllegalArgumentException( "Unknown margin constant: " + marginIndex ); |
| } |
| |
| |
| /** |
| * Gets the size of the margin in inches. |
| * @param margin which margin to get |
| * @return the size of the margin |
| */ |
| public double getMargin(short margin) { |
| Margin m = getMarginRec(margin); |
| if (m != null) { |
| return m.getMargin(); |
| } |
| switch (margin) { |
| case InternalSheet.LeftMargin: return .75; |
| case InternalSheet.RightMargin: return .75; |
| case InternalSheet.TopMargin: return 1.0; |
| case InternalSheet.BottomMargin: return 1.0; |
| } |
| throw new IllegalArgumentException( "Unknown margin constant: " + margin ); |
| } |
| |
| /** |
| * Sets the size of the margin in inches. |
| * @param margin which margin to get |
| * @param size the size of the margin |
| */ |
| public void setMargin(short margin, double size) { |
| Margin m = getMarginRec(margin); |
| if (m == null) { |
| switch (margin) { |
| case InternalSheet.LeftMargin: |
| _leftMargin = new LeftMarginRecord(); |
| m = _leftMargin; |
| break; |
| case InternalSheet.RightMargin: |
| _rightMargin = new RightMarginRecord(); |
| m = _rightMargin; |
| break; |
| case InternalSheet.TopMargin: |
| _topMargin = new TopMarginRecord(); |
| m = _topMargin; |
| break; |
| case InternalSheet.BottomMargin: |
| _bottomMargin = new BottomMarginRecord(); |
| m = _bottomMargin; |
| break; |
| default : |
| throw new IllegalArgumentException( "Unknown margin constant: " + margin ); |
| } |
| } |
| m.setMargin( size ); |
| } |
| |
| /** |
| * Shifts all the page breaks in the range "count" number of rows/columns |
| * @param breaks The page record to be shifted |
| * @param start Starting "main" value to shift breaks |
| * @param stop Ending "main" value to shift breaks |
| * @param count number of units (rows/columns) to shift by |
| */ |
| private static void shiftBreaks(PageBreakRecord breaks, int start, int stop, int count) { |
| |
| Iterator<PageBreakRecord.Break> iterator = breaks.getBreaksIterator(); |
| List<PageBreakRecord.Break> shiftedBreak = new ArrayList<PageBreakRecord.Break>(); |
| while(iterator.hasNext()) |
| { |
| PageBreakRecord.Break breakItem = iterator.next(); |
| int breakLocation = breakItem.main; |
| boolean inStart = (breakLocation >= start); |
| boolean inEnd = (breakLocation <= stop); |
| if(inStart && inEnd) { |
| shiftedBreak.add(breakItem); |
| } |
| } |
| |
| iterator = shiftedBreak.iterator(); |
| while (iterator.hasNext()) { |
| PageBreakRecord.Break breakItem = iterator.next(); |
| breaks.removeBreak(breakItem.main); |
| breaks.addBreak((short)(breakItem.main+count), breakItem.subFrom, breakItem.subTo); |
| } |
| } |
| |
| |
| /** |
| * Sets a page break at the indicated row |
| * @param row the row |
| * @param fromCol the starting column |
| * @param toCol the ending column |
| */ |
| public void setRowBreak(int row, short fromCol, short toCol) { |
| getRowBreaksRecord().addBreak((short)row, fromCol, toCol); |
| } |
| |
| /** |
| * Removes a page break at the indicated row |
| * @param row the row |
| */ |
| public void removeRowBreak(int row) { |
| if (getRowBreaksRecord().getBreaks().length < 1) { |
| throw new IllegalArgumentException("Sheet does not define any row breaks"); |
| } |
| getRowBreaksRecord().removeBreak((short)row); |
| } |
| |
| /** |
| * Queries if the specified row has a page break |
| * |
| * @param row the row to check for |
| * |
| * @return true if the specified row has a page break |
| */ |
| public boolean isRowBroken(int row) { |
| return getRowBreaksRecord().getBreak(row) != null; |
| } |
| |
| |
| /** |
| * Queries if the specified column has a page break |
| * |
| * @param column the column to check for |
| * |
| * @return <code>true</code> if the specified column has a page break |
| */ |
| public boolean isColumnBroken(int column) { |
| return getColumnBreaksRecord().getBreak(column) != null; |
| } |
| |
| /** |
| * Shifts the horizontal page breaks for the indicated count |
| * @param startingRow the starting row |
| * @param endingRow the ending row |
| * @param count the number of rows to shift by |
| */ |
| public void shiftRowBreaks(int startingRow, int endingRow, int count) { |
| shiftBreaks(getRowBreaksRecord(), startingRow, endingRow, count); |
| } |
| |
| /** |
| * Shifts the vertical page breaks for the indicated count |
| * @param startingCol the starting column |
| * @param endingCol the ending column |
| * @param count the number of columns to shift by |
| */ |
| public void shiftColumnBreaks(short startingCol, short endingCol, short count) { |
| shiftBreaks(getColumnBreaksRecord(), startingCol, endingCol, count); |
| } |
| |
| /** |
| * @return all the horizontal page breaks, never <code>null</code> |
| */ |
| public int[] getRowBreaks() { |
| return getRowBreaksRecord().getBreaks(); |
| } |
| |
| /** |
| * @return the number of row page breaks |
| */ |
| public int getNumRowBreaks(){ |
| return getRowBreaksRecord().getNumBreaks(); |
| } |
| |
| /** |
| * @return all the column page breaks, never <code>null</code> |
| */ |
| public int[] getColumnBreaks(){ |
| return getColumnBreaksRecord().getBreaks(); |
| } |
| |
| /** |
| * @return the number of column page breaks |
| */ |
| public int getNumColumnBreaks(){ |
| return getColumnBreaksRecord().getNumBreaks(); |
| } |
| |
| public VCenterRecord getVCenter() { |
| return _vCenter; |
| } |
| |
| public HCenterRecord getHCenter() { |
| return _hCenter; |
| } |
| |
| /** |
| * HEADERFOOTER is new in 2007. Some apps seem to have scattered this record long after |
| * the {@link PageSettingsBlock} where it belongs. |
| * |
| * @param rec the HeaderFooterRecord to set |
| */ |
| public void addLateHeaderFooter(HeaderFooterRecord rec) { |
| if (_headerFooter != null) { |
| throw new IllegalStateException("This page settings block already has a header/footer record"); |
| } |
| if (rec.getSid() != HeaderFooterRecord.sid) { |
| throw new RecordFormatException("Unexpected header-footer record sid: 0x" + Integer.toHexString(rec.getSid())); |
| } |
| _headerFooter = rec; |
| } |
| |
| /** |
| * This method reads PageSettingsBlock records from the supplied RecordStream until the first |
| * non-PageSettingsBlock record is encountered. As each record is read, it is incorporated |
| * into this PageSettingsBlock.<p> |
| * |
| * The latest Excel version seems to write the PageSettingsBlock uninterrupted. However there |
| * are several examples (that Excel reads OK) where these records are not written together: |
| * |
| * <ul> |
| * <li><b>HEADER_FOOTER(0x089C) after WINDOW2</b> - This record is new in 2007. Some apps |
| * seem to have scattered this record long after the PageSettingsBlock where it belongs |
| * test samples: SharedFormulaTest.xls, ex44921-21902.xls, ex42570-20305.xls</li> |
| * <li><b>PLS, WSBOOL, PageSettingsBlock</b> - WSBOOL is not a PSB record. |
| * This happens in the test sample file "NoGutsRecords.xls" and "WORKBOOK_in_capitals.xls"</li> |
| * <li><b>Margins after DIMENSION</b> - All of PSB should be before DIMENSION. (Bug-47199)</li> |
| * </ul> |
| * |
| * These were probably written by other applications (or earlier versions of Excel). It was |
| * decided to not write specific code for detecting each of these cases. POI now tolerates |
| * PageSettingsBlock records scattered all over the sheet record stream, and in any order, but |
| * does not allow duplicates of any of those records.<p> |
| * |
| * <b>Note</b> - when POI writes out this PageSettingsBlock, the records will always be written |
| * in one consolidated block (in the standard ordering) regardless of how scattered the records |
| * were when they were originally read. |
| * |
| * @param rs the RecordStream to read from |
| * |
| * @throws RecordFormatException if any PSB record encountered has the same type (sid) as |
| * a record that is already part of this PageSettingsBlock |
| */ |
| public void addLateRecords(RecordStream rs) { |
| while(true) { |
| if (!readARecord(rs)) { |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Some apps can define multiple HeaderFooterRecord records for a sheet. |
| * When saving such a file Excel 2007 re-positions them according to the following 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) { |
| // Take a copy to loop over, so we can update the real one |
| // without concurrency issues |
| List<HeaderFooterRecord> hfRecordsToIterate = new ArrayList<HeaderFooterRecord>(_sviewHeaderFooters); |
| |
| final Map<String, HeaderFooterRecord> hfGuidMap = new HashMap<String, HeaderFooterRecord>(); |
| |
| for(final HeaderFooterRecord hf : hfRecordsToIterate) { |
| hfGuidMap.put(HexDump.toHex(hf.getGuid()), hf); |
| } |
| |
| // loop through HeaderFooterRecord records having not-empty GUID and match them with |
| // CustomViewSettingsRecordAggregate blocks having UserSViewBegin with the same GUID |
| for (RecordBase rb : sheetRecords) { |
| if (rb instanceof CustomViewSettingsRecordAggregate) { |
| final CustomViewSettingsRecordAggregate cv = (CustomViewSettingsRecordAggregate) rb; |
| cv.visitContainedRecords(new RecordVisitor() { |
| @Override |
| public void visitRecord(Record r) { |
| if (r.getSid() == UserSViewBegin.sid) { |
| String guid = HexDump.toHex(((UserSViewBegin) r).getGuid()); |
| HeaderFooterRecord hf = hfGuidMap.get(guid); |
| |
| if (hf != null) { |
| cv.append(hf); |
| _sviewHeaderFooters.remove(hf); |
| } |
| } |
| } |
| }); |
| } |
| } |
| } |
| } |