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.record;
57   
58   import java.io.InputStream;
59   import java.io.IOException;
60   
61   import java.util.*;
62   
63   import java.lang.reflect.Constructor;
64   
65   import org.apache.poi.util.LittleEndian;
66   
67   /**
68    * Title:  Record Factory<P>
69    * Description:  Takes a stream and outputs an array of Record objects.<P>
70    *
71    * @deprecated use EventRecordFactory instead
72    * @see org.apache.poi.hssf.record.EventRecordFactory
73    * @author Andrew C. Oliver (acoliver at apache dot org)
74    * @author Marc Johnson (mjohnson at apache dot org)
75    * @author Glen Stampoultzis (glens at apache.org)
76    * @version 1.0-pre
77    */
78   
79   public class RecordFactory
80   {
81       private static int           NUM_RECORDS = 10000;
82       private static final Class[] records;
83   
84       static {
85           if (FormulaRecord.EXPERIMENTAL_FORMULA_SUPPORT_ENABLED) {
86               records = new Class[]
87               {
88                   BOFRecord.class, InterfaceHdrRecord.class, MMSRecord.class,
89                   InterfaceEndRecord.class, WriteAccessRecord.class,
90                   CodepageRecord.class, DSFRecord.class, TabIdRecord.class,
91                   FnGroupCountRecord.class, WindowProtectRecord.class,
92                   ProtectRecord.class, PasswordRecord.class, ProtectionRev4Record.class,
93                   PasswordRev4Record.class, WindowOneRecord.class, BackupRecord.class,
94                   HideObjRecord.class, DateWindow1904Record.class,
95                   PrecisionRecord.class, RefreshAllRecord.class, BookBoolRecord.class,
96                   FontRecord.class, FormatRecord.class, ExtendedFormatRecord.class,
97                   StyleRecord.class, UseSelFSRecord.class, BoundSheetRecord.class,
98                   CountryRecord.class, SSTRecord.class, ExtSSTRecord.class,
99                   EOFRecord.class, IndexRecord.class, CalcModeRecord.class,
100                  CalcCountRecord.class, RefModeRecord.class, IterationRecord.class,
101                  DeltaRecord.class, SaveRecalcRecord.class, PrintHeadersRecord.class,
102                  PrintGridlinesRecord.class, GridsetRecord.class, GutsRecord.class,
103                  DefaultRowHeightRecord.class, WSBoolRecord.class, HeaderRecord.class,
104                  FooterRecord.class, HCenterRecord.class, VCenterRecord.class,
105                  PrintSetupRecord.class, DefaultColWidthRecord.class,
106                  DimensionsRecord.class, RowRecord.class, LabelSSTRecord.class,
107                  RKRecord.class, NumberRecord.class, DBCellRecord.class,
108                  WindowTwoRecord.class, SelectionRecord.class, ContinueRecord.class,
109                  LabelRecord.class, BlankRecord.class, ColumnInfoRecord.class,
110                  MulRKRecord.class, MulBlankRecord.class, MergeCellsRecord.class,
111                  FormulaRecord.class, BoolErrRecord.class, ExternSheetRecord.class,
112                  NameRecord.class, LeftMarginRecord.class, RightMarginRecord.class,
113                  TopMarginRecord.class, BottomMarginRecord.class,
114                  PaletteRecord.class, StringRecord.class, RecalcIdRecord.class
115              };
116          } else {
117              records = new Class[]
118              {
119                  BOFRecord.class, InterfaceHdrRecord.class, MMSRecord.class,
120                  InterfaceEndRecord.class, WriteAccessRecord.class,
121                  CodepageRecord.class, DSFRecord.class, TabIdRecord.class,
122                  FnGroupCountRecord.class, WindowProtectRecord.class,
123                  ProtectRecord.class, PasswordRecord.class, ProtectionRev4Record.class,
124                  PasswordRev4Record.class, WindowOneRecord.class, BackupRecord.class,
125                  HideObjRecord.class, DateWindow1904Record.class,
126                  PrecisionRecord.class, RefreshAllRecord.class, BookBoolRecord.class,
127                  FontRecord.class, FormatRecord.class, ExtendedFormatRecord.class,
128                  StyleRecord.class, UseSelFSRecord.class, BoundSheetRecord.class,
129                  CountryRecord.class, SSTRecord.class, ExtSSTRecord.class,
130                  EOFRecord.class, IndexRecord.class, CalcModeRecord.class,
131                  CalcCountRecord.class, RefModeRecord.class, IterationRecord.class,
132                  DeltaRecord.class, SaveRecalcRecord.class, PrintHeadersRecord.class,
133                  PrintGridlinesRecord.class, GridsetRecord.class, GutsRecord.class,
134                  DefaultRowHeightRecord.class, WSBoolRecord.class, HeaderRecord.class,
135                  FooterRecord.class, HCenterRecord.class, VCenterRecord.class,
136                  PrintSetupRecord.class, DefaultColWidthRecord.class,
137                  DimensionsRecord.class, RowRecord.class, LabelSSTRecord.class,
138                  RKRecord.class, NumberRecord.class, DBCellRecord.class,
139                  WindowTwoRecord.class, SelectionRecord.class, ContinueRecord.class,
140                  LabelRecord.class, BlankRecord.class, ColumnInfoRecord.class,
141                  MulRKRecord.class, MulBlankRecord.class, MergeCellsRecord.class,
142                  BoolErrRecord.class, ExternSheetRecord.class, NameRecord.class,
143                  LeftMarginRecord.class, RightMarginRecord.class,
144                  TopMarginRecord.class, BottomMarginRecord.class,
145                  PaletteRecord.class, StringRecord.class, RecalcIdRecord.class
146              };
147  
148          }
149      }
150      private static Map           recordsMap  = recordsToMap(records);
151  
152      /**
153       * changes the default capacity (10000) to handle larger files
154       */
155  
156      public static void setCapacity(int capacity)
157      {
158          NUM_RECORDS = capacity;
159      }
160  
161      /**
162       * Create an array of records from an input stream
163       *
164       * @param in the InputStream from which the records will be
165       *           obtained
166       *
167       * @return an array of Records created from the InputStream
168       *
169       * @exception RecordFormatException on error processing the
170       *            InputStream
171       */
172  
173      public static List createRecords(InputStream in)
174          throws RecordFormatException
175      {
176          ArrayList records     = new ArrayList(NUM_RECORDS);
177          Record    last_record = null;
178  
179          try
180          {
181              short rectype = 0;
182  
183              do
184              {
185                  rectype = LittleEndian.readShort(in);
186                  if (rectype != 0)
187                  {
188                      short  recsize = LittleEndian.readShort(in);
189                      byte[] data    = new byte[ ( int ) recsize ];
190  
191                      in.read(data);
192                      Record[] recs = createRecord(rectype, recsize,
193                                                   data);   // handle MulRK records
194  
195                      if (recs.length > 1)
196                      {
197                          for (int k = 0; k < recs.length; k++)
198                          {
199                              records.add(
200                                  recs[ k ]);               // these will be number records
201                              last_record =
202                                  recs[ k ];                // do to keep the algorythm homogenous...you can't
203                          }                                 // actually continue a number record anyhow.
204                      }
205                      else
206                      {
207                          Record record = recs[ 0 ];
208  
209                          if (record != null)
210                          {
211                              if (rectype == ContinueRecord.sid)
212                              {
213                                  if (last_record == null)
214                                  {
215                                      throw new RecordFormatException(
216                                          "First record is a ContinueRecord??");
217                                  }
218                                  last_record.processContinueRecord(data);
219                              }
220                              else
221                              {
222                                  last_record = record;
223                                  records.add(record);
224                              }
225                          }
226                      }
227                  }
228              }
229              while (rectype != 0);
230          }
231          catch (IOException e)
232          {
233              throw new RecordFormatException("Error reading bytes");
234          }
235  
236          // Record[] retval = new Record[ records.size() ];
237          // retval = ( Record [] ) records.toArray(retval);
238          return records;
239      }
240  
241      public static Record [] createRecord(short rectype, short size,
242                                           byte [] data)
243      {
244          Record   retval     = null;
245          Record[] realretval = null;
246  
247          try
248          {
249              Constructor constructor =
250                  ( Constructor ) recordsMap.get(new Short(rectype));
251  
252              if (constructor != null)
253              {
254                  retval = ( Record ) constructor.newInstance(new Object[]
255                  {
256                      new Short(rectype), new Short(size), data
257                  });
258              }
259              else
260              {
261                  retval = new UnknownRecord(rectype, size, data);
262              }
263          }
264          catch (Exception introspectionException)
265          {
266              introspectionException.printStackTrace();
267              throw new RecordFormatException(
268                  "Unable to construct record instance, the following exception occured: " + introspectionException.getMessage());
269          }
270          if (retval instanceof RKRecord)
271          {
272              RKRecord     rk  = ( RKRecord ) retval;
273              NumberRecord num = new NumberRecord();
274  
275              num.setColumn(rk.getColumn());
276              num.setRow(rk.getRow());
277              num.setXFIndex(rk.getXFIndex());
278              num.setValue(rk.getRKNumber());
279              retval = num;
280          }
281          else if (retval instanceof DBCellRecord)
282          {
283              retval = null;
284          }
285          else if (retval instanceof MulRKRecord)
286          {
287              MulRKRecord mrk = ( MulRKRecord ) retval;
288  
289              realretval = new Record[ mrk.getNumColumns() ];
290              for (int k = 0; k < mrk.getNumColumns(); k++)
291              {
292                  NumberRecord nr = new NumberRecord();
293  
294                  nr.setColumn(( short ) (k + mrk.getFirstColumn()));
295                  nr.setRow(mrk.getRow());
296                  nr.setXFIndex(mrk.getXFAt(k));
297                  nr.setValue(mrk.getRKNumberAt(k));
298                  realretval[ k ] = nr;
299              }
300          }
301          else if (retval instanceof MulBlankRecord)
302          {
303              MulBlankRecord mb = ( MulBlankRecord ) retval;
304  
305              realretval = new Record[ mb.getNumColumns() ];
306              for (int k = 0; k < mb.getNumColumns(); k++)
307              {
308                  BlankRecord br = new BlankRecord();
309  
310                  br.setColumn(( short ) (k + mb.getFirstColumn()));
311                  br.setRow(mb.getRow());
312                  br.setXFIndex(mb.getXFAt(k));
313                  realretval[ k ] = br;
314              }
315          }
316          if (realretval == null)
317          {
318              realretval      = new Record[ 1 ];
319              realretval[ 0 ] = retval;
320          }
321          return realretval;
322      }
323  
324      public static short [] getAllKnownRecordSIDs()
325      {
326          short[] results = new short[ recordsMap.size() ];
327          int     i       = 0;
328  
329          for (Iterator iterator = recordsMap.keySet().iterator();
330                  iterator.hasNext(); )
331          {
332              Short sid = ( Short ) iterator.next();
333  
334              results[ i++ ] = sid.shortValue();
335          }
336          return results;
337      }
338  
339      private static Map recordsToMap(Class [] records)
340      {
341          Map         result = new HashMap();
342          Constructor constructor;
343  
344          for (int i = 0; i < records.length; i++)
345          {
346              Class record = null;
347              short sid    = 0;
348  
349              record = records[ i ];
350              try
351              {
352                  sid         = record.getField("sid").getShort(null);
353                  constructor = record.getConstructor(new Class[]
354                  {
355                      short.class, short.class, byte [].class
356                  });
357              }
358              catch (Exception illegalArgumentException)
359              {
360                  throw new RecordFormatException(
361                      "Unable to determine record types");
362              }
363              result.put(new Short(sid), constructor);
364          }
365          return result;
366      }
367  }
368