1    
2    /* ====================================================================
3     * The Apache Software License, Version 1.1
4     *
5     * Copyright (c) 2002 The Apache Software Foundation.  All rights
6     * reserved.
7     *
8     * Redistribution and use in source and binary forms, with or without
9     * modification, are permitted provided that the following conditions
10    * are met:
11    *
12    * 1. Redistributions of source code must retain the above copyright
13    *    notice, this list of conditions and the following disclaimer.
14    *
15    * 2. Redistributions in binary form must reproduce the above copyright
16    *    notice, this list of conditions and the following disclaimer in
17    *    the documentation and/or other materials provided with the
18    *    distribution.
19    *
20    * 3. The end-user documentation included with the redistribution,
21    *    if any, must include the following acknowledgment:
22    *       "This product includes software developed by the
23    *        Apache Software Foundation (http://www.apache.org/)."
24    *    Alternately, this acknowledgment may appear in the software itself,
25    *    if and wherever such third-party acknowledgments normally appear.
26    *
27    * 4. The names "Apache" and "Apache Software Foundation" and
28    *    "Apache POI" must not be used to endorse or promote products
29    *    derived from this software without prior written permission. For
30    *    written permission, please contact apache@apache.org.
31    *
32    * 5. Products derived from this software may not be called "Apache",
33    *    "Apache POI", nor may "Apache" appear in their name, without
34    *    prior written permission of the Apache Software Foundation.
35    *
36    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47    * SUCH DAMAGE.
48    * ====================================================================
49    *
50    * This software consists of voluntary contributions made by many
51    * individuals on behalf of the Apache Software Foundation.  For more
52    * information on the Apache Software Foundation, please see
53    * <http://www.apache.org/>.
54    */
55   
56   package org.apache.poi.hssf.eventusermodel;
57   
58   import java.io.InputStream;
59   import java.io.IOException;
60   
61   import org.apache.poi.util.LittleEndian;
62   import org.apache.poi.hssf.eventusermodel.HSSFUserException;
63   import org.apache.poi.hssf.record.RecordFormatException;
64   import org.apache.poi.hssf.record.Record;
65   import org.apache.poi.hssf.record.RecordFactory;
66   import org.apache.poi.hssf.record.ContinueRecord;
67   import org.apache.poi.poifs.filesystem.POIFSFileSystem;
68   
69   /**
70    * Low level event based HSSF reader.  Pass either a DocumentInputStream to
71    * process events along with a request object or pass a POIFS POIFSFileSystem to
72    * processWorkbookEvents along with a request.
73    *
74    * This will cause your file to be processed a record at a time.  Each record with
75    * a static id matching one that you have registed in your HSSFRequest will be passed
76    * to your associated HSSFListener.
77    *
78    * @see org.apache.poi.hssf.dev.EFHSSF
79    *
80    * @author Andrew C. Oliver (acoliver at apache dot org)
81    * @author Carey Sublette  (careysub@earthling.net)
82    */
83   
84   public class HSSFEventFactory
85   {
86       /** Creates a new instance of HSSFEventFactory */
87   
88       public HSSFEventFactory()
89       {
90       }
91   
92       /**
93        * Processes a file into essentially record events.
94        *
95        * @param req       an Instance of HSSFRequest which has your registered listeners
96        * @param fs        a POIFS filesystem containing your workbook
97        */
98   
99       public void processWorkbookEvents(HSSFRequest req, POIFSFileSystem fs)
100          throws IOException
101      {
102          InputStream in = fs.createDocumentInputStream("Workbook");
103  
104          processEvents(req, in);
105      }
106  
107      /**
108  	 * Processes a file into essentially record events.
109  	 *
110  	 * @param req       an Instance of HSSFRequest which has your registered listeners
111  	 * @param fs        a POIFS filesystem containing your workbook
112  	 * @return 			numeric user-specified result code.
113  	 */
114  
115  	public short abortableProcessWorkbookEvents(HSSFRequest req, POIFSFileSystem fs)
116  		throws IOException, HSSFUserException
117  	{
118  		InputStream in = fs.createDocumentInputStream("Workbook");
119  		return abortableProcessEvents(req, in);
120      }
121  
122      /**
123       * Processes a DocumentInputStream into essentially Record events.
124       *
125       * If an <code>AbortableHSSFListener</code> causes a halt to processing during this call
126       * the method will return just as with <code>abortableProcessEvents</code>, but no
127       * user code or <code>HSSFUserException</code> will be passed back.
128       *
129       * @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String)
130       * @param req       an Instance of HSSFRequest which has your registered listeners
131       * @param in        a DocumentInputStream obtained from POIFS's POIFSFileSystem object
132       */
133  
134      public void processEvents(HSSFRequest req, InputStream in)
135          throws IOException
136  	{
137  		try
138  		{
139  			genericProcessEvents(req, in);
140  		}
141  		catch (HSSFUserException hue)
142  		{/*If an HSSFUserException user exception is thrown, ignore it.*/ }
143  	}
144  
145  
146      /**
147       * Processes a DocumentInputStream into essentially Record events.
148       *
149       * @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String)
150       * @param req       an Instance of HSSFRequest which has your registered listeners
151       * @param in        a DocumentInputStream obtained from POIFS's POIFSFileSystem object
152  	 * @return 			numeric user-specified result code.
153       */
154  
155      public short abortableProcessEvents(HSSFRequest req, InputStream in)
156          throws IOException, HSSFUserException
157      {
158  		return genericProcessEvents(req, in);
159      }
160  
161       /**
162  	 * Processes a DocumentInputStream into essentially Record events.
163  	 *
164  	 * @see org.apache.poi.poifs.filesystem.POIFSFileSystem#createDocumentInputStream(String)
165  	 * @param req       an Instance of HSSFRequest which has your registered listeners
166  	 * @param in        a DocumentInputStream obtained from POIFS's POIFSFileSystem object
167  	 * @param in        a DocumentInputStream obtained from POIFS's POIFSFileSystem object
168  	 * @return 			numeric user-specified result code.
169  	 */
170  
171  	protected short genericProcessEvents(HSSFRequest req, InputStream in)
172  		throws IOException, HSSFUserException
173  	{
174  		short userCode = 0;
175  
176  		short sid = 0;
177  		process:
178  		try
179  		{
180  			byte[] sidbytes  = new byte[ 2 ];
181  			int    bytesread = in.read(sidbytes);
182  			Record rec       = null;
183  
184  			while (bytesread > 0)
185  			{
186  
187  				sid = LittleEndian.getShort(sidbytes);
188                  
189                  //
190                  // for some reasons we have to make the workbook to be at least 4096 bytes
191                  // but if we have such workbook we fill the end of it with zeros (many zeros)
192                  //
193                  // it is not good:
194                  // if the length( all zero records ) % 4 = 1
195                  // e.g.: any zero record would be readed as  4 bytes at once ( 2 - id and 2 - size ).
196                  // And the last 1 byte will be readed WRONG ( the id must be 2 bytes )
197                  //
198                  // So we should better to check if the sid is zero and not to read more data
199                  // The zero sid shows us that rest of the stream data is a fake to make workbook 
200                  // certain size
201                  //
202                  if ( sid == 0 )
203                      break;
204  
205  
206  				if ((rec != null) && (sid != ContinueRecord.sid))
207  				{
208  					userCode = req.processRecord(rec);
209  					if (userCode != 0) break process;
210  				}
211  				if (sid != ContinueRecord.sid)
212  				{
213  					short  size = LittleEndian.readShort(in);
214  					byte[] data = new byte[ size ];
215  
216  					if (data.length > 0)
217  					{
218  						in.read(data);
219  					}
220                                          //System.out.println("creating "+sid);
221  					Record[] recs = RecordFactory.createRecord(sid, size,
222  															   data);
223  
224  					if (recs.length > 1)
225  					{                                // we know that the multiple
226  						for (int k = 0; k < (recs.length - 1); k++)
227  						{                            // record situations do not
228  							userCode = req.processRecord(
229  								recs[ k ]);          // contain continue records
230  							if (userCode != 0) break process;
231  						}
232  					}
233  					rec = recs[ recs.length - 1 ];   // regardless we'll process
234  
235  					// the last record as though
236  					// it might be continued
237  					// if there is only one
238  					// records, it will go here too.
239  				}
240  				else
241  				{                                    // we do have a continue record
242  					short  size = LittleEndian.readShort(in);
243  					byte[] data = new byte[ size ];
244  
245  					if (data.length > 0)
246  					{
247  						in.read(data);
248  					}
249  					rec.processContinueRecord(data);
250  				}
251  				bytesread = in.read(sidbytes);       // read next record sid
252  			}
253  			if (rec != null)
254  			{
255  				userCode = req.processRecord(rec);
256  				if (userCode != 0) break process;
257  			}
258  		}
259  		catch (IOException e)
260  		{
261  			throw new RecordFormatException("Error reading bytes" +
262                          "while processing record sid="+sid);
263  		}
264  		return userCode;
265  
266  		// Record[] retval = new Record[ records.size() ];
267  		// retval = ( Record [] ) records.toArray(retval);
268  		// return null;
269      }
270  }
271