/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2002 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" and * "Apache POI" must not be used to endorse or promote products * derived from this software without prior written permission. For * written permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * "Apache POI", nor may "Apache" appear in their name, without * prior written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . */ package org.apache.poi.hssf.record; import java.io.InputStream; import java.io.IOException; import java.util.*; import java.lang.reflect.Constructor; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.HexDump; /** * Title: Record Factory

* Description: Takes a stream and outputs an array of Record objects.

* * @author Andrew C. Oliver (acoliver at apache dot org) * @author Marc Johnson (mjohnson at apache dot org) * @author Glen Stampoultzis (glens at apache.org) * @version 1.0-pre */ public class RecordFactory { private static int NUM_RECORDS = 10000; private static final Class[] records = new Class[] { BOFRecord.class, InterfaceHdrRecord.class, MMSRecord.class, InterfaceEndRecord.class, WriteAccessRecord.class, CodepageRecord.class, DSFRecord.class, TabIdRecord.class, FnGroupCountRecord.class, WindowProtectRecord.class, ProtectRecord.class, PasswordRecord.class, ProtectionRev4Record.class, PasswordRev4Record.class, WindowOneRecord.class, BackupRecord.class, HideObjRecord.class, DateWindow1904Record.class, PrecisionRecord.class, RefreshAllRecord.class, BookBoolRecord.class, FontRecord.class, FormatRecord.class, ExtendedFormatRecord.class, StyleRecord.class, UseSelFSRecord.class, BoundSheetRecord.class, CountryRecord.class, SSTRecord.class, ExtSSTRecord.class, EOFRecord.class, IndexRecord.class, CalcModeRecord.class, CalcCountRecord.class, RefModeRecord.class, IterationRecord.class, DeltaRecord.class, SaveRecalcRecord.class, PrintHeadersRecord.class, PrintGridlinesRecord.class, GridsetRecord.class, GutsRecord.class, DefaultRowHeightRecord.class, WSBoolRecord.class, HeaderRecord.class, FooterRecord.class, HCenterRecord.class, VCenterRecord.class, PrintSetupRecord.class, DefaultColWidthRecord.class, DimensionsRecord.class, RowRecord.class, LabelSSTRecord.class, RKRecord.class, NumberRecord.class, DBCellRecord.class, WindowTwoRecord.class, SelectionRecord.class, ContinueRecord.class, LabelRecord.class, BlankRecord.class, ColumnInfoRecord.class, MulRKRecord.class, MulBlankRecord.class, MergeCellsRecord.class, FormulaRecord.class }; private static Map recordsMap = recordsToMap(records); /** * changes the default capacity (10000) to handle larger files */ public static void setCapacity(int capacity) { NUM_RECORDS = capacity; } /** * Create an array of records from an input stream * * @param in the InputStream from which the records will be * obtained * * @return an array of Records created from the InputStream * * @exception RecordFormatException on error processing the * InputStream */ public static List createRecords(InputStream in) throws RecordFormatException { ArrayList records = new ArrayList(NUM_RECORDS); Record last_record = null; try { short rectype = 0; do { rectype = LittleEndian.readShort(in); if (rectype != 0) { short recsize = LittleEndian.readShort(in); byte[] data = new byte[ ( int ) recsize ]; in.read(data); Record[] recs = createRecord(rectype, recsize, data); // handle MulRK records if (recs.length > 1) { for (int k = 0; k < recs.length; k++) { records.add( recs[ k ]); // these will be number records last_record = recs[ k ]; // do to keep the algorythm homogenous...you can't } // actually continue a number record anyhow. } else { Record record = recs[ 0 ]; if (record != null) { if (rectype == ContinueRecord.sid) { if (last_record == null) { throw new RecordFormatException( "First record is a ContinueRecord??"); } last_record.processContinueRecord(data); } else { last_record = record; records.add(record); } } } } } while (rectype != 0); } catch (IOException e) { throw new RecordFormatException("Error reading bytes"); } // Record[] retval = new Record[ records.size() ]; // retval = ( Record [] ) records.toArray(retval); return records; } public static Record [] createRecord(short rectype, short size, byte [] data) { Record retval = null; Record[] realretval = null; try { Constructor constructor = ( Constructor ) recordsMap.get(new Short(rectype)); if (constructor != null) { retval = ( Record ) constructor.newInstance(new Object[] { new Short(rectype), new Short(size), data }); } else { retval = new UnknownRecord(rectype, size, data); } } catch (Exception introspectionException) { throw new RecordFormatException( "Unable to construct record instance"); } if (retval instanceof RKRecord) { RKRecord rk = ( RKRecord ) retval; NumberRecord num = new NumberRecord(); num.setColumn(rk.getColumn()); num.setRow(rk.getRow()); num.setXFIndex(rk.getXFIndex()); num.setValue(rk.getRKNumber()); retval = num; } else if (retval instanceof DBCellRecord) { retval = null; } else if (retval instanceof MulRKRecord) { MulRKRecord mrk = ( MulRKRecord ) retval; realretval = new Record[ mrk.getNumColumns() ]; for (int k = 0; k < mrk.getNumColumns(); k++) { NumberRecord nr = new NumberRecord(); nr.setColumn(( short ) (k + mrk.getFirstColumn())); nr.setRow(mrk.getRow()); nr.setXFIndex(mrk.getXFAt(k)); nr.setValue(mrk.getRKNumberAt(k)); realretval[ k ] = nr; } } else if (retval instanceof MulBlankRecord) { MulBlankRecord mb = ( MulBlankRecord ) retval; realretval = new Record[ mb.getNumColumns() ]; for (int k = 0; k < mb.getNumColumns(); k++) { BlankRecord br = new BlankRecord(); br.setColumn(( short ) (k + mb.getFirstColumn())); br.setRow(mb.getRow()); br.setXFIndex(mb.getXFAt(k)); realretval[ k ] = br; } } if (realretval == null) { realretval = new Record[ 1 ]; realretval[ 0 ] = retval; } return realretval; } public static short [] getAllKnownRecordSIDs() { short[] results = new short[ recordsMap.size() ]; int i = 0; for (Iterator iterator = recordsMap.keySet().iterator(); iterator.hasNext(); ) { Short sid = ( Short ) iterator.next(); results[ i++ ] = sid.shortValue(); } return results; } private static Map recordsToMap(Class [] records) { Map result = new HashMap(); Constructor constructor; for (int i = 0; i < records.length; i++) { Class record = null; short sid = 0; record = records[ i ]; try { sid = record.getField("sid").getShort(null); constructor = record.getConstructor(new Class[] { short.class, short.class, byte [].class }); } catch (Exception illegalArgumentException) { throw new RecordFormatException( "Unable to determine record types"); } result.put(new Short(sid), constructor); } return result; } }