My last post introduced a shortToConstructorCache class. This post shows a changed RecordFactory class that uses it. In my test case, I was able to reduce the execution time of RecordFactory.createRecord() by 3.6 seconds and reduce the number of objects created by 466,586.
/*
* ====================================================================
* Copyright 2002-2004 Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
* ====================================================================
*/
package org.apache.poi.hssf.record;
import org.apache.poi.util.LittleEndian;
import org.wwre.common.shortToShortCache;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.*;
/**
* Title: Record Factory
*
* Description: Takes a stream and outputs an array of Record objects.
*
*
* @deprecated use EventRecordFactory instead
* @see org.apache.poi.hssf.eventmodel.EventRecordFactory
* @author Andrew C. Oliver (acoliver at apache dot org)
* @author Marc Johnson (mjohnson at apache dot org)
* @author Glen Stampoultzis (glens at apache.org)
* @author Csaba Nagy (ncsaba at yahoo dot com)
* @version 1.0-pre
*/
public class RecordFactory {
private static int NUM_RECORDS = 10000;
private static shortToConstructorCache scm = null;
private static shortToShortCache ssc = new shortToShortCache();
static {
Class[] records;
if (FormulaRecord.EXPERIMENTAL_FORMULA_SUPPORT_ENABLED) {
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, BoolErrRecord.class, ExternSheetRecord.class, NameRecord.class,
LeftMarginRecord.class, RightMarginRecord.class, TopMarginRecord.class, BottomMarginRecord.class,
DrawingRecord.class, DrawingGroupRecord.class, DrawingSelectionRecord.class, ObjRecord.class,
TextObjectRecord.class, PaletteRecord.class, StringRecord.class, RecalcIdRecord.class,
SharedFormulaRecord.class, HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class
};
} else {
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, BoolErrRecord.class,
ExternSheetRecord.class, NameRecord.class, LeftMarginRecord.class, RightMarginRecord.class,
TopMarginRecord.class, BottomMarginRecord.class, PaletteRecord.class, StringRecord.class,
RecalcIdRecord.class, SharedFormulaRecord.class, DrawingRecord.class, DrawingGroupRecord.class,
DrawingSelectionRecord.class, ObjRecord.class, TextObjectRecord.class, HorizontalPageBreakRecord.class,
VerticalPageBreakRecord.class};
}
scm = new shortToConstructorCache(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[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 && !(last_record instanceof ContinueRecord) && // include continuation records after
!(last_record instanceof UnknownRecord)) // unknown records or previous continuation records
{
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 = scm.get(rectype);
if (constructor != null) {
retval = (Record) constructor.newInstance(new Object[] {ssc.get(rectype), ssc.get(size), data});
} else {
retval = new UnknownRecord(rectype, size, data);
}
} catch (Exception introspectionException) {
introspectionException.printStackTrace();
throw new RecordFormatException("Unable to construct record instance, the following exception occured: " +
introspectionException.getMessage());
}
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() {
return scm.getTable();
}
}