You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ye...@apache.org on 2012/07/22 12:50:01 UTC

svn commit: r1364254 - in /poi/trunk/src: documentation/content/xdocs/status.xml java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java

Author: yegor
Date: Sun Jul 22 10:50:01 2012
New Revision: 1364254

URL: http://svn.apache.org/viewvc?rev=1364254&view=rev
Log:
Bugzilla 53204: Improve performance when opening certain Excel files (PageSettingsBlock) 

Modified:
    poi/trunk/src/documentation/content/xdocs/status.xml
    poi/trunk/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java

Modified: poi/trunk/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/trunk/src/documentation/content/xdocs/status.xml?rev=1364254&r1=1364253&r2=1364254&view=diff
==============================================================================
--- poi/trunk/src/documentation/content/xdocs/status.xml (original)
+++ poi/trunk/src/documentation/content/xdocs/status.xml Sun Jul 22 10:50:01 2012
@@ -34,6 +34,7 @@
 
     <changes>
         <release version="3.9-beta1" date="2012-??-??">
+          <action dev="poi-developers" type="add">53204 - Improved performanceof PageSettingsBlock in HSSF </action>
           <action dev="poi-developers" type="add">53500 - Getter for repeating rows and columns</action>
           <action dev="poi-developers" type="fix">53369 - Fixed tests failing on JDK 1.7</action>
           <action dev="poi-developers" type="fix">53360 - Fixed SXSSF to correctly write text before escaped Unicode control character</action>

Modified: poi/trunk/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java?rev=1364254&r1=1364253&r2=1364254&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java Sun Jul 22 10:50:01 2012
@@ -18,13 +18,15 @@
 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.Arrays;
+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/>
@@ -35,59 +37,59 @@ import org.apache.poi.hssf.record.*;
  */
 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 (int i = 0; i < _plsContinues.length; i++) {
-				rv.visitRecord(_plsContinues[i]);
-			}
-		}
-	}
-
-	// 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;
+    /**
+     * 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 (int i = 0; i < _plsContinues.length; i++) {
+                rv.visitRecord(_plsContinues[i]);
+            }
+        }
+    }
+
+    // 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,
@@ -96,546 +98,546 @@ public final class PageSettingsBlock ext
     private 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();
-    }
-
-	/**
-	 * @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();
+    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();
+    }
+
+    /**
+     * @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
-	 *
-	 */
-	public void setColumnBreak(short column, short fromRow, short toRow) {
-		getColumnBreaksRecord().addBreak(column, fromRow, toRow);
-	}
-
-	/**
-	 * Removes a page break at the indicated column
-	 *
-	 */
-	public void removeColumnBreak(int column) {
-		getColumnBreaksRecord().removeBreak(column);
-	}
-
-
-
-
-	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(_bitmap, rv);
-		visitIfPresent(_printSize, rv);
-		visitIfPresent(_headerFooter, 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
-	 */
-	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
-	 */
-	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
-	 * @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
-	 *
-	 * @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
-	 * @param endingRow
-	 * @param count
-	 */
-	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
-	 * @param endingCol
-	 * @param count
-	 */
-	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.
-	 */
-	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.
-	 *
-	 * @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;
-			}
-		}
-	}
+            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
+     *
+     */
+    public void setColumnBreak(short column, short fromRow, short toRow) {
+        getColumnBreaksRecord().addBreak(column, fromRow, toRow);
+    }
+
+    /**
+     * Removes a page break at the indicated column
+     *
+     */
+    public void removeColumnBreak(int column) {
+        getColumnBreaksRecord().removeBreak(column);
+    }
+
+
+
+
+    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(_bitmap, rv);
+        visitIfPresent(_printSize, rv);
+        visitIfPresent(_headerFooter, 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
+     */
+    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
+     */
+    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
+     * @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
+     *
+     * @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
+     * @param endingRow
+     * @param count
+     */
+    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
+     * @param endingCol
+     * @param count
+     */
+    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.
+     */
+    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.
+     *
+     * @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.
@@ -652,26 +654,31 @@ public final class PageSettingsBlock ext
         // 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(final HeaderFooterRecord hf : hfRecordsToIterate) {
-            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);
-                                    _sviewHeaderFooters.remove(hf);
-                                }
+        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) {
+                            String guid = HexDump.toHex(((UserSViewBegin) r).getGuid());
+                            HeaderFooterRecord hf = hfGuidMap.get(guid);
+
+                            if (hf != null) {
+                                cv.append(hf);
+                                _sviewHeaderFooters.remove(hf);
                             }
                         }
-                    });
-                }
+                    }
+                });
             }
         }
     }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org