KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > poi > hssf > eventmodel > EventRecordFactory


1 /* ====================================================================
2    Copyright 2002-2004 Apache Software Foundation
3
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6    You may obtain a copy of the License at
7
8        http://www.apache.org/licenses/LICENSE-2.0
9
10    Unless required by applicable law or agreed to in writing, software
11    distributed under the License is distributed on an "AS IS" BASIS,
12    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    See the License for the specific language governing permissions and
14    limitations under the License.
15 ==================================================================== */

16
17 package org.apache.poi.hssf.eventmodel;
18
19 import java.io.IOException JavaDoc;
20 import java.io.InputStream JavaDoc;
21 import java.lang.reflect.Constructor JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.Map JavaDoc;
27
28 import org.apache.poi.hssf.record.BOFRecord;
29 import org.apache.poi.hssf.record.BackupRecord;
30 import org.apache.poi.hssf.record.BlankRecord;
31 import org.apache.poi.hssf.record.BookBoolRecord;
32 import org.apache.poi.hssf.record.BoolErrRecord;
33 import org.apache.poi.hssf.record.BottomMarginRecord;
34 import org.apache.poi.hssf.record.BoundSheetRecord;
35 import org.apache.poi.hssf.record.CalcCountRecord;
36 import org.apache.poi.hssf.record.CalcModeRecord;
37 import org.apache.poi.hssf.record.CodepageRecord;
38 import org.apache.poi.hssf.record.ColumnInfoRecord;
39 import org.apache.poi.hssf.record.ContinueRecord;
40 import org.apache.poi.hssf.record.CountryRecord;
41 import org.apache.poi.hssf.record.DBCellRecord;
42 import org.apache.poi.hssf.record.DSFRecord;
43 import org.apache.poi.hssf.record.DateWindow1904Record;
44 import org.apache.poi.hssf.record.DefaultColWidthRecord;
45 import org.apache.poi.hssf.record.DefaultRowHeightRecord;
46 import org.apache.poi.hssf.record.DeltaRecord;
47 import org.apache.poi.hssf.record.DimensionsRecord;
48 import org.apache.poi.hssf.record.EOFRecord;
49 import org.apache.poi.hssf.record.ExtSSTRecord;
50 import org.apache.poi.hssf.record.ExtendedFormatRecord;
51 import org.apache.poi.hssf.record.ExternSheetRecord;
52 import org.apache.poi.hssf.record.FnGroupCountRecord;
53 import org.apache.poi.hssf.record.FontRecord;
54 import org.apache.poi.hssf.record.FooterRecord;
55 import org.apache.poi.hssf.record.FormatRecord;
56 import org.apache.poi.hssf.record.GridsetRecord;
57 import org.apache.poi.hssf.record.GutsRecord;
58 import org.apache.poi.hssf.record.HCenterRecord;
59 import org.apache.poi.hssf.record.HeaderRecord;
60 import org.apache.poi.hssf.record.HideObjRecord;
61 import org.apache.poi.hssf.record.IndexRecord;
62 import org.apache.poi.hssf.record.InterfaceEndRecord;
63 import org.apache.poi.hssf.record.InterfaceHdrRecord;
64 import org.apache.poi.hssf.record.IterationRecord;
65 import org.apache.poi.hssf.record.LabelRecord;
66 import org.apache.poi.hssf.record.LabelSSTRecord;
67 import org.apache.poi.hssf.record.LeftMarginRecord;
68 import org.apache.poi.hssf.record.MMSRecord;
69 import org.apache.poi.hssf.record.MergeCellsRecord;
70 import org.apache.poi.hssf.record.MulBlankRecord;
71 import org.apache.poi.hssf.record.MulRKRecord;
72 import org.apache.poi.hssf.record.NameRecord;
73 import org.apache.poi.hssf.record.NumberRecord;
74 import org.apache.poi.hssf.record.PaletteRecord;
75 import org.apache.poi.hssf.record.PasswordRecord;
76 import org.apache.poi.hssf.record.PasswordRev4Record;
77 import org.apache.poi.hssf.record.PrecisionRecord;
78 import org.apache.poi.hssf.record.PrintGridlinesRecord;
79 import org.apache.poi.hssf.record.PrintHeadersRecord;
80 import org.apache.poi.hssf.record.PrintSetupRecord;
81 import org.apache.poi.hssf.record.ProtectRecord;
82 import org.apache.poi.hssf.record.ProtectionRev4Record;
83 import org.apache.poi.hssf.record.RKRecord;
84 import org.apache.poi.hssf.record.Record;
85 import org.apache.poi.hssf.record.RecordFormatException;
86 import org.apache.poi.hssf.record.RefModeRecord;
87 import org.apache.poi.hssf.record.RefreshAllRecord;
88 import org.apache.poi.hssf.record.RightMarginRecord;
89 import org.apache.poi.hssf.record.RowRecord;
90 import org.apache.poi.hssf.record.SSTRecord;
91 import org.apache.poi.hssf.record.SaveRecalcRecord;
92 import org.apache.poi.hssf.record.SelectionRecord;
93 import org.apache.poi.hssf.record.SharedFormulaRecord;
94 import org.apache.poi.hssf.record.StringRecord;
95 import org.apache.poi.hssf.record.StyleRecord;
96 import org.apache.poi.hssf.record.TabIdRecord;
97 import org.apache.poi.hssf.record.TopMarginRecord;
98 import org.apache.poi.hssf.record.UnknownRecord;
99 import org.apache.poi.hssf.record.UseSelFSRecord;
100 import org.apache.poi.hssf.record.VCenterRecord;
101 import org.apache.poi.hssf.record.WSBoolRecord;
102 import org.apache.poi.hssf.record.WindowOneRecord;
103 import org.apache.poi.hssf.record.WindowProtectRecord;
104 import org.apache.poi.hssf.record.WindowTwoRecord;
105 import org.apache.poi.hssf.record.WriteAccessRecord;
106 import org.apache.poi.util.LittleEndian;
107
108
109 /**
110  * Event-based record factory. As opposed to RecordFactory
111  * this refactored version throws record events as it comes
112  * accross the records. I throws the "lazily" one record behind
113  * to ensure that ContinueRecords are processed first.
114  *
115  * @author Andrew C. Oliver (acoliver@apache.org) - probably to blame for the bugs (so yank his chain on the list)
116  * @author Marc Johnson (mjohnson at apache dot org) - methods taken from RecordFactory
117  * @author Glen Stampoultzis (glens at apache.org) - methods taken from RecordFactory
118  * @author Csaba Nagy (ncsaba at yahoo dot com)
119  */

