You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ni...@apache.org on 2008/01/31 18:30:24 UTC

svn commit: r617167 - in /poi/trunk/src: documentation/content/xdocs/ java/org/apache/poi/hssf/eventusermodel/ testcases/org/apache/poi/hssf/eventusermodel/

Author: nick
Date: Thu Jan 31 09:30:16 2008
New Revision: 617167

URL: http://svn.apache.org/viewvc?rev=617167&view=rev
Log:
Convert HSSFEventFactory to using the new HSSFRecordStream, which returns fully-formed HSSFRecords. HSSFRecordStream allows for pull-style eventusermodel processing

Added:
    poi/trunk/src/java/org/apache/poi/hssf/eventusermodel/HSSFRecordStream.java   (with props)
Modified:
    poi/trunk/src/documentation/content/xdocs/changes.xml
    poi/trunk/src/documentation/content/xdocs/status.xml
    poi/trunk/src/java/org/apache/poi/hssf/eventusermodel/HSSFEventFactory.java
    poi/trunk/src/testcases/org/apache/poi/hssf/eventusermodel/TestHSSFEventFactory.java

Modified: poi/trunk/src/documentation/content/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/poi/trunk/src/documentation/content/xdocs/changes.xml?rev=617167&r1=617166&r2=617167&view=diff
==============================================================================
--- poi/trunk/src/documentation/content/xdocs/changes.xml (original)
+++ poi/trunk/src/documentation/content/xdocs/changes.xml Thu Jan 31 09:30:16 2008
@@ -36,8 +36,7 @@
 
 		<!-- Don't forget to update status.xml too! -->
         <release version="3.1-beta1" date="2008-??-??">
-           <!-- an action tag is requered to keep Forrest quite.  --> 
-           <action dev="POI-DEVELOPERS" type="fix">...</action>
+           <action dev="POI-DEVELOPERS" type="add">Support for a completed Record based "pull" stream, via org.apache.poi.hssf.eventusermodel.HSSFRecordStream, to complement the existing "push" Event User Model listener stuff</action>
         </release>
         <release version="3.0.2-FINAL" date="2008-02-04">
             <action dev="POI-DEVELOPERS" type="fix">44297 - IntPtg must operate with unsigned short. Reading signed short results in incorrect formula calculation</action>

Modified: poi/trunk/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/trunk/src/documentation/content/xdocs/status.xml?rev=617167&r1=617166&r2=617167&view=diff
==============================================================================
--- poi/trunk/src/documentation/content/xdocs/status.xml (original)
+++ poi/trunk/src/documentation/content/xdocs/status.xml Thu Jan 31 09:30:16 2008
@@ -33,6 +33,7 @@
 	<!-- Don't forget to update changes.xml too! -->
     <changes>
         <release version="3.1-beta1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="add">Support for a completed Record based "pull" stream, via org.apache.poi.hssf.eventusermodel.HSSFRecordStream, to complement the existing "push" Event User Model listener stuff</action>
         </release>
         <release version="3.0.2-FINAL" date="2008-02-04">
             <action dev="POI-DEVELOPERS" type="fix">44297 - IntPtg must operate with unsigned short. Reading signed short results in incorrect formula calculation</action>

