1
2 /* ====================================================================
3 Licensed to the Apache Software Foundation (ASF) under one or more
4 contributor license agreements. See the NOTICE file distributed with
5 this work for additional information regarding copyright ownership.
6 The ASF licenses this file to You under the Apache License, Version 2.0
7 (the "License"); you may not use this file except in compliance with
8 the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 ==================================================================== */
18
19
20 package org.apache.poi.hssf.record;
21
22 import java.io.InputStream;
23 import java.lang.reflect.Constructor;
24 import java.util;
25
26 /**
27 * Title: Record Factory<P>
28 * Description: Takes a stream and outputs an array of Record objects.<P>
29 *
30 * @deprecated use {@link org.apache.poi.hssf.eventmodel.EventRecordFactory} instead
31 * @see org.apache.poi.hssf.eventmodel.EventRecordFactory
32 * @author Andrew C. Oliver (acoliver at apache dot org)
33 * @author Marc Johnson (mjohnson at apache dot org)
34 * @author Glen Stampoultzis (glens at apache.org)
35 * @author Csaba Nagy (ncsaba at yahoo dot com)
36 */
37
38 public class RecordFactory
39 {
40 private static int NUM_RECORDS = 10000;
41 private static final Class[] records;
42
43 static {
44 records = new Class[]
45 {
46 BOFRecord.class, InterfaceHdrRecord.class, MMSRecord.class,
47 InterfaceEndRecord.class, WriteAccessRecord.class,
48 CodepageRecord.class, DSFRecord.class, TabIdRecord.class,
49 FnGroupCountRecord.class, WindowProtectRecord.class,
50 ProtectRecord.class, PasswordRecord.class, ProtectionRev4Record.class,
51 PasswordRev4Record.class, WindowOneRecord.class, BackupRecord.class,
52 HideObjRecord.class, DateWindow1904Record.class,
53 PrecisionRecord.class, RefreshAllRecord.class, BookBoolRecord.class,
54 FontRecord.class, FormatRecord.class, ExtendedFormatRecord.class,
55 StyleRecord.class, UseSelFSRecord.class, BoundSheetRecord.class,
56 CountryRecord.class, SSTRecord.class, ExtSSTRecord.class,
57 EOFRecord.class, IndexRecord.class, CalcModeRecord.class,
58 CalcCountRecord.class, RefModeRecord.class, IterationRecord.class,
59 DeltaRecord.class, SaveRecalcRecord.class, PrintHeadersRecord.class,
60 PrintGridlinesRecord.class, GridsetRecord.class, GutsRecord.class,
61 DefaultRowHeightRecord.class, WSBoolRecord.class, HeaderRecord.class,
62 FooterRecord.class, HCenterRecord.class, VCenterRecord.class,
63 PrintSetupRecord.class, DefaultColWidthRecord.class,
64 DimensionsRecord.class, RowRecord.class, LabelSSTRecord.class,
65 RKRecord.class, NumberRecord.class, DBCellRecord.class,
66 WindowTwoRecord.class, SelectionRecord.class, ContinueRecord.class,
67 LabelRecord.class, BlankRecord.class, ColumnInfoRecord.class,
68 MulRKRecord.class, MulBlankRecord.class, MergeCellsRecord.class,
69 FormulaRecord.class, BoolErrRecord.class, ExternSheetRecord.class,
70 NameRecord.class, LeftMarginRecord.class, RightMarginRecord.class,
71 TopMarginRecord.class, BottomMarginRecord.class,
72 DrawingRecord.class, DrawingGroupRecord.class, DrawingSelectionRecord.class,
73 ObjRecord.class, TextObjectRecord.class,
74 PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class,
75 HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class,
76 WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
77 NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class,
78 FileSharingRecord.class, ChartTitleFormatRecord.class,
79 DVRecord.class, DVALRecord.class, UncalcedRecord.class,
80 ChartRecord.class, LegendRecord.class, ChartTitleFormatRecord.class,
81 SeriesRecord.class, SeriesTextRecord.class,
82 HyperlinkRecord.class,
83 ExternalNameRecord.class, // TODO - same changes in non-@deprecated version of this class
84 SupBookRecord.class,
85 CRNCountRecord.class,
86 CRNRecord.class,
87 CFHeaderRecord.class,
88 CFRuleRecord.class,
89 };
90 }
91 private static Map recordsMap = recordsToMap(records);
92
93 /**
94 * changes the default capacity (10000) to handle larger files
95 */
96
97 public static void setCapacity(int capacity)
98 {
99 NUM_RECORDS = capacity;
100 }
101
102 /**
103 * Create an array of records from an input stream
104 *
105 * @param in the InputStream from which the records will be
106 * obtained
107 *
108 * @return an array of Records created from the InputStream
109 *
110 * @exception RecordFormatException on error processing the
111 * InputStream
112 */
113
114 public static List createRecords(InputStream in)
115 throws RecordFormatException
116 {
117 ArrayList records = new ArrayList(NUM_RECORDS);
118
119 RecordInputStream recStream = new RecordInputStream(in);
120 DrawingRecord lastDrawingRecord = new DrawingRecord( );
121 Record lastRecord = null;
122 while (recStream.hasNextRecord()) {
123 recStream.nextRecord();
124 if (recStream.getSid() != 0)
125 {
126 Record[] recs = createRecord(recStream); // handle MulRK records
127
128 if (recs.length > 1)
129 {
130 for (int k = 0; k < recs.length; k++)
131 {
132 records.add(
133 recs[ k ]); // these will be number records
134 }
135 }
136 else
137 {
138 Record record = recs[ 0 ];
139
140 if (record != null)
141 {
142 if (record.getSid() == DrawingGroupRecord.sid
143 && lastRecord instanceof DrawingGroupRecord)
144 {
145 DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) lastRecord;
146 lastDGRecord.join((AbstractEscherHolderRecord) record);
147 }
148 else if (record.getSid() == ContinueRecord.sid &&
149 ((lastRecord instanceof ObjRecord) || (lastRecord instanceof TextObjectRecord))) {
150 // Drawing records have a very strange continue behaviour.
151 //There can actually be OBJ records mixed between the continues.
152 lastDrawingRecord.processContinueRecord( ((ContinueRecord)record).getData() );
153 //we must rememeber the position of the continue record.
154 //in the serialization procedure the original structure of records must be preserved
155 records.add(record);
156 } else if (record.getSid() == ContinueRecord.sid &&
157 (lastRecord instanceof DrawingGroupRecord)) {
158 ((DrawingGroupRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
159 } else if (record.getSid() == ContinueRecord.sid &&
160 (lastRecord instanceof StringRecord)) {
161 ((StringRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
162 } else if (record.getSid() == ContinueRecord.sid) {
163 if (lastRecord instanceof UnknownRecord) {
164 //Gracefully handle records that we dont know about,
165 //that happen to be continued
166 records.add(record);
167 } else
168 throw new RecordFormatException("Unhandled Continue Record");
169 }
170 else {
171 lastRecord = record;
172 if (record instanceof DrawingRecord)
173 lastDrawingRecord = (DrawingRecord) record;
174 records.add(record);
175 }
176 }
177 }
178 }
179 }
180
181 return records;
182 }
183
184 public static Record [] createRecord(RecordInputStream in)
185 {
186 Record retval;
187 Record[] realretval = null;
188
189 try
190 {
191 Constructor constructor =
192 ( Constructor ) recordsMap.get(new Short(in.getSid()));
193
194 if (constructor != null)
195 {
196 retval = ( Record ) constructor.newInstance(new Object[]
197 {
198 in
199 });
200 }
201 else
202 {
203 retval = new UnknownRecord(in);
204 }
205 }
206 catch (Exception introspectionException)
207 {
208 throw new RecordFormatException("Unable to construct record instance",introspectionException);
209 }
210 if (retval instanceof RKRecord)
211 {
212 RKRecord rk = ( RKRecord ) retval;
213 NumberRecord num = new NumberRecord();
214
215 num.setColumn(rk.getColumn());
216 num.setRow(rk.getRow());
217 num.setXFIndex(rk.getXFIndex());
218 num.setValue(rk.getRKNumber());
219 retval = num;
220 }
221 else if (retval instanceof DBCellRecord)
222 {
223 retval = null;
224 }
225 else if (retval instanceof MulRKRecord)
226 {
227 MulRKRecord mrk = ( MulRKRecord ) retval;
228
229 realretval = new Record[ mrk.getNumColumns() ];
230 for (int k = 0; k < mrk.getNumColumns(); k++)
231 {
232 NumberRecord nr = new NumberRecord();
233
234 nr.setColumn(( short ) (k + mrk.getFirstColumn()));
235 nr.setRow(mrk.getRow());
236 nr.setXFIndex(mrk.getXFAt(k));
237 nr.setValue(mrk.getRKNumberAt(k));
238 realretval[ k ] = nr;
239 }
240 }
241 else if (retval instanceof MulBlankRecord)
242 {
243 MulBlankRecord mb = ( MulBlankRecord ) retval;
244
245 realretval = new Record[ mb.getNumColumns() ];
246 for (int k = 0; k < mb.getNumColumns(); k++)
247 {
248 BlankRecord br = new BlankRecord();
249
250 br.setColumn(( short ) (k + mb.getFirstColumn()));
251 br.setRow(mb.getRow());
252 br.setXFIndex(mb.getXFAt(k));
253 realretval[ k ] = br;
254 }
255 }
256 if (realretval == null)
257 {
258 realretval = new Record[ 1 ];
259 realretval[ 0 ] = retval;
260 }
261 return realretval;
262 }
263
264 public static short [] getAllKnownRecordSIDs()
265 {
266 short[] results = new short[ recordsMap.size() ];
267 int i = 0;
268
269 for (Iterator iterator = recordsMap.keySet().iterator();
270 iterator.hasNext(); )
271 {
272 Short sid = ( Short ) iterator.next();
273
274 results[ i++ ] = sid.shortValue();
275 }
276 return results;
277 }
278
279 private static Map recordsToMap(Class [] records)
280 {
281 Map result = new HashMap();
282 Constructor constructor;
283
284 for (int i = 0; i < records.length; i++)
285 {
286 Class record;
287 short sid;
288
289 record = records[ i ];
290 try
291 {
292 sid = record.getField("sid").getShort(null);
293 constructor = record.getConstructor(new Class[]
294 {
295 RecordInputStream.class
296 });
297 }
298 catch (Exception illegalArgumentException)
299 {
300 throw new RecordFormatException(
301 "Unable to determine record types", illegalArgumentException);
302 }
303 result.put(new Short(sid), constructor);
304 }
305 return result;
306 }
307 }