120 public class EventRecordFactory
121 {
122     
123     /**
124      * contains the classes for all the records we want to parse.
125      */

126     private static final Class JavaDoc[] records;
127
128     static {
129             records = new Class JavaDoc[]
130             {
131                 BOFRecord.class, InterfaceHdrRecord.class, MMSRecord.class,
132                 InterfaceEndRecord.class, WriteAccessRecord.class,
133                 CodepageRecord.class, DSFRecord.class, TabIdRecord.class,
134                 FnGroupCountRecord.class, WindowProtectRecord.class,
135                 ProtectRecord.class, PasswordRecord.class, ProtectionRev4Record.class,
136                 PasswordRev4Record.class, WindowOneRecord.class, BackupRecord.class,
137                 HideObjRecord.class, DateWindow1904Record.class,
138                 PrecisionRecord.class, RefreshAllRecord.class, BookBoolRecord.class,
139                 FontRecord.class, FormatRecord.class, ExtendedFormatRecord.class,
140                 StyleRecord.class, UseSelFSRecord.class, BoundSheetRecord.class,
141                 CountryRecord.class, SSTRecord.class, ExtSSTRecord.class,
142                 EOFRecord.class, IndexRecord.class, CalcModeRecord.class,
143                 CalcCountRecord.class, RefModeRecord.class, IterationRecord.class,
144                 DeltaRecord.class, SaveRecalcRecord.class, PrintHeadersRecord.class,
145                 PrintGridlinesRecord.class, GridsetRecord.class, GutsRecord.class,
146                 DefaultRowHeightRecord.class, WSBoolRecord.class, HeaderRecord.class,
147                 FooterRecord.class, HCenterRecord.class, VCenterRecord.class,
148                 PrintSetupRecord.class, DefaultColWidthRecord.class,
149                 DimensionsRecord.class, RowRecord.class, LabelSSTRecord.class,
150                 RKRecord.class, NumberRecord.class, DBCellRecord.class,
151                 WindowTwoRecord.class, SelectionRecord.class, ContinueRecord.class,
152                 LabelRecord.class, BlankRecord.class, ColumnInfoRecord.class,
153                 MulRKRecord.class, MulBlankRecord.class, MergeCellsRecord.class,
154                 BoolErrRecord.class, ExternSheetRecord.class, NameRecord.class,
155                 LeftMarginRecord.class, RightMarginRecord.class,
156                 TopMarginRecord.class, BottomMarginRecord.class,
157                 PaletteRecord.class, StringRecord.class, SharedFormulaRecord.class
158             };
159        
160     }
161     
162     /**
163      * cache of the recordsToMap();
164      */

165     private static Map JavaDoc recordsMap = recordsToMap(records);
166
167     /**
168      * cache of the return of getAllKnownSids so that we don't have to
169      * expensively get them every time.
170      */

171     private static short[] sidscache;
172
173     /**
174      * List of the listners that are registred. should all be ERFListener
175      */

176     private List JavaDoc listeners;
177
178     /**
179      * instance is abortable or not
180      */

181     private boolean abortable;
182     
183     /**
184      * Construct an abortable EventRecordFactory.
185      * The same as calling new EventRecordFactory(true)
186      * @see #EventRecordFactory(boolean)
187      */

188     public EventRecordFactory() {
189         this(true);
190     }
191     
192     /**
193      * Create an EventRecordFactory
194      * @param abortable specifies whether the return from the listener
195      * handler functions are obeyed. False means they are ignored. True
196      * means the event loop exits on error.
197      */

198     public EventRecordFactory(boolean abortable) {
199         this.abortable = abortable;
200         listeners = new ArrayList JavaDoc(recordsMap.size());
201         
202         if (sidscache == null) {
203          sidscache = getAllKnownRecordSIDs();
204         }
205
206     }
207     
208     /**
209      * Register a listener for records. These can be for all records
210      * or just a subset.
211      *
212      * @param sids an array of Record.sid values identifying the records
213      * the listener will work with. Alternatively if this is "null" then
214      * all records are passed.
215      */

216     public void registerListener(ERFListener listener, short[] sids) {
217       if (sids == null)
218         sids = sidscache;
219       ERFListener wrapped = new ListenerWrapper(listener, sids, abortable);
220       listeners.add(wrapped);
221     }
222     
223     /**
224      * used for unit tests to test the registration of record listeners.
225      * @return Iterator of ERFListeners
226      */

227     protected Iterator JavaDoc listeners() {
228         return listeners.iterator();
229     }
230
231     /**
232      * sends the record event to all registered listeners.
233      * @param record the record to be thrown.
234      * @return boolean abort. If exitability is turned on this aborts
235      * out of the event loop should any listener specify to do so.
236      */

237     private boolean throwRecordEvent(Record record)
238     {
239         boolean result = true;
240         Iterator JavaDoc i = listeners.iterator();
241         
242         while (i.hasNext()) {
243             result = ((ERFListener) i.next()).processRecord(record);
244             if (abortable == true && result == false) {
245                 break;
246             }
247         }
248         return result;
249     }
250
251     /**
252      * Create an array of records from an input stream
253      *
254      * @param in the InputStream from which the records will be
255      * obtained
256      *
257      * @exception RecordFormatException on error processing the
258      * InputStream
259      */

260     public void processRecords(InputStream JavaDoc in)
261         throws RecordFormatException
262     {
263         Record last_record = null;
264
265         try
266         {
267             short rectype = 0;
268
269             do
270             {
271                 rectype = LittleEndian.readShort(in);
272                 if (rectype != 0)
273                 {
274                     short recsize = LittleEndian.readShort(in);
275                     byte[] data = new byte[ ( int ) recsize ];
276
277                     in.read(data);
278                     Record[] recs = createRecord(rectype, recsize,
279                                                  data); // handle MulRK records
280

281                     if (recs.length > 1)
282                     {
283                         for (int k = 0; k < recs.length; k++)
284                         {
285                             if ( last_record != null ) {
286                                 if (throwRecordEvent(last_record) == false && abortable == true) {
287                                  last_record = null;
288                                  break;
289                                 }
290                             }
291                          // records.add(
292
// recs[ k ]); // these will be number records
293
last_record =
294                                 recs[ k ]; // do to keep the algorythm homogenous...you can't
295
} // actually continue a number record anyhow.
296
}
297                     else
298                     {
299                         Record record = recs[ 0 ];
300
301                         if (record != null)
302                         {
303                             if (rectype == ContinueRecord.sid &&
304                                 ! (last_record instanceof ContinueRecord) && // include continuation records after
305
! (last_record instanceof UnknownRecord) ) // unknown records or previous continuation records
306
{
307                                 if (last_record == null)
308                                 {
309                                     throw new RecordFormatException(
310                                         "First record is a ContinueRecord??");
311                                 }
312                                 last_record.processContinueRecord(data);
313                             }
314                             else
315                             {
316                                 if (last_record != null) {
317                                     if (throwRecordEvent(last_record) == false && abortable == true) {
318                                         last_record = null;
319                                         break;
320                                     }
321                                 }
322                                 
323                                 last_record = record;
324                                 
325                                 //records.add(record);
326
}
327                         }
328                     }
329                 }
330             }
331             while (rectype != 0);
332             
333             if (last_record != null) {
334                throwRecordEvent(last_record);
335             }
336         }
337         catch (IOException JavaDoc e)
338         {
339             throw new RecordFormatException("Error reading bytes");
340         }
341
342         // Record[] retval = new Record[ records.size() ];
343
// retval = ( Record [] ) records.toArray(retval);
344

345     }
346
347     /**
348      * create a record, if there are MUL records than multiple records
349      * are returned digested into the non-mul form.
350      */

351     public static Record [] createRecord(short rectype, short size,
352                                          byte [] data)
353     {
354         Record retval = null;
355         Record[] realretval = null;
356
357         try
358         {
359             Constructor JavaDoc constructor =
360                 ( Constructor JavaDoc ) recordsMap.get(new Short JavaDoc(rectype));
361
362             if (constructor != null)
363             {
364                 retval = ( Record ) constructor.newInstance(new Object JavaDoc[]
365                 {
366                     new Short JavaDoc(rectype), new Short JavaDoc(size), data
367                 });
368             }
369             else
370             {
371                 retval = new UnknownRecord(rectype, size, data);
372             }
373         }
374         catch (Exception JavaDoc introspectionException)
375         {
376             introspectionException.printStackTrace();
377             throw new RecordFormatException(
378                 "Unable to construct record instance, the following exception occured: " + introspectionException.getMessage());
379         }
380         if (retval instanceof RKRecord)
381         {
382             RKRecord rk = ( RKRecord ) retval;
383             NumberRecord num = new NumberRecord();
384
385             num.setColumn(rk.getColumn());
386             num.setRow(rk.getRow());
387             num.setXFIndex(rk.getXFIndex());
388             num.setValue(rk.getRKNumber());
389             retval = num;
390         }
391         else if (retval instanceof DBCellRecord)
392         {
393             retval = null;
394         }
395         else if (retval instanceof MulRKRecord)
396         {
397             MulRKRecord mrk = ( MulRKRecord ) retval;
398
399             realretval = new Record[ mrk.getNumColumns() ];
400             for (int k = 0; k < mrk.getNumColumns(); k++)
401             {
402                 NumberRecord nr = new NumberRecord();
403
404                 nr.setColumn(( short ) (k + mrk.getFirstColumn()));
405                 nr.setRow(mrk.getRow());
406                 nr.setXFIndex(mrk.getXFAt(k));
407                 nr.setValue(mrk.getRKNumberAt(k));
408                 realretval[ k ] = nr;
409             }
410         }
411         else if (retval instanceof MulBlankRecord)
412         {
413             MulBlankRecord mb = ( MulBlankRecord ) retval;
414
415             realretval = new Record[ mb.getNumColumns() ];
416             for (int k = 0; k < mb.getNumColumns(); k++)
417             {
418                 BlankRecord br = new BlankRecord();
419
420                 br.setColumn(( short ) (k + mb.getFirstColumn()));
421                 br.setRow(mb.getRow());
422                 br.setXFIndex(mb.getXFAt(k));
423                 realretval[ k ] = br;
424             }
425         }
426         if (realretval == null)
427         {
428             realretval = new Record[ 1 ];
429             realretval[ 0 ] = retval;
430         }
431         return realretval;
432     }
433
434     /**
435      * @return an array of all the SIDS for all known records
436      */

437     public static short [] getAllKnownRecordSIDs()
438     {
439         short[] results = new short[ recordsMap.size() ];
440         int i = 0;
441
442         for (Iterator JavaDoc iterator = recordsMap.keySet().iterator();
443                 iterator.hasNext(); )
444         {
445             Short JavaDoc sid = ( Short JavaDoc ) iterator.next();
446
447             results[ i++ ] = sid.shortValue();
448         }
449         return results;
450     }
451
452     /**
453      * gets the record constructors and sticks them in the map by SID
454      * @return map of SIDs to short,short,byte[] constructors for Record classes
455      * most of org.apache.poi.hssf.record.*
456      */

457     private static Map JavaDoc recordsToMap(Class JavaDoc [] records)
458     {
459         Map JavaDoc result = new HashMap JavaDoc();
460         Constructor JavaDoc constructor;
461
462         for (int i = 0; i < records.length; i++)
463         {
464             Class JavaDoc record = null;
465             short sid = 0;
466
467             record = records[ i ];
468             try
469             {
470                 sid = record.getField("sid").getShort(null);
471                 constructor = record.getConstructor(new Class JavaDoc[]
472                 {
473                     short.class, short.class, byte [].class
474                 });
475             }
476             catch (Exception JavaDoc illegalArgumentException)
477             {
478                 throw new RecordFormatException(
479                     "Unable to determine record types");
480             }
481             result.put(new Short JavaDoc(sid), constructor);
482         }
483         return result;
484     }
485
486 }
487
488 /**
489  * ListenerWrapper just wraps an ERFListener and adds support for throwing
490  * the event to multiple SIDs
491  */

492 class ListenerWrapper implements ERFListener {
493        private ERFListener listener;
494        private short[] sids;
495        private boolean abortable;
496
497     ListenerWrapper(ERFListener listener, short[] sids, boolean abortable) {
498         this.listener = listener;
499         this.sids = sids;
500         this.abortable = abortable;
501     }
502     
503
504     public boolean processRecord(Record rec)
505     {
506         boolean result = true;
507         for (int k = 0; k < sids.length; k++) {
508             if (sids[k] == rec.getSid()) {
509                 result = listener.processRecord(rec);
510             
511                 if (abortable == true && result == false) {
512                     break;
513                 }
514             }
515         }
516         return result;
517     }
518 }
519
Popular Tags