1 /* ====================================================================
2 Licensed to the Apache Software Foundation (ASF) under one or more
3 contributor license agreements. See the NOTICE file distributed with
4 this work for additional information regarding copyright ownership.
5 The ASF licenses this file to You under the Apache License, Version 2.0
6 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
9 http://www.apache.org/licenses/LICENSE-2.0
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 ==================================================================== */
18 package org
.apache
.poi
.hssf
.eventmodel
;
20 import java
.io
.InputStream
;
21 import java
.lang
.reflect
.Constructor
;
22 import java
.util
.ArrayList
;
23 import java
.util
.HashMap
;
24 import java
.util
.Iterator
;
25 import java
.util
.List
;
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
.PaneRecord
;
75 import org
.apache
.poi
.hssf
.record
.PaletteRecord
;
76 import org
.apache
.poi
.hssf
.record
.PasswordRecord
;
77 import org
.apache
.poi
.hssf
.record
.PasswordRev4Record
;
78 import org
.apache
.poi
.hssf
.record
.PrecisionRecord
;
79 import org
.apache
.poi
.hssf
.record
.PrintGridlinesRecord
;
80 import org
.apache
.poi
.hssf
.record
.PrintHeadersRecord
;
81 import org
.apache
.poi
.hssf
.record
.PrintSetupRecord
;
82 import org
.apache
.poi
.hssf
.record
.ProtectRecord
;
83 import org
.apache
.poi
.hssf
.record
.ProtectionRev4Record
;
84 import org
.apache
.poi
.hssf
.record
.RKRecord
;
85 import org
.apache
.poi
.hssf
.record
.Record
;
86 import org
.apache
.poi
.hssf
.record
.RecordFormatException
;
87 import org
.apache
.poi
.hssf
.record
.RecordInputStream
;
88 import org
.apache
.poi
.hssf
.record
.RefModeRecord
;
89 import org
.apache
.poi
.hssf
.record
.RefreshAllRecord
;
90 import org
.apache
.poi
.hssf
.record
.RightMarginRecord
;
91 import org
.apache
.poi
.hssf
.record
.RowRecord
;
92 import org
.apache
.poi
.hssf
.record
.SSTRecord
;
93 import org
.apache
.poi
.hssf
.record
.SaveRecalcRecord
;
94 import org
.apache
.poi
.hssf
.record
.SelectionRecord
;
95 import org
.apache
.poi
.hssf
.record
.SharedFormulaRecord
;
96 import org
.apache
.poi
.hssf
.record
.StringRecord
;
97 import org
.apache
.poi
.hssf
.record
.StyleRecord
;
98 import org
.apache
.poi
.hssf
.record
.TabIdRecord
;
99 import org
.apache
.poi
.hssf
.record
.TableRecord
;
100 import org
.apache
.poi
.hssf
.record
.TopMarginRecord
;
101 import org
.apache
.poi
.hssf
.record
.UnknownRecord
;
102 import org
.apache
.poi
.hssf
.record
.UseSelFSRecord
;
103 import org
.apache
.poi
.hssf
.record
.VCenterRecord
;
104 import org
.apache
.poi
.hssf
.record
.WSBoolRecord
;
105 import org
.apache
.poi
.hssf
.record
.WindowOneRecord
;
106 import org
.apache
.poi
.hssf
.record
.WindowProtectRecord
;
107 import org
.apache
.poi
.hssf
.record
.WindowTwoRecord
;
108 import org
.apache
.poi
.hssf
.record
.WriteAccessRecord
;
109 import org
.apache
.poi
.hssf
.record
.WriteProtectRecord
;
110 import org
.apache
.poi
.hssf
.record
.FilePassRecord
;
111 import org
.apache
.poi
.hssf
.record
.NoteRecord
;
115 * Event-based record factory. As opposed to RecordFactory
116 * this refactored version throws record events as it comes
117 * accross the records. I throws the "lazily" one record behind
118 * to ensure that ContinueRecords are processed first.
120 * @author Andrew C. Oliver (acoliver@apache.org) - probably to blame for the bugs (so yank his chain on the list)
121 * @author Marc Johnson (mjohnson at apache dot org) - methods taken from RecordFactory
122 * @author Glen Stampoultzis (glens at apache.org) - methods taken from RecordFactory
123 * @author Csaba Nagy (ncsaba at yahoo dot com)
125 public class EventRecordFactory
129 * contains the classes for all the records we want to parse.
131 private static final Class
[] records
;
134 records
= new Class
[]
136 BOFRecord
.class, InterfaceHdrRecord
.class, MMSRecord
.class,
137 InterfaceEndRecord
.class, WriteAccessRecord
.class,
138 CodepageRecord
.class, DSFRecord
.class, TabIdRecord
.class,
139 FnGroupCountRecord
.class, WindowProtectRecord
.class,
140 ProtectRecord
.class, PasswordRecord
.class, ProtectionRev4Record
.class,
141 PasswordRev4Record
.class, WindowOneRecord
.class, BackupRecord
.class,
142 HideObjRecord
.class, DateWindow1904Record
.class,
143 PrecisionRecord
.class, RefreshAllRecord
.class, BookBoolRecord
.class,
144 FontRecord
.class, FormatRecord
.class, ExtendedFormatRecord
.class,
145 StyleRecord
.class, UseSelFSRecord
.class, BoundSheetRecord
.class,
146 CountryRecord
.class, SSTRecord
.class, ExtSSTRecord
.class,
147 EOFRecord
.class, IndexRecord
.class, CalcModeRecord
.class,
148 CalcCountRecord
.class, RefModeRecord
.class, IterationRecord
.class,
149 DeltaRecord
.class, SaveRecalcRecord
.class, PrintHeadersRecord
.class,
150 PrintGridlinesRecord
.class, GridsetRecord
.class, GutsRecord
.class,
151 DefaultRowHeightRecord
.class, WSBoolRecord
.class, HeaderRecord
.class,
152 FooterRecord
.class, HCenterRecord
.class, VCenterRecord
.class,
153 PrintSetupRecord
.class, DefaultColWidthRecord
.class,
154 DimensionsRecord
.class, RowRecord
.class, LabelSSTRecord
.class,
155 RKRecord
.class, NumberRecord
.class, DBCellRecord
.class,
156 WindowTwoRecord
.class, SelectionRecord
.class, ContinueRecord
.class,
157 LabelRecord
.class, BlankRecord
.class, ColumnInfoRecord
.class,
158 MulRKRecord
.class, MulBlankRecord
.class, MergeCellsRecord
.class,
159 BoolErrRecord
.class, ExternSheetRecord
.class, NameRecord
.class,
160 LeftMarginRecord
.class, RightMarginRecord
.class,
161 TopMarginRecord
.class, BottomMarginRecord
.class,
162 PaletteRecord
.class, StringRecord
.class, SharedFormulaRecord
.class,
163 WriteProtectRecord
.class, FilePassRecord
.class, PaneRecord
.class,
164 NoteRecord
.class, TableRecord
.class
170 * cache of the recordsToMap();
172 private static Map recordsMap
= recordsToMap(records
);
175 * cache of the return of getAllKnownSids so that we don't have to
176 * expensively get them every time.
178 private static short[] sidscache
;
181 * List of the listners that are registred. should all be ERFListener
183 private List listeners
;
186 * instance is abortable or not
188 private boolean abortable
;
191 * Construct an abortable EventRecordFactory.
192 * The same as calling new EventRecordFactory(true)
193 * @see #EventRecordFactory(boolean)
195 public EventRecordFactory() {
200 * Create an EventRecordFactory
201 * @param abortable specifies whether the return from the listener
202 * handler functions are obeyed. False means they are ignored. True
203 * means the event loop exits on error.
205 public EventRecordFactory(boolean abortable
) {
206 this.abortable
= abortable
;
207 listeners
= new ArrayList(recordsMap
.size());
209 if (sidscache
== null) {
210 sidscache
= getAllKnownRecordSIDs();
216 * Register a listener for records. These can be for all records
219 * @param sids an array of Record.sid values identifying the records
220 * the listener will work with. Alternatively if this is "null" then
221 * all records are passed.
223 public void registerListener(ERFListener listener
, short[] sids
) {
226 ERFListener wrapped
= new ListenerWrapper(listener
, sids
, abortable
);
227 listeners
.add(wrapped
);
231 * used for unit tests to test the registration of record listeners.
232 * @return Iterator of ERFListeners
234 protected Iterator
listeners() {
235 return listeners
.iterator();
239 * sends the record event to all registered listeners.
240 * @param record the record to be thrown.
241 * @return boolean abort. If exitability is turned on this aborts
242 * out of the event loop should any listener specify to do so.
244 private boolean throwRecordEvent(Record record
)
246 boolean result
= true;
247 Iterator i
= listeners
.iterator();
249 while (i
.hasNext()) {
250 result
= ((ERFListener
) i
.next()).processRecord(record
);
251 if (abortable
== true && result
== false) {
259 * Create an array of records from an input stream
261 * @param in the InputStream from which the records will be
264 * @exception RecordFormatException on error processing the
267 public void processRecords(InputStream in
)
268 throws RecordFormatException
270 Record last_record
= null;
272 RecordInputStream recStream
= new RecordInputStream(in
);
274 while (recStream
.hasNextRecord()) {
275 recStream
.nextRecord();
276 Record
[] recs
= createRecord(recStream
); // handle MulRK records
279 for (int k
= 0; k
< recs
.length
; k
++)
281 if ( last_record
!= null ) {
282 if (throwRecordEvent(last_record
) == false && abortable
== true) {
288 recs
[ k
]; // do to keep the algorythm homogenous...you can't
289 } // actually continue a number record anyhow.
293 Record record
= recs
[ 0 ];
297 if (last_record
!= null) {
298 if (throwRecordEvent(last_record
) == false && abortable
== true) {
304 last_record
= record
;
310 if (last_record
!= null) {
311 throwRecordEvent(last_record
);
316 * create a record, if there are MUL records than multiple records
317 * are returned digested into the non-mul form.
319 public static Record
[] createRecord(RecordInputStream in
)
321 Record retval
= null;
322 Record
[] realretval
= null;
326 Constructor constructor
=
327 ( Constructor
) recordsMap
.get(new Short(in
.getSid()));
329 if (constructor
!= null)
331 retval
= ( Record
) constructor
.newInstance(new Object
[]
338 retval
= new UnknownRecord(in
);
341 catch (Exception introspectionException
)
343 throw new RecordFormatException("Unable to construct record instance" , introspectionException
);
345 if (retval
instanceof RKRecord
)
347 RKRecord rk
= ( RKRecord
) retval
;
348 NumberRecord num
= new NumberRecord();
350 num
.setColumn(rk
.getColumn());
351 num
.setRow(rk
.getRow());
352 num
.setXFIndex(rk
.getXFIndex());
353 num
.setValue(rk
.getRKNumber());
356 else if (retval
instanceof DBCellRecord
)
360 else if (retval
instanceof MulRKRecord
)
362 MulRKRecord mrk
= ( MulRKRecord
) retval
;
364 realretval
= new Record
[ mrk
.getNumColumns() ];
365 for (int k
= 0; k
< mrk
.getNumColumns(); k
++)
367 NumberRecord nr
= new NumberRecord();
369 nr
.setColumn(( short ) (k
+ mrk
.getFirstColumn()));
370 nr
.setRow(mrk
.getRow());
371 nr
.setXFIndex(mrk
.getXFAt(k
));
372 nr
.setValue(mrk
.getRKNumberAt(k
));
373 realretval
[ k
] = nr
;
376 else if (retval
instanceof MulBlankRecord
)
378 MulBlankRecord mb
= ( MulBlankRecord
) retval
;
380 realretval
= new Record
[ mb
.getNumColumns() ];
381 for (int k
= 0; k
< mb
.getNumColumns(); k
++)
383 BlankRecord br
= new BlankRecord();
385 br
.setColumn(( short ) (k
+ mb
.getFirstColumn()));
386 br
.setRow(mb
.getRow());
387 br
.setXFIndex(mb
.getXFAt(k
));
388 realretval
[ k
] = br
;
391 if (realretval
== null)
393 realretval
= new Record
[ 1 ];
394 realretval
[ 0 ] = retval
;
400 * @return an array of all the SIDS for all known records
402 public static short [] getAllKnownRecordSIDs()
404 short[] results
= new short[ recordsMap
.size() ];
407 for (Iterator iterator
= recordsMap
.keySet().iterator();
408 iterator
.hasNext(); )
410 Short sid
= ( Short
) iterator
.next();
412 results
[ i
++ ] = sid
.shortValue();
418 * gets the record constructors and sticks them in the map by SID
419 * @return map of SIDs to short,short,byte[] constructors for Record classes
420 * most of org.apache.poi.hssf.record.*
422 private static Map
recordsToMap(Class
[] records
)
424 Map result
= new HashMap();
425 Constructor constructor
;
427 for (int i
= 0; i
< records
.length
; i
++)
432 record
= records
[ i
];
435 sid
= record
.getField("sid").getShort(null);
436 constructor
= record
.getConstructor(new Class
[]
438 RecordInputStream
.class
441 catch (Exception illegalArgumentException
)
443 throw new RecordFormatException(
444 "Unable to determine record types");
446 result
.put(new Short(sid
), constructor
);
454 * ListenerWrapper just wraps an ERFListener and adds support for throwing
455 * the event to multiple SIDs
457 class ListenerWrapper
implements ERFListener
{
458 private ERFListener listener
;
459 private short[] sids
;
460 private boolean abortable
;
462 ListenerWrapper(ERFListener listener
, short[] sids
, boolean abortable
) {
463 this.listener
= listener
;
465 this.abortable
= abortable
;
469 public boolean processRecord(Record rec
)
471 boolean result
= true;
472 for (int k
= 0; k
< sids
.length
; k
++) {
473 if (sids
[k
] == rec
.getSid()) {
474 result
= listener
.processRecord(rec
);
476 if (abortable
== true && result
== false) {