Modified: poi/trunk/src/java/org/apache/poi/hssf/eventusermodel/HSSFEventFactory.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/eventusermodel/HSSFEventFactory.java?rev=617167&r1=617166&r2=617167&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/eventusermodel/HSSFEventFactory.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/eventusermodel/HSSFEventFactory.java Thu Jan 31 09:30:16 2008
@@ -129,113 +129,25 @@
 	protected short genericProcessEvents(HSSFRequest req, RecordInputStream in)
 		throws IOException, HSSFUserException
 	{
+		boolean going = true;
 		short userCode = 0;
-
-		short sid = 0;
-		process:
-		{
-                  
-			Record rec       = null;
-			Record lastRec   = null;
-			DrawingRecord lastDrawingRecord = new DrawingRecord();
-
-			while (in.hasNextRecord())
-			{
-				in.nextRecord();
-				sid = in.getSid();;
-
-                //
-                // for some reasons we have to make the workbook to be at least 4096 bytes
-                // but if we have such workbook we fill the end of it with zeros (many zeros)
-                //
-                // it is not good:
-                // if the length( all zero records ) % 4 = 1
-                // e.g.: any zero record would be readed as  4 bytes at once ( 2 - id and 2 - size ).
-                // And the last 1 byte will be readed WRONG ( the id must be 2 bytes )
-                //
-                // So we should better to check if the sid is zero and not to read more data
-                // The zero sid shows us that rest of the stream data is a fake to make workbook 
-                // certain size
-                //
-                if ( sid == 0 )
-                    break;
-
-
-                // If we had a last record, and this one
-                //  isn't a continue record, then pass
-                //  it on to the listener
-				if ((rec != null) && (sid != ContinueRecord.sid))
-				{
-					userCode = req.processRecord(rec);
-					if (userCode != 0) break process;
-				}
-				
-				// If this record isn't a continue record,
-				//  then build it up
-				if (sid != ContinueRecord.sid)
-				{
-					//System.out.println("creating "+sid);
-					Record[] recs = RecordFactory.createRecord(in);
-
-					// We know that the multiple record situations
-					//  don't contain continue records, so just
-					//  pass those on to the listener now
-					if (recs.length > 1) {
-						for (int k = 0; k < (recs.length - 1); k++)	{
-							userCode = req.processRecord(
-								recs[ k ]);
-							if (userCode != 0) break process;
-						}
-					}
-					
-					// Regardless of the number we created, always hold
-					//  onto the last record to be processed on the next
-					//  loop, in case it has any continue records
-					rec = recs[ recs.length - 1 ];
-				}
-				else {
-					// Normally, ContinueRecords are handled internally
-					// However, in a few cases, there is a gap between a record at
-					//  its Continue, so we have to handle them specially
-					// This logic is much like in RecordFactory.createRecords()
-					Record[] recs = RecordFactory.createRecord(in);
-					ContinueRecord crec = (ContinueRecord)recs[0];
-					if((lastRec instanceof ObjRecord) || (lastRec instanceof TextObjectRecord)) {
-						// You can have Obj records between a DrawingRecord
-						//  and its continue!
-						lastDrawingRecord.processContinueRecord( crec.getData() );
-						// Trigger them on the drawing record, now it's complete
-						rec = lastDrawingRecord;
-					}
-					else if((lastRec instanceof DrawingGroupRecord)) {
-						((DrawingGroupRecord)lastRec).processContinueRecord(crec.getData());
-						// Trigger them on the drawing record, now it's complete
-						rec = lastRec;
-					}
-					else {
-                        if (rec instanceof UnknownRecord) {
-                            ;//silently skip records we don't know about
-                        } else {
-						    throw new RecordFormatException("Records should handle ContinueRecord internally. Should not see this exception");
-                        }
-					}
-				}
-
-				// Update our tracking of the last record
-				lastRec = rec;
-				if(rec instanceof DrawingRecord) {
-					lastDrawingRecord = (DrawingRecord)rec;
-				}
-			} // main while loop
-			
-			// Process the last record in the stream, if
-			//  it's still outstanding
-			if (rec != null) {
-				userCode = req.processRecord(rec);
-				if (userCode != 0) break process;
+		Record r = null;
+		
+		// Create a new RecordStream and use that
+		HSSFRecordStream recordStream = new HSSFRecordStream(in);
+		
+		// Process each record as they come in
+		while(going) {
+			r = recordStream.nextRecord();
+			if(r != null) {
+				userCode = req.processRecord(r);
+				if (userCode != 0) break;
+			} else {
+				going = false;
 			}
 		}
 		
+		// All done, return our last code
 		return userCode;
     }
 }

Added: poi/trunk/src/java/org/apache/poi/hssf/eventusermodel/HSSFRecordStream.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/eventusermodel/HSSFRecordStream.java?rev=617167&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/eventusermodel/HSSFRecordStream.java (added)
+++ poi/trunk/src/java/org/apache/poi/hssf/eventusermodel/HSSFRecordStream.java Thu Jan 31 09:30:16 2008
@@ -0,0 +1,234 @@
+/* ====================================================================
+   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.eventusermodel;
+
+import java.util.Vector;
+
+import org.apache.poi.hssf.record.ContinueRecord;
+import org.apache.poi.hssf.record.DrawingGroupRecord;
+import org.apache.poi.hssf.record.DrawingRecord;
+import org.apache.poi.hssf.record.ObjRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.RecordFactory;
+import org.apache.poi.hssf.record.RecordFormatException;
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.hssf.record.TextObjectRecord;
+import org.apache.poi.hssf.record.UnknownRecord;
+
+/**
+ * A stream based way to get at complete records, with
+ *  as low a memory footprint as possible.
+ * This handles reading from a RecordInputStream, turning
+ *  the data into full records, processing continue records
+ *  etc.
+ * Most users should use {@link HSSFEventFactory} /
+ *  {@link HSSFListener} and have new records pushed to
+ *  them, but this does allow for a "pull" style of coding.  
+ */
+public class HSSFRecordStream {
+	private RecordInputStream in;
+
+	/** Have we run out of records on the stream? */
+	private boolean hitEOS = false;
+	/** Have we returned all the records there are? */
+	private boolean complete = false;
+	
+	/**
+	 * Sometimes we end up with a bunch of
+	 *  records. When we do, these should
+	 *  be returned before the next normal
+	 *  record processing occurs (i.e. before
+	 *  we check for continue records and
+	 *  return rec)
+	 */
+	private Vector bonusRecords = null;
+	
+	/** 
+	 * The next record to return, which may need to have its
+	 *  continue records passed to it before we do
+	 */
+	private Record rec = null;
+	/**
+	 * The most recent record that we gave to the user
+	 */
+	private Record lastRec = null;
+	/**
+	 * The most recent DrawingRecord seen
+	 */
+	private DrawingRecord lastDrawingRecord = new DrawingRecord();
+	
+	public HSSFRecordStream(RecordInputStream inp) {
+		this.in = inp;
+	}
+
+	/**
+	 * Returns the next (complete) record from the 
+	 *  stream, or null if there are no more.
+	 */
+	public Record nextRecord() {
+		Record r = null;
+		
+		// Loop until we get something
+		while(r == null && !complete) {
+			// Are there any bonus records that we need to
+			//  return?
+			r = getBonusRecord();
+			
+			// If not, ask for the next real record
+			if(r == null) {
+				r = getNextRecord();
+			}
+		}
+		
+		// All done
+		return r;
+	}
+	
+	/**
+	 * If there are any "bonus" records, that should
+	 *  be returned before processing new ones, 
+	 *  grabs the next and returns it.
+	 * If not, returns null;
+	 */
+	private Record getBonusRecord() {
+		if(bonusRecords != null) {
+			Record r = (Record)bonusRecords.remove(0);
+			if(bonusRecords.size() == 0) {
+				bonusRecords = null;
+			}
+			return r;
+		}
+		return null;
+	}
+	
+	/**
+	 * Returns the next available record, or null if
+	 *  this pass didn't return a record that's
+	 *  suitable for returning (eg was a continue record).
+	 */
+	private Record getNextRecord() {
+		Record toReturn = null;
+		
+		if(in.hasNextRecord()) {
+			// Grab our next record
+			in.nextRecord();
+			short sid = in.getSid();
+			
+            //
+            // for some reasons we have to make the workbook to be at least 4096 bytes
+            // but if we have such workbook we fill the end of it with zeros (many zeros)
+            //
+            // it is not good:
+            // if the length( all zero records ) % 4 = 1
+            // e.g.: any zero record would be readed as  4 bytes at once ( 2 - id and 2 - size ).
+            // And the last 1 byte will be readed WRONG ( the id must be 2 bytes )
+            //
+            // So we should better to check if the sid is zero and not to read more data
+            // The zero sid shows us that rest of the stream data is a fake to make workbook 
+            // certain size
+            //
+            if ( sid == 0 )
+                return null;
+
+
+            // If we had a last record, and this one
+            //  isn't a continue record, then pass
+            //  it on to the listener
+			if ((rec != null) && (sid != ContinueRecord.sid))
+			{
+				// This last record ought to be returned
+				toReturn = rec;
+			}
+			
+			// If this record isn't a continue record,
+			//  then build it up
+			if (sid != ContinueRecord.sid)
+			{
+				//System.out.println("creating "+sid);
+				Record[] recs = RecordFactory.createRecord(in);
+
+				// We know that the multiple record situations
+				//  don't contain continue records, so just
+				//  pass those on to the listener now
+				if (recs.length > 1) {
+					bonusRecords = new Vector(recs.length-1);
+					for (int k = 0; k < (recs.length - 1); k++)	{
+						bonusRecords.add(recs[k]);
+					}
+				}
+				
+				// Regardless of the number we created, always hold
+				//  onto the last record to be processed on the next
+				//  loop, in case it has any continue records
+				rec = recs[ recs.length - 1 ];
+				// Don't return it just yet though, as we probably have
+				//  a record from the last round to return
+			}
+			else {
+				// Normally, ContinueRecords are handled internally
+				// However, in a few cases, there is a gap between a record at
+				//  its Continue, so we have to handle them specially
+				// This logic is much like in RecordFactory.createRecords()
+				Record[] recs = RecordFactory.createRecord(in);
+				ContinueRecord crec = (ContinueRecord)recs[0];
+				if((lastRec instanceof ObjRecord) || (lastRec instanceof TextObjectRecord)) {
+					// You can have Obj records between a DrawingRecord
+					//  and its continue!
+					lastDrawingRecord.processContinueRecord( crec.getData() );
+					// Trigger them on the drawing record, now it's complete
+					rec = lastDrawingRecord;
+				}
+				else if((lastRec instanceof DrawingGroupRecord)) {
+					((DrawingGroupRecord)lastRec).processContinueRecord(crec.getData());
+					// Trigger them on the drawing record, now it's complete
+					rec = lastRec;
+				}
+				else {
+                    if (rec instanceof UnknownRecord) {
+                        ;//silently skip records we don't know about
+                    } else {
+					    throw new RecordFormatException("Records should handle ContinueRecord internally. Should not see this exception");
+                    }
+				}
+			}
+
+			// Update our tracking of the last record
+			lastRec = rec;
+			if(rec instanceof DrawingRecord) {
+				lastDrawingRecord = (DrawingRecord)rec;
+			}
+		} else {
+			// No more records
+			hitEOS = true;
+		}
+		
+		// If we've hit the end-of-stream, then
+		//  finish off the last record and be done
+		if(hitEOS) {
+			complete = true;
+			
+			// Return the last record if there was
+			//  one, otherwise null
+			if(rec != null) {
+				toReturn = rec;
+				rec = null;
+			}
+		}
+			
+		return toReturn;
+	}
+}
\ No newline at end of file

Propchange: poi/trunk/src/java/org/apache/poi/hssf/eventusermodel/HSSFRecordStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/testcases/org/apache/poi/hssf/eventusermodel/TestHSSFEventFactory.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/eventusermodel/TestHSSFEventFactory.java?rev=617167&r1=617166&r2=617167&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/eventusermodel/TestHSSFEventFactory.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/eventusermodel/TestHSSFEventFactory.java Thu Jan 31 09:30:16 2008
@@ -23,8 +23,13 @@
 import java.io.FileInputStream;
 import java.util.ArrayList;
 
+import org.apache.poi.hssf.record.DVALRecord;
+import org.apache.poi.hssf.record.DVRecord;
+import org.apache.poi.hssf.record.EOFRecord;
 import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.ContinueRecord;
+import org.apache.poi.hssf.record.SelectionRecord;
+import org.apache.poi.hssf.record.WindowTwoRecord;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
 import junit.framework.TestCase;
@@ -48,7 +53,15 @@
 		factory.processWorkbookEvents(req, fs);
 
 		// Check we got the records
+		System.out.println("Processed, found " + mockListen.records.size() + " records");
 		assertTrue( mockListen.records.size() > 100 );
+		
+		// Check that the last few records are as we expect
+		// (Makes sure we don't accidently skip the end ones)
+		int numRec = mockListen.records.size();
+		assertEquals(WindowTwoRecord.class, mockListen.records.get(numRec-3).getClass());
+		assertEquals(SelectionRecord.class, mockListen.records.get(numRec-2).getClass());
+		assertEquals(EOFRecord.class,       mockListen.records.get(numRec-1).getClass());
 	}
 
 	public void testWithCrazyContinueRecords() throws Exception {
@@ -66,6 +79,7 @@
 		factory.processWorkbookEvents(req, fs);
 
 		// Check we got the records
+		System.out.println("Processed, found " + mockListen.records.size() + " records");
 		assertTrue( mockListen.records.size() > 100 );
 
 		// And none of them are continue ones
@@ -74,6 +88,13 @@
 		for(int i=0; i<r.length; i++) {
 			assertFalse( r[i] instanceof ContinueRecord );
 		}
+		
+		// Check that the last few records are as we expect
+		// (Makes sure we don't accidently skip the end ones)
+		int numRec = mockListen.records.size();
+		assertEquals(DVALRecord.class, mockListen.records.get(numRec-3).getClass());
+		assertEquals(DVRecord.class, mockListen.records.get(numRec-2).getClass());
+		assertEquals(EOFRecord.class,       mockListen.records.get(numRec-1).getClass());
 	}
 
     /**



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