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    *  BiffViewer.java
57    *
58    *  Created on November 13, 2001, 9:23 AM
59    */
60   package org.apache.poi.hssf.dev;
61   
62   import org.apache.poi.hssf.record.*;
63   import org.apache.poi.poifs.filesystem.POIFSFileSystem;
64   import org.apache.poi.util.HexDump;
65   import org.apache.poi.util.LittleEndian;
66   
67   import java.io.FileInputStream;
68   import java.io.IOException;
69   import java.io.InputStream;
70   import java.util.ArrayList;
71   
72   /**
73    *  Utillity for reading in BIFF8 records and displaying data from them.
74    *
75    *@author     Andrew C. Oliver (acoliver at apache dot org)
76    *@author     Glen Stampoultzis (glens at apache.org)
77    *@created    May 10, 2002
78    *@see        #main
79    */
80   
81   public class BiffViewer {
82       String filename;
83       private boolean dump;
84   
85   
86       /**
87        *  Creates new BiffViewer
88        *
89        *@param  args
90        */
91   
92       public BiffViewer(String[] args) {
93           if (args.length > 0) {
94               filename = args[0];
95           } else {
96               System.out.println("BIFFVIEWER REQUIRES A FILENAME***");
97           }
98       }
99   
100  
101      /**
102       *  Method run starts up BiffViewer...
103       */
104  
105      public void run() {
106          try {
107              POIFSFileSystem fs =
108                      new POIFSFileSystem(new FileInputStream(filename));
109              InputStream stream =
110                      fs.createDocumentInputStream("Workbook");
111              Record[] records = createRecords(stream, dump);
112          } catch (Exception e) {
113              e.printStackTrace();
114          }
115      }
116  
117  
118      /**
119       *  Create an array of records from an input stream
120       *
121       *@param  in                         the InputStream from which the records
122       *      will be obtained
123       *@param  dump
124       *@return                            an array of Records created from the
125       *      InputStream
126       *@exception  RecordFormatException  on error processing the InputStream
127       */
128  
129      public static Record[] createRecords(InputStream in, boolean dump)
130               throws RecordFormatException {
131          ArrayList records = new ArrayList();
132          Record last_record = null;
133          int loc = 0;
134  
135          try {
136  //            long  offset  = 0;
137              short rectype = 0;
138  
139              do {
140                  rectype = LittleEndian.readShort(in);
141                  System.out.println("============================================");
142                  System.out.println("Offset 0x" + Integer.toHexString(loc) + " (" + loc + ")");
143                  loc += 2;
144                  if (rectype != 0) {
145                      short recsize = LittleEndian.readShort(in);
146  
147                      loc += 2;
148                      byte[] data = new byte[(int) recsize];
149  
150                      in.read(data);
151                      if ((rectype == WSBoolRecord.sid) && (recsize == 0)) {
152                          System.out.println(loc);
153                      }
154                      loc += recsize;
155  //                    offset += 4 + recsize;
156                      if (dump) {
157                          dump(rectype, recsize, data);
158                      }
159                      Record[] recs = createRecord(rectype, recsize,
160                              data);
161                      // handle MulRK records
162  
163                      Record record = recs[0];
164  
165                      if ((record instanceof UnknownRecord)
166                              && !dump) {
167                          // if we didn't already dump
168                          // just cause dump was on and we're hit an unknow
169                          dumpUnknownRecord(data);
170                      }
171                      if (record != null) {
172                          if (rectype == ContinueRecord.sid) {
173                              dumpContinueRecord(last_record, dump, data);
174                          } else {
175                              last_record = record;
176                              records.add(record);
177                          }
178                      }
179                  }
180              } while (rectype != 0);
181          } catch (IOException e) {
182              throw new RecordFormatException("Error reading bytes");
183          }
184          Record[] retval = new Record[records.size()];
185  
186          retval = (Record[]) records.toArray(retval);
187          return retval;
188      }
189  
190  
191      /**
192       *  Description of the Method
193       *
194       *@param  last_record      Description of the Parameter
195       *@param  dump             Description of the Parameter
196       *@param  data             Description of the Parameter
197       *@exception  IOException  Description of the Exception
198       */
199      private static void dumpContinueRecord(Record last_record, boolean dump, byte[] data) throws IOException {
200          if (last_record == null) {
201              throw new RecordFormatException(
202                      "First record is a ContinueRecord??");
203          }
204          if (dump) {
205              System.out.println(
206                      "-----PRECONTINUED LAST RECORD WOULD SERIALIZE LIKE:");
207              byte[] lr = last_record.serialize();
208  
209              if (lr != null) {
210                  HexDump.dump(last_record.serialize(),
211                          0, System.out, 0);
212              }
213              System.out.println();
214              System.out.println(
215                      "-----PRECONTINUED----------------------------------");
216          }
217          last_record.processContinueRecord(data);
218          if (dump) {
219              System.out.println(
220                      "-----CONTINUED LAST RECORD WOULD SERIALIZE LIKE:");
221              HexDump.dump(last_record.serialize(), 0,
222                      System.out, 0);
223              System.out.println();
224              System.out.println(
225                      "-----CONTINUED----------------------------------");
226          }
227      }
228  
229  
230      /**
231       *  Description of the Method
232       *
233       *@param  data             Description of the Parameter
234       *@exception  IOException  Description of the Exception
235       */
236      private static void dumpUnknownRecord(byte[] data) throws IOException {
237          // record hex dump it!
238          System.out.println(
239                  "-----UNKNOWN----------------------------------");
240          if (data.length > 0) {
241              HexDump.dump(data, 0, System.out, 0);
242          } else {
243              System.out.print("**NO RECORD DATA**");
244          }
245          System.out.println();
246          System.out.println(
247                  "-----UNKNOWN----------------------------------");
248      }
249  
250  
251      /**
252       *  Description of the Method
253       *
254       *@param  rectype          Description of the Parameter
255       *@param  recsize          Description of the Parameter
256       *@param  data             Description of the Parameter
257       *@exception  IOException  Description of the Exception
258       */
259      private static void dump(short rectype, short recsize, byte[] data) throws IOException {
260  //                        System.out
261  //                            .println("fixing to recordize the following");
262          System.out.print("rectype = 0x"
263                  + Integer.toHexString(rectype));
264          System.out.println(", recsize = 0x"
265                  + Integer.toHexString(recsize));
266          System.out.println(
267                  "-BEGIN DUMP---------------------------------");
268          if (data.length > 0) {
269              HexDump.dump(data, 0, System.out, 0);
270          } else {
271              System.out.println("**NO RECORD DATA**");
272          }
273  //        System.out.println();
274          System.out.println(
275                  "-END DUMP-----------------------------------");
276      }
277  
278  
279      /**
280       *  Essentially a duplicate of RecordFactory. Kept seperate as not to screw
281       *  up non-debug operations.
282       *
283       *@param  rectype  Description of the Parameter
284       *@param  size     Description of the Parameter
285       *@param  data     Description of the Parameter
286       *@return          Description of the Return Value
287       */
288  
289      private static Record[] createRecord(short rectype, short size,
290              byte[] data) {
291          Record retval = null;
292          Record[] realretval = null;
293  
294          // int irectype = rectype;
295          switch (rectype) {
296  
297              case ChartRecord.sid:
298                  retval = new ChartRecord(rectype, size, data);
299                  break;
300              case ChartFormatRecord.sid:
301                  retval = new ChartFormatRecord(rectype, size, data);
302                  break;
303              case SeriesRecord.sid:
304                  retval = new SeriesRecord(rectype, size, data);
305                  break;
306              case BeginRecord.sid:
307                  retval = new BeginRecord(rectype, size, data);
308                  break;
309              case EndRecord.sid:
310                  retval = new EndRecord(rectype, size, data);
311                  break;
312              case BOFRecord.sid:
313                  retval = new BOFRecord(rectype, size, data);
314                  break;
315              case InterfaceHdrRecord.sid:
316                  retval = new InterfaceHdrRecord(rectype, size, data);
317                  break;
318              case MMSRecord.sid:
319                  retval = new MMSRecord(rectype, size, data);
320                  break;
321              case InterfaceEndRecord.sid:
322                  retval = new InterfaceEndRecord(rectype, size, data);
323                  break;
324              case WriteAccessRecord.sid:
325                  retval = new WriteAccessRecord(rectype, size, data);
326                  break;
327              case CodepageRecord.sid:
328                  retval = new CodepageRecord(rectype, size, data);
329                  break;
330              case DSFRecord.sid:
331                  retval = new DSFRecord(rectype, size, data);
332                  break;
333              case TabIdRecord.sid:
334                  retval = new TabIdRecord(rectype, size, data);
335                  break;
336              case FnGroupCountRecord.sid:
337                  retval = new FnGroupCountRecord(rectype, size, data);
338                  break;
339              case WindowProtectRecord.sid:
340                  retval = new WindowProtectRecord(rectype, size, data);
341                  break;
342              case ProtectRecord.sid:
343                  retval = new ProtectRecord(rectype, size, data);
344                  break;
345              case PasswordRecord.sid:
346                  retval = new PasswordRecord(rectype, size, data);
347                  break;
348              case ProtectionRev4Record.sid:
349                  retval = new ProtectionRev4Record(rectype, size, data);
350                  break;
351              case PasswordRev4Record.sid:
352                  retval = new PasswordRev4Record(rectype, size, data);
353                  break;
354              case WindowOneRecord.sid:
355                  retval = new WindowOneRecord(rectype, size, data);
356                  break;
357              case BackupRecord.sid:
358                  retval = new BackupRecord(rectype, size, data);
359                  break;
360              case HideObjRecord.sid:
361                  retval = new HideObjRecord(rectype, size, data);
362                  break;
363              case DateWindow1904Record.sid:
364                  retval = new DateWindow1904Record(rectype, size, data);
365                  break;
366              case PrecisionRecord.sid:
367                  retval = new PrecisionRecord(rectype, size, data);
368                  break;
369              case RefreshAllRecord.sid:
370                  retval = new RefreshAllRecord(rectype, size, data);
371                  break;
372              case BookBoolRecord.sid:
373                  retval = new BookBoolRecord(rectype, size, data);
374                  break;
375              case FontRecord.sid:
376                  retval = new FontRecord(rectype, size, data);
377                  break;
378              case FormatRecord.sid:
379                  retval = new FormatRecord(rectype, size, data);
380                  break;
381              case ExtendedFormatRecord.sid:
382                  retval = new ExtendedFormatRecord(rectype, size, data);
383                  break;
384              case StyleRecord.sid:
385                  retval = new StyleRecord(rectype, size, data);
386                  break;
387              case UseSelFSRecord.sid:
388                  retval = new UseSelFSRecord(rectype, size, data);
389                  break;
390              case BoundSheetRecord.sid:
391                  retval = new BoundSheetRecord(rectype, size, data);
392                  break;
393              case CountryRecord.sid:
394                  retval = new CountryRecord(rectype, size, data);
395                  break;
396              case SSTRecord.sid:
397                  retval = new SSTRecord(rectype, size, data);
398                  break;
399              case ExtSSTRecord.sid:
400                  retval = new ExtSSTRecord(rectype, size, data);
401                  break;
402              case EOFRecord.sid:
403                  retval = new EOFRecord(rectype, size, data);
404                  break;
405              case IndexRecord.sid:
406                  retval = new IndexRecord(rectype, size, data);
407                  break;
408              case CalcModeRecord.sid:
409                  retval = new CalcModeRecord(rectype, size, data);
410                  break;
411              case CalcCountRecord.sid:
412                  retval = new CalcCountRecord(rectype, size, data);
413                  break;
414              case RefModeRecord.sid:
415                  retval = new RefModeRecord(rectype, size, data);
416                  break;
417              case IterationRecord.sid:
418                  retval = new IterationRecord(rectype, size, data);
419                  break;
420              case DeltaRecord.sid:
421                  retval = new DeltaRecord(rectype, size, data);
422                  break;
423              case SaveRecalcRecord.sid:
424                  retval = new SaveRecalcRecord(rectype, size, data);
425                  break;
426              case PrintHeadersRecord.sid:
427                  retval = new PrintHeadersRecord(rectype, size, data);
428                  break;
429              case PrintGridlinesRecord.sid:
430                  retval = new PrintGridlinesRecord(rectype, size, data);
431                  break;
432              case GridsetRecord.sid:
433                  retval = new GridsetRecord(rectype, size, data);
434                  break;
435              case GutsRecord.sid:
436                  retval = new GutsRecord(rectype, size, data);
437                  break;
438              case DefaultRowHeightRecord.sid:
439                  retval = new DefaultRowHeightRecord(rectype, size, data);
440                  break;
441              case WSBoolRecord.sid:
442                  retval = new WSBoolRecord(rectype, size, data);
443                  break;
444              case HeaderRecord.sid:
445                  retval = new HeaderRecord(rectype, size, data);
446                  break;
447              case FooterRecord.sid:
448                  retval = new FooterRecord(rectype, size, data);
449                  break;
450              case HCenterRecord.sid:
451                  retval = new HCenterRecord(rectype, size, data);
452                  break;
453              case VCenterRecord.sid:
454                  retval = new VCenterRecord(rectype, size, data);
455                  break;
456              case PrintSetupRecord.sid:
457                  retval = new PrintSetupRecord(rectype, size, data);
458                  break;
459              case DefaultColWidthRecord.sid:
460                  retval = new DefaultColWidthRecord(rectype, size, data);
461                  break;
462              case DimensionsRecord.sid:
463                  retval = new DimensionsRecord(rectype, size, data);
464                  break;
465              case RowRecord.sid:
466                  retval = new RowRecord(rectype, size, data);
467                  break;
468              case LabelSSTRecord.sid:
469                  retval = new LabelSSTRecord(rectype, size, data);
470                  break;
471              case RKRecord.sid:
472                  retval = new RKRecord(rectype, size, data);
473                  break;
474              case NumberRecord.sid:
475                  retval = new NumberRecord(rectype, size, data);
476                  break;
477              case DBCellRecord.sid:
478                  retval = new DBCellRecord(rectype, size, data);
479                  break;
480              case WindowTwoRecord.sid:
481                  retval = new WindowTwoRecord(rectype, size, data);
482                  break;
483              case SelectionRecord.sid:
484                  retval = new SelectionRecord(rectype, size, data);
485                  break;
486              case ContinueRecord.sid:
487                  retval = new ContinueRecord(rectype, size, data);
488                  break;
489              case LabelRecord.sid:
490                  retval = new LabelRecord(rectype, size, data);
491                  break;
492              case MulRKRecord.sid:
493                  retval = new MulRKRecord(rectype, size, data);
494                  break;
495              case MulBlankRecord.sid:
496                  retval = new MulBlankRecord(rectype, size, data);
497                  break;
498              case BlankRecord.sid:
499                  retval = new BlankRecord(rectype, size, data);
500                  break;
501              case BoolErrRecord.sid:
502                  retval = new BoolErrRecord(rectype, size, data);
503                  break;
504              case ColumnInfoRecord.sid:
505                  retval = new ColumnInfoRecord(rectype, size, data);
506                  break;
507              case MergeCellsRecord.sid:
508                  retval = new MergeCellsRecord(rectype, size, data);
509                  break;
510              case AreaRecord.sid:
511                  retval = new AreaRecord(rectype, size, data);
512                  break;
513              case DataFormatRecord.sid:
514                  retval = new DataFormatRecord(rectype, size, data);
515                  break;
516              case BarRecord.sid:
517                  retval = new BarRecord(rectype, size, data);
518                  break;
519              case DatRecord.sid:
520                  retval = new DatRecord(rectype, size, data);
521                  break;
522              case PlotGrowthRecord.sid:
523                  retval = new PlotGrowthRecord(rectype, size, data);
524                  break;
525              case UnitsRecord.sid:
526                  retval = new UnitsRecord(rectype, size, data);
527                  break;
528              case FrameRecord.sid:
529                  retval = new FrameRecord(rectype, size, data);
530                  break;
531              case ValueRangeRecord.sid:
532                  retval = new ValueRangeRecord(rectype, size, data);
533                  break;
534              case SeriesListRecord.sid:
535                  retval = new SeriesListRecord(rectype, size, data);
536                  break;
537              case FontBasisRecord.sid:
538                  retval = new FontBasisRecord(rectype, size, data);
539                  break;
540              case FontIndexRecord.sid:
541                  retval = new FontIndexRecord(rectype, size, data);
542                  break;
543              case LineFormatRecord.sid:
544                  retval = new LineFormatRecord(rectype, size, data);
545                  break;
546              case AreaFormatRecord.sid:
547                  retval = new AreaFormatRecord(rectype, size, data);
548                  break;
549              case LinkedDataRecord.sid:
550                  retval = new LinkedDataRecord(rectype, size, data);
551                  break;
552              case FormulaRecord.sid:
553                  retval = new FormulaRecord(rectype, size, data);
554                  break;
555              case SheetPropertiesRecord.sid:
556                  retval = new SheetPropertiesRecord(rectype, size, data);
557                  break;
558              case DefaultDataLabelTextPropertiesRecord.sid:
559                  retval = new DefaultDataLabelTextPropertiesRecord(rectype, size, data);
560                  break;
561              case TextRecord.sid:
562                  retval = new TextRecord(rectype, size, data);
563                  break;
564              case AxisParentRecord.sid:
565                  retval = new AxisParentRecord(rectype, size, data);
566                  break;
567              case AxisLineFormatRecord.sid:
568                  retval = new AxisLineFormatRecord(rectype, size, data);
569                  break;
570              case SupBookRecord.sid:
571                  retval = new SupBookRecord(rectype, size, data);
572                  break;
573              case ExternSheetRecord.sid:
574                  retval = new ExternSheetRecord(rectype, size, data);
575                  break;
576              case SCLRecord.sid:
577                  retval = new SCLRecord(rectype, size, data);
578                  break;
579              case SeriesToChartGroupRecord.sid:
580                  retval = new SeriesToChartGroupRecord(rectype, size, data);
581                  break;
582              case AxisUsedRecord.sid:
583                  retval = new AxisUsedRecord(rectype, size, data);
584                  break;
585              case AxisRecord.sid:
586                  retval = new AxisRecord(rectype, size, data);
587                  break;
588              case CategorySeriesAxisRecord.sid:
589                  retval = new CategorySeriesAxisRecord(rectype, size, data);
590                  break;                
591              case AxisOptionsRecord.sid:
592                  retval = new AxisOptionsRecord(rectype, size, data);
593                  break;
594              case TickRecord.sid:
595                  retval = new TickRecord(rectype, size, data);
596                  break;
597              case SeriesTextRecord.sid:
598                  retval = new SeriesTextRecord(rectype, size, data);
599                  break;
600              case ObjectLinkRecord.sid:
601                  retval = new ObjectLinkRecord(rectype, size, data);
602                  break;
603              case PlotAreaRecord.sid:
604                  retval = new PlotAreaRecord(rectype, size, data);
605                  break;
606              case SeriesIndexRecord.sid:
607                  retval = new SeriesIndexRecord(rectype, size, data);
608                  break;
609              case LegendRecord.sid:
610                  retval = new LegendRecord(rectype, size, data);
611                  break;
612                  
613                  
614              default:
615                  retval = new UnknownRecord(rectype, size, data);
616          }
617          if (realretval == null) {
618              realretval = new Record[1];
619              realretval[0] = retval;
620              System.out.println("recordid = 0x" + Integer.toHexString(rectype) + ", size =" + size);
621              System.out.println(realretval[0].toString());
622          }
623          return realretval;
624      }
625  
626  
627      /**
628       *  Method setDump - hex dump out data or not.
629       *
630       *@param  dump
631       */
632  
633      public void setDump(boolean dump) {
634          this.dump = dump;
635      }
636  
637  
638      /**
639       *  Method main with 1 argument just run straight biffview against given
640       *  file<P>
641       *
642       *  with 2 arguments where the second argument is "on" - run biffviewer<P>
643       *
644       *  with hex dumps of records <P>
645       *
646       *  with 2 arguments where the second argument is "bfd" just run a big fat
647       *  hex dump of the file...don't worry about biffviewing it at all
648       *
649       *@param  args
650       */
651  
652      public static void main(String[] args) {
653          try {
654              BiffViewer viewer = new BiffViewer(args);
655  
656              if ((args.length > 1) && args[1].equals("on")) {
657                  viewer.setDump(true);
658              }
659              if ((args.length > 1) && args[1].equals("bfd")) {
660                  POIFSFileSystem fs =
661                          new POIFSFileSystem(new FileInputStream(args[0]));
662                  InputStream stream =
663                          fs.createDocumentInputStream("Workbook");
664                  int size = stream.available();
665                  byte[] data = new byte[size];
666  
667                  stream.read(data);
668                  HexDump.dump(data, 0, System.out, 0);
669              } else {
670                  viewer.run();
671              }
672          } catch (Exception e) {
673              e.printStackTrace();
674          }
675      }
676  }
677