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