Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574,659576-660255,660257-660262,660264-660279,660281-660343,660345-660473,660475-660827,660829-660833,660835-660888,660890-663321,663323-663435,663437-663764,663766-663854,663856-664219,664221-664489,664494-664514,664516-668013,668015-668142,668144-668152,668154,668156-668256,668258,668260-669139,669141-669455,669457-669657,669659-669808,669810-670189,670191-671321,671323-672229,672231-672549,672551-672552,672554-672561,672563-672566,672568,672571-673049,673051-673852,673854-673862,673864-673986,673988-673996,673998-674347,674349-674890,674892-674910,674912-674936,674938-674952,674954-675078,675080-675085,675087-675217,675219-675660,675662-675670,675672-675716,675718-675726,675728-675733,675735-675775,675777-675782,675784,675786-675791,675794-675852,675854-676200,676202,676204,676206-676220,676222-676309,676311-676456,676458-676994,676996-677027,677030-677040,677042-677056,677058-677375,677377-677968,677970-677971,677973,677975-677994,677996-678286,678288-678538,678540-680393,680395-680469,680471-680529,680531-680852,680854-681529,681531-681571,681573-682224,682226,682228,682231-682281,682283-682335,682337-682511 via svnmerge from

https://svn.apache.org/repos/asf/poi/trunk

........
  r682508 | josh | 2008-08-04 22:08:11 +0100 (Mon, 04 Aug 2008) | 1 line
  
  Patch 44894 - refactoring duplicate logic from EventRecordFactory to RecordFactory
........
  r682510 | josh | 2008-08-04 22:14:37 +0100 (Mon, 04 Aug 2008) | 1 line
  
  removed debugging code leftover from r682508 (patch 44894)
........
  r682511 | nick | 2008-08-04 22:21:16 +0100 (Mon, 04 Aug 2008) | 1 line
  
  Fix bug #45538 - Include excel headers and footers in the output of ExcelExtractor
........


git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@682516 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2008-08-04 21:31:51 +00:00
parent cfdf265975
commit 946dea40a7
19 changed files with 533 additions and 791 deletions

View File

@ -52,6 +52,8 @@
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action> <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release> </release>
<release version="3.1.1-alpha1" date="2008-??-??"> <release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45538 - Include excel headers and footers in the output of ExcelExtractor</action>
<action dev="POI-DEVELOPERS" type="fix">44894 - refactor duplicate logic from EventRecordFactory to RecordFactory</action>
<action dev="POI-DEVELOPERS" type="add">Support for Headers / Footers in HSLF</action> <action dev="POI-DEVELOPERS" type="add">Support for Headers / Footers in HSLF</action>
<action dev="POI-DEVELOPERS" type="fix">44953 - Extensive fixes for data validation</action> <action dev="POI-DEVELOPERS" type="fix">44953 - Extensive fixes for data validation</action>
<action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action> <action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action>

View File

@ -49,6 +49,8 @@
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action> <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release> </release>
<release version="3.1.1-alpha1" date="2008-??-??"> <release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45538 - Include excel headers and footers in the output of ExcelExtractor</action>
<action dev="POI-DEVELOPERS" type="fix">44894 - refactor duplicate logic from EventRecordFactory to RecordFactory</action>
<action dev="POI-DEVELOPERS" type="add">Support for Headers / Footers in HSLF</action> <action dev="POI-DEVELOPERS" type="add">Support for Headers / Footers in HSLF</action>
<action dev="POI-DEVELOPERS" type="fix">44953 - Extensive fixes for data validation</action> <action dev="POI-DEVELOPERS" type="fix">44953 - Extensive fixes for data validation</action>
<action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action> <action dev="POI-DEVELOPERS" type="fix">45519 - Fixed to keep datavalidation records together</action>

View File

@ -18,241 +18,60 @@
package org.apache.poi.hssf.eventmodel; package org.apache.poi.hssf.eventmodel;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Constructor; import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.BackupRecord;
import org.apache.poi.hssf.record.BlankRecord;
import org.apache.poi.hssf.record.BookBoolRecord;
import org.apache.poi.hssf.record.BoolErrRecord;
import org.apache.poi.hssf.record.BottomMarginRecord;
import org.apache.poi.hssf.record.BoundSheetRecord;
import org.apache.poi.hssf.record.CalcCountRecord;
import org.apache.poi.hssf.record.CalcModeRecord;
import org.apache.poi.hssf.record.CodepageRecord;
import org.apache.poi.hssf.record.ColumnInfoRecord;
import org.apache.poi.hssf.record.ContinueRecord;
import org.apache.poi.hssf.record.CountryRecord;
import org.apache.poi.hssf.record.DBCellRecord;
import org.apache.poi.hssf.record.DSFRecord;
import org.apache.poi.hssf.record.DateWindow1904Record;
import org.apache.poi.hssf.record.DefaultColWidthRecord;
import org.apache.poi.hssf.record.DefaultRowHeightRecord;
import org.apache.poi.hssf.record.DeltaRecord;
import org.apache.poi.hssf.record.DimensionsRecord;
import org.apache.poi.hssf.record.EOFRecord;
import org.apache.poi.hssf.record.ExtSSTRecord;
import org.apache.poi.hssf.record.ExtendedFormatRecord;
import org.apache.poi.hssf.record.ExternSheetRecord;
import org.apache.poi.hssf.record.FnGroupCountRecord;
import org.apache.poi.hssf.record.FontRecord;
import org.apache.poi.hssf.record.FooterRecord;
import org.apache.poi.hssf.record.FormatRecord;
import org.apache.poi.hssf.record.GridsetRecord;
import org.apache.poi.hssf.record.GutsRecord;
import org.apache.poi.hssf.record.HCenterRecord;
import org.apache.poi.hssf.record.HeaderRecord;
import org.apache.poi.hssf.record.HideObjRecord;
import org.apache.poi.hssf.record.IndexRecord;
import org.apache.poi.hssf.record.InterfaceEndRecord;
import org.apache.poi.hssf.record.InterfaceHdrRecord;
import org.apache.poi.hssf.record.IterationRecord;
import org.apache.poi.hssf.record.LabelRecord;
import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.LeftMarginRecord;
import org.apache.poi.hssf.record.MMSRecord;
import org.apache.poi.hssf.record.MergeCellsRecord;
import org.apache.poi.hssf.record.MulBlankRecord;
import org.apache.poi.hssf.record.MulRKRecord;
import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.PaneRecord;
import org.apache.poi.hssf.record.PaletteRecord;
import org.apache.poi.hssf.record.PasswordRecord;
import org.apache.poi.hssf.record.PasswordRev4Record;
import org.apache.poi.hssf.record.PrecisionRecord;
import org.apache.poi.hssf.record.PrintGridlinesRecord;
import org.apache.poi.hssf.record.PrintHeadersRecord;
import org.apache.poi.hssf.record.PrintSetupRecord;
import org.apache.poi.hssf.record.ProtectRecord;
import org.apache.poi.hssf.record.ProtectionRev4Record;
import org.apache.poi.hssf.record.RKRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.record.RefModeRecord;
import org.apache.poi.hssf.record.RefreshAllRecord;
import org.apache.poi.hssf.record.RightMarginRecord;
import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.hssf.record.SSTRecord;
import org.apache.poi.hssf.record.SaveRecalcRecord;
import org.apache.poi.hssf.record.SelectionRecord;
import org.apache.poi.hssf.record.SharedFormulaRecord;
import org.apache.poi.hssf.record.StringRecord;
import org.apache.poi.hssf.record.StyleRecord;
import org.apache.poi.hssf.record.TabIdRecord;
import org.apache.poi.hssf.record.TableRecord;
import org.apache.poi.hssf.record.TopMarginRecord;
import org.apache.poi.hssf.record.UnknownRecord;
import org.apache.poi.hssf.record.UseSelFSRecord;
import org.apache.poi.hssf.record.VCenterRecord;
import org.apache.poi.hssf.record.WSBoolRecord;
import org.apache.poi.hssf.record.WindowOneRecord;
import org.apache.poi.hssf.record.WindowProtectRecord;
import org.apache.poi.hssf.record.WindowTwoRecord;
import org.apache.poi.hssf.record.WriteAccessRecord;
import org.apache.poi.hssf.record.WriteProtectRecord;
import org.apache.poi.hssf.record.FilePassRecord;
import org.apache.poi.hssf.record.NoteRecord;
import org.apache.poi.hssf.record.*;
/** /**
* Event-based record factory. As opposed to RecordFactory * Event-based record factory. As opposed to RecordFactory
* this refactored version throws record events as it comes * this version sends {@link ERFListener#processRecord(Record) } messages to
* accross the records. I throws the "lazily" one record behind * the supplied listener. Record notifications are sent one record behind
* to ensure that ContinueRecords are processed first. * to ensure that {@link ContinueRecord}s are processed first.
* *
* @author Andrew C. Oliver (acoliver@apache.org) - probably to blame for the bugs (so yank his chain on the list) * @author Andrew C. Oliver (acoliver@apache.org) - probably to blame for the bugs (so yank his chain on the list)
* @author Marc Johnson (mjohnson at apache dot org) - methods taken from RecordFactory * @author Marc Johnson (mjohnson at apache dot org) - methods taken from RecordFactory
* @author Glen Stampoultzis (glens at apache.org) - methods taken from RecordFactory * @author Glen Stampoultzis (glens at apache.org) - methods taken from RecordFactory
* @author Csaba Nagy (ncsaba at yahoo dot com) * @author Csaba Nagy (ncsaba at yahoo dot com)
*/ */
public class EventRecordFactory public final class EventRecordFactory {
{
private final ERFListener _listener;
private final short[] _sids;
/** /**
* contains the classes for all the records we want to parse.
*/
private static final Class[] records;
static {
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, SharedFormulaRecord.class,
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
NoteRecord.class, TableRecord.class
};
}
/**
* cache of the recordsToMap();
*/
private static Map recordsMap = recordsToMap(records);
/**
* cache of the return of getAllKnownSids so that we don't have to
* expensively get them every time.
*/
private static short[] sidscache;
/**
* List of the listners that are registred. should all be ERFListener
*/
private List listeners;
/**
* instance is abortable or not
*/
private boolean abortable;
/**
* Construct an abortable EventRecordFactory.
* The same as calling new EventRecordFactory(true)
* @see #EventRecordFactory(boolean)
*/
public EventRecordFactory() {
this(true);
}
/**
* Create an EventRecordFactory
* @param abortable specifies whether the return from the listener
* handler functions are obeyed. False means they are ignored. True
* means the event loop exits on error.
*/
public EventRecordFactory(boolean abortable) {
this.abortable = abortable;
listeners = new ArrayList(recordsMap.size());
if (sidscache == null) {
sidscache = getAllKnownRecordSIDs();
}
}
/**
* Register a listener for records. These can be for all records
* or just a subset.
* *
* @param sids an array of Record.sid values identifying the records * @param sids an array of Record.sid values identifying the records
* the listener will work with. Alternatively if this is "null" then * the listener will work with. Alternatively if this is "null" then
* all records are passed. * all records are passed. For all 'known' record types use {@link RecordFactory#getAllKnownRecordSIDs()}
*/ */
public void registerListener(ERFListener listener, short[] sids) { public EventRecordFactory(ERFListener listener, short[] sids) {
if (sids == null) _listener = listener;
sids = sidscache; if (sids == null) {
ERFListener wrapped = new ListenerWrapper(listener, sids, abortable); _sids = null;
listeners.add(wrapped); } else {
_sids = (short[]) sids.clone();
Arrays.sort(_sids); // for faster binary search
}
} }
private boolean isSidIncluded(short sid) {
if (_sids == null) {
return true;
}
return Arrays.binarySearch(_sids, sid) >= 0;
}
/**
* used for unit tests to test the registration of record listeners.
* @return Iterator of ERFListeners
*/
protected Iterator listeners() {
return listeners.iterator();
}
/** /**
* sends the record event to all registered listeners. * sends the record event to all registered listeners.
* @param record the record to be thrown. * @param record the record to be thrown.
* @return boolean abort. If exitability is turned on this aborts * @return <code>false</code> to abort. This aborts
* out of the event loop should any listener specify to do so. * out of the event loop should the listener return false
*/ */
private boolean throwRecordEvent(Record record) private boolean processRecord(Record record) {
{ if (!isSidIncluded(record.getSid())) {
boolean result = true; return true;
Iterator i = listeners.iterator(); }
return _listener.processRecord(record);
while (i.hasNext()) {
result = ((ERFListener) i.next()).processRecord(record);
if (abortable == true && result == false) {
break;
}
}
return result;
} }
/** /**
@ -264,220 +83,39 @@ public class EventRecordFactory
* @exception RecordFormatException on error processing the * @exception RecordFormatException on error processing the
* InputStream * InputStream
*/ */
public void processRecords(InputStream in) public void processRecords(InputStream in) throws RecordFormatException {
throws RecordFormatException Record last_record = null;
{
Record last_record = null;
RecordInputStream recStream = new RecordInputStream(in); RecordInputStream recStream = new RecordInputStream(in);
while (recStream.hasNextRecord()) { while (recStream.hasNextRecord()) {
recStream.nextRecord(); recStream.nextRecord();
Record[] recs = createRecord(recStream); // handle MulRK records Record[] recs = RecordFactory.createRecord(recStream); // handle MulRK records
if (recs.length > 1) if (recs.length > 1) {
{ for (int k = 0; k < recs.length; k++) {
for (int k = 0; k < recs.length; k++) if ( last_record != null ) {
{ if (!processRecord(last_record)) {
if ( last_record != null ) { return;
if (throwRecordEvent(last_record) == false && abortable == true) {
last_record = null;
break;
}
}
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 (last_record != null) {
if (throwRecordEvent(last_record) == false && abortable == true) {
last_record = null;
break;
}
}
last_record = record;
}
} }
} }
last_record = recs[ k ]; // do to keep the algorithm homogeneous...you can't
} // actually continue a number record anyhow.
} else {
Record record = recs[ 0 ];
if (record != null) {
if (last_record != null) { if (last_record != null) {
throwRecordEvent(last_record); if (!processRecord(last_record)) {
} return;
} }
}
/** last_record = record;
* create a record, if there are MUL records than multiple records
* are returned digested into the non-mul form.
*/
public static Record [] createRecord(RecordInputStream in)
{
Record retval = null;
Record[] realretval = null;
try
{
Constructor constructor =
( Constructor ) recordsMap.get(new Short(in.getSid()));
if (constructor != null)
{
retval = ( Record ) constructor.newInstance(new Object[]
{
in
});
}
else
{
retval = new UnknownRecord(in);
}
}
catch (Exception introspectionException)
{
throw new RecordFormatException("Unable to construct record instance" , introspectionException);
}
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;
}
/**
* @return an array of all the SIDS for all known records
*/
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;
}
/**
* gets the record constructors and sticks them in the map by SID
* @return map of SIDs to short,short,byte[] constructors for Record classes
* most of org.apache.poi.hssf.record.*
*/
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[]
{
RecordInputStream.class
});
}
catch (Exception illegalArgumentException)
{
throw new RecordFormatException(
"Unable to determine record types");
}
result.put(new Short(sid), constructor);
}
return result;
}
}
/**
* ListenerWrapper just wraps an ERFListener and adds support for throwing
* the event to multiple SIDs
*/
class ListenerWrapper implements ERFListener {
private ERFListener listener;
private short[] sids;
private boolean abortable;
ListenerWrapper(ERFListener listener, short[] sids, boolean abortable) {
this.listener = listener;
this.sids = sids;
this.abortable = abortable;
}
public boolean processRecord(Record rec)
{
boolean result = true;
for (int k = 0; k < sids.length; k++) {
if (sids[k] == rec.getSid()) {
result = listener.processRecord(rec);
if (abortable == true && result == false) {
break;
} }
} }
} }
return result;
if (last_record != null) {
processRecord(last_record);
}
} }
} }

View File

@ -65,8 +65,7 @@ public class ModelFactory implements ERFListener
* Start processing the Workbook stream into Model events. * Start processing the Workbook stream into Model events.
*/ */
public void run(InputStream stream) { public void run(InputStream stream) {
EventRecordFactory factory = new EventRecordFactory(true); EventRecordFactory factory = new EventRecordFactory(this,null);
factory.registerListener(this,null);
lastEOF = true; lastEOF = true;
factory.processRecords(stream); factory.processRecords(stream);
} }

View File

@ -19,8 +19,11 @@ package org.apache.poi.hssf.extractor;
import java.io.IOException; import java.io.IOException;
import org.apache.poi.POIOLE2TextExtractor; import org.apache.poi.POIOLE2TextExtractor;
import org.apache.poi.hssf.usermodel.HeaderFooter;
import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFComment; import org.apache.poi.hssf.usermodel.HSSFComment;
import org.apache.poi.hssf.usermodel.HSSFFooter;
import org.apache.poi.hssf.usermodel.HSSFHeader;
import org.apache.poi.hssf.usermodel.HSSFRichTextString; import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFSheet;
@ -89,6 +92,13 @@ public class ExcelExtractor extends POIOLE2TextExtractor {
} }
} }
// Header text, if there is any
if(sheet.getHeader() != null) {
text.append(
extractHeaderFooter(sheet.getHeader())
);
}
int firstRow = sheet.getFirstRowNum(); int firstRow = sheet.getFirstRowNum();
int lastRow = sheet.getLastRowNum(); int lastRow = sheet.getLastRowNum();
for(int j=firstRow;j<=lastRow;j++) { for(int j=firstRow;j<=lastRow;j++) {
@ -154,8 +164,37 @@ public class ExcelExtractor extends POIOLE2TextExtractor {
// Finish off the row // Finish off the row
text.append("\n"); text.append("\n");
} }
// Finally Feader text, if there is any
if(sheet.getFooter() != null) {
text.append(
extractHeaderFooter(sheet.getFooter())
);
}
} }
return text.toString(); return text.toString();
} }
private String extractHeaderFooter(HeaderFooter hf) {
StringBuffer text = new StringBuffer();
if(hf.getLeft() != null) {
text.append(hf.getLeft());
}
if(hf.getCenter() != null) {
if(text.length() > 0)
text.append("\t");
text.append(hf.getCenter());
}
if(hf.getRight() != null) {
if(text.length() > 0)
text.append("\t");
text.append(hf.getRight());
}
if(text.length() > 0)
text.append("\n");
return text.toString();
}
} }

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,293 +15,358 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.*; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* Title: Record Factory<P> * Title: Record Factory<P>
* Description: Takes a stream and outputs an array of Record objects.<P> * Description: Takes a stream and outputs an array of Record objects.<P>
* *
* @deprecated use {@link org.apache.poi.hssf.eventmodel.EventRecordFactory} instead
* @see org.apache.poi.hssf.eventmodel.EventRecordFactory * @see org.apache.poi.hssf.eventmodel.EventRecordFactory
* @author Andrew C. Oliver (acoliver at apache dot org) * @author Andrew C. Oliver (acoliver at apache dot org)
* @author Marc Johnson (mjohnson at apache dot org) * @author Marc Johnson (mjohnson at apache dot org)
* @author Glen Stampoultzis (glens at apache.org) * @author Glen Stampoultzis (glens at apache.org)
* @author Csaba Nagy (ncsaba at yahoo dot com) * @author Csaba Nagy (ncsaba at yahoo dot com)
*/ */
public final class RecordFactory {
private static final int NUM_RECORDS = 512;
public class RecordFactory private static final Class[] CONSTRUCTOR_ARGS = { RecordInputStream.class, };
{
private static int NUM_RECORDS = 10000;
private static final Class[] records;
static { /**
records = new Class[] * contains the classes for all the records we want to parse.<br/>
{ * Note - this most but not *every* subclass of Record.
BOFRecord.class, InterfaceHdrRecord.class, MMSRecord.class, */
InterfaceEndRecord.class, WriteAccessRecord.class, private static final Class[] records = {
CodepageRecord.class, DSFRecord.class, TabIdRecord.class, BackupRecord.class,
FnGroupCountRecord.class, WindowProtectRecord.class, BlankRecord.class,
ProtectRecord.class, PasswordRecord.class, ProtectionRev4Record.class, BOFRecord.class,
PasswordRev4Record.class, WindowOneRecord.class, BackupRecord.class, BookBoolRecord.class,
HideObjRecord.class, DateWindow1904Record.class, BoolErrRecord.class,
PrecisionRecord.class, RefreshAllRecord.class, BookBoolRecord.class, BottomMarginRecord.class,
FontRecord.class, FormatRecord.class, ExtendedFormatRecord.class, BoundSheetRecord.class,
StyleRecord.class, UseSelFSRecord.class, BoundSheetRecord.class, CalcCountRecord.class,
CountryRecord.class, SSTRecord.class, ExtSSTRecord.class, CalcModeRecord.class,
EOFRecord.class, IndexRecord.class, CalcModeRecord.class, CFHeaderRecord.class,
CalcCountRecord.class, RefModeRecord.class, IterationRecord.class, CFRuleRecord.class,
DeltaRecord.class, SaveRecalcRecord.class, PrintHeadersRecord.class, ChartRecord.class,
PrintGridlinesRecord.class, GridsetRecord.class, GutsRecord.class, ChartTitleFormatRecord.class,
DefaultRowHeightRecord.class, WSBoolRecord.class, HeaderRecord.class, CodepageRecord.class,
FooterRecord.class, HCenterRecord.class, VCenterRecord.class, ColumnInfoRecord.class,
PrintSetupRecord.class, DefaultColWidthRecord.class, ContinueRecord.class,
DimensionsRecord.class, RowRecord.class, LabelSSTRecord.class, CountryRecord.class,
RKRecord.class, NumberRecord.class, DBCellRecord.class, CRNCountRecord.class,
WindowTwoRecord.class, SelectionRecord.class, ContinueRecord.class, CRNRecord.class,
LabelRecord.class, BlankRecord.class, ColumnInfoRecord.class, DateWindow1904Record.class,
MulRKRecord.class, MulBlankRecord.class, MergeCellsRecord.class, DBCellRecord.class,
FormulaRecord.class, BoolErrRecord.class, ExternSheetRecord.class, DefaultColWidthRecord.class,
NameRecord.class, LeftMarginRecord.class, RightMarginRecord.class, DefaultRowHeightRecord.class,
TopMarginRecord.class, BottomMarginRecord.class, DeltaRecord.class,
DrawingRecord.class, DrawingGroupRecord.class, DrawingSelectionRecord.class, DimensionsRecord.class,
ObjRecord.class, TextObjectRecord.class, DrawingGroupRecord.class,
PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class, DrawingRecord.class,
HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class, DrawingSelectionRecord.class,
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class, DSFRecord.class,
NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class, DVALRecord.class,
FileSharingRecord.class, ChartTitleFormatRecord.class, DVRecord.class,
DVRecord.class, DVALRecord.class, UncalcedRecord.class, EOFRecord.class,
ChartRecord.class, LegendRecord.class, ChartTitleFormatRecord.class, ExtendedFormatRecord.class,
SeriesRecord.class, SeriesTextRecord.class, ExternalNameRecord.class,
HyperlinkRecord.class, ExternSheetRecord.class,
ExternalNameRecord.class, // TODO - same changes in non-@deprecated version of this class ExtSSTRecord.class,
SupBookRecord.class, FilePassRecord.class,
CRNCountRecord.class, FileSharingRecord.class,
CRNRecord.class, FnGroupCountRecord.class,
CFHeaderRecord.class, FontRecord.class,
CFRuleRecord.class, FooterRecord.class,
TableRecord.class FormatRecord.class,
}; FormulaRecord.class,
} GridsetRecord.class,
private static Map recordsMap = recordsToMap(records); GutsRecord.class,
HCenterRecord.class,
HeaderRecord.class,
HideObjRecord.class,
HorizontalPageBreakRecord.class,
HyperlinkRecord.class,
IndexRecord.class,
InterfaceEndRecord.class,
InterfaceHdrRecord.class,
IterationRecord.class,
LabelRecord.class,
LabelSSTRecord.class,
LeftMarginRecord.class,
LegendRecord.class,
MergeCellsRecord.class,
MMSRecord.class,
MulBlankRecord.class,
MulRKRecord.class,
NameRecord.class,
NoteRecord.class,
NumberRecord.class,
ObjectProtectRecord.class,
ObjRecord.class,
PaletteRecord.class,
PaneRecord.class,
PasswordRecord.class,
PasswordRev4Record.class,
PrecisionRecord.class,
PrintGridlinesRecord.class,
PrintHeadersRecord.class,
PrintSetupRecord.class,
ProtectionRev4Record.class,
ProtectRecord.class,
RecalcIdRecord.class,
RefModeRecord.class,
RefreshAllRecord.class,
RightMarginRecord.class,
RKRecord.class,
RowRecord.class,
SaveRecalcRecord.class,
ScenarioProtectRecord.class,
SelectionRecord.class,
SeriesRecord.class,
SeriesTextRecord.class,
SharedFormulaRecord.class,
SSTRecord.class,
StringRecord.class,
StyleRecord.class,
SupBookRecord.class,
TabIdRecord.class,
TableRecord.class,
TextObjectRecord.class,
TopMarginRecord.class,
UncalcedRecord.class,
UseSelFSRecord.class,
VCenterRecord.class,
VerticalPageBreakRecord.class,
WindowOneRecord.class,
WindowProtectRecord.class,
WindowTwoRecord.class,
WriteAccessRecord.class,
WriteProtectRecord.class,
WSBoolRecord.class,
};
/** /**
* changes the default capacity (10000) to handle larger files * cache of the recordsToMap();
*/ */
private static Map recordsMap = recordsToMap(records);
public static void setCapacity(int capacity) private static short[] _allKnownRecordSIDs;
{
NUM_RECORDS = capacity;
}
/** /**
* Create an array of records from an input stream * create a record, if there are MUL records than multiple records
* * are returned digested into the non-mul form.
* @param in the InputStream from which the records will be */
* obtained public static Record [] createRecord(RecordInputStream in) {
* Constructor constructor = (Constructor) recordsMap.get(new Short(in.getSid()));
* @return an array of Records created from the InputStream
*
* @exception RecordFormatException on error processing the
* InputStream
*/
public static List createRecords(InputStream in) if (constructor == null) {
throws RecordFormatException return new Record[] { new UnknownRecord(in), };
{ }
ArrayList records = new ArrayList(NUM_RECORDS);
RecordInputStream recStream = new RecordInputStream(in); Record retval;
DrawingRecord lastDrawingRecord = new DrawingRecord( );
Record lastRecord = null;
while (recStream.hasNextRecord()) {
recStream.nextRecord();
if (recStream.getSid() != 0)
{
Record[] recs = createRecord(recStream); // handle MulRK records
if (recs.length > 1) try {
{ retval = ( Record ) constructor.newInstance(new Object[] { in });
for (int k = 0; k < recs.length; k++) } catch (InvocationTargetException e) {
{ throw new RecordFormatException("Unable to construct record instance" , e.getTargetException());
records.add( } catch (IllegalArgumentException e) {
recs[ k ]); // these will be number records throw new RuntimeException(e);
} } catch (InstantiationException e) {
} throw new RuntimeException(e);
else } catch (IllegalAccessException e) {
{ throw new RuntimeException(e);
Record record = recs[ 0 ]; }
if (record != null) if (retval instanceof RKRecord) {
{ // RK record is a slightly smaller alternative to NumberRecord
if (record.getSid() == DrawingGroupRecord.sid // POI likes NumberRecord better
&& lastRecord instanceof DrawingGroupRecord) RKRecord rk = ( RKRecord ) retval;
{ NumberRecord num = new NumberRecord();
DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) lastRecord;
lastDGRecord.join((AbstractEscherHolderRecord) record);
}
else if (record.getSid() == ContinueRecord.sid &&
((lastRecord instanceof ObjRecord) || (lastRecord instanceof TextObjectRecord))) {
// Drawing records have a very strange continue behaviour.
//There can actually be OBJ records mixed between the continues.
lastDrawingRecord.processContinueRecord( ((ContinueRecord)record).getData() );
//we must rememeber the position of the continue record.
//in the serialization procedure the original structure of records must be preserved
records.add(record);
} else if (record.getSid() == ContinueRecord.sid &&
(lastRecord instanceof DrawingGroupRecord)) {
((DrawingGroupRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
} else if (record.getSid() == ContinueRecord.sid &&
(lastRecord instanceof StringRecord)) {
((StringRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
} else if (record.getSid() == ContinueRecord.sid) {
if (lastRecord instanceof UnknownRecord) {
//Gracefully handle records that we dont know about,
//that happen to be continued
records.add(record);
} else
throw new RecordFormatException("Unhandled Continue Record");
}
else {
lastRecord = record;
if (record instanceof DrawingRecord)
lastDrawingRecord = (DrawingRecord) record;
records.add(record);
}
}
}
}
}
return records; num.setColumn(rk.getColumn());
} num.setRow(rk.getRow());
num.setXFIndex(rk.getXFIndex());
num.setValue(rk.getRKNumber());
return new Record[] { num, };
}
if (retval instanceof DBCellRecord) {
// Not needed by POI. Regenerated from scratch by POI when spreadsheet is written
return new Record[] { null, };
}
// expand multiple records where necessary
if (retval instanceof MulRKRecord) {
MulRKRecord mrk = ( MulRKRecord ) retval;
public static Record [] createRecord(RecordInputStream in) Record[] mulRecs = new Record[ mrk.getNumColumns() ];
{ for (int k = 0; k < mrk.getNumColumns(); k++) {
Record retval; NumberRecord nr = new NumberRecord();
Record[] realretval = null;
try nr.setColumn(( short ) (k + mrk.getFirstColumn()));
{ nr.setRow(mrk.getRow());
Constructor constructor = nr.setXFIndex(mrk.getXFAt(k));
( Constructor ) recordsMap.get(new Short(in.getSid())); nr.setValue(mrk.getRKNumberAt(k));
mulRecs[ k ] = nr;
}
return mulRecs;
}
if (retval instanceof MulBlankRecord) {
MulBlankRecord mb = ( MulBlankRecord ) retval;
if (constructor != null) Record[] mulRecs = new Record[ mb.getNumColumns() ];
{ for (int k = 0; k < mb.getNumColumns(); k++) {
retval = ( Record ) constructor.newInstance(new Object[] BlankRecord br = new BlankRecord();
{
in
});
}
else
{
retval = new UnknownRecord(in);
}
}
catch (Exception introspectionException)
{
throw new RecordFormatException("Unable to construct record instance",introspectionException);
}
if (retval instanceof RKRecord)
{
RKRecord rk = ( RKRecord ) retval;
NumberRecord num = new NumberRecord();
num.setColumn(rk.getColumn()); br.setColumn(( short ) (k + mb.getFirstColumn()));
num.setRow(rk.getRow()); br.setRow(mb.getRow());
num.setXFIndex(rk.getXFIndex()); br.setXFIndex(mb.getXFAt(k));
num.setValue(rk.getRKNumber()); mulRecs[ k ] = br;
retval = num; }
} return mulRecs;
else if (retval instanceof DBCellRecord) }
{ return new Record[] { retval, };
retval = null; }
}
else if (retval instanceof MulRKRecord)
{
MulRKRecord mrk = ( MulRKRecord ) retval;
realretval = new Record[ mrk.getNumColumns() ]; /**
for (int k = 0; k < mrk.getNumColumns(); k++) * @return an array of all the SIDS for all known records
{ */
NumberRecord nr = new NumberRecord(); public static short[] getAllKnownRecordSIDs() {
if (_allKnownRecordSIDs == null) {
short[] results = new short[ recordsMap.size() ];
int i = 0;
nr.setColumn(( short ) (k + mrk.getFirstColumn())); for (Iterator iterator = recordsMap.keySet().iterator(); iterator.hasNext(); ) {
nr.setRow(mrk.getRow()); Short sid = (Short) iterator.next();
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() ]; results[i++] = sid.shortValue();
for (int k = 0; k < mb.getNumColumns(); k++) }
{ Arrays.sort(results);
BlankRecord br = new BlankRecord(); _allKnownRecordSIDs = results;
}
br.setColumn(( short ) (k + mb.getFirstColumn())); return (short[]) _allKnownRecordSIDs.clone();
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() /**
{ * gets the record constructors and sticks them in the map by SID
short[] results = new short[ recordsMap.size() ]; * @return map of SIDs to short,short,byte[] constructors for Record classes
int i = 0; * most of org.apache.poi.hssf.record.*
*/
private static Map recordsToMap(Class [] records) {
Map result = new HashMap();
Set uniqueRecClasses = new HashSet(records.length * 3 / 2);
for (Iterator iterator = recordsMap.keySet().iterator(); for (int i = 0; i < records.length; i++) {
iterator.hasNext(); )
{
Short sid = ( Short ) iterator.next();
results[ i++ ] = sid.shortValue(); Class recClass = records[ i ];
} if(!Record.class.isAssignableFrom(recClass)) {
return results; throw new RuntimeException("Invalid record sub-class (" + recClass.getName() + ")");
} }
if(Modifier.isAbstract(recClass.getModifiers())) {
throw new RuntimeException("Invalid record class (" + recClass.getName() + ") - must not be abstract");
}
if(!uniqueRecClasses.add(recClass)) {
throw new RuntimeException("duplicate record class (" + recClass.getName() + ")");
}
private static Map recordsToMap(Class [] records) short sid;
{ Constructor constructor;
Map result = new HashMap(); try {
Constructor constructor; sid = recClass.getField("sid").getShort(null);
constructor = recClass.getConstructor(CONSTRUCTOR_ARGS);
} catch (Exception illegalArgumentException) {
throw new RecordFormatException(
"Unable to determine record types");
}
Short key = new Short(sid);
if (result.containsKey(key)) {
Class prev = (Class)result.get(key);
throw new RuntimeException("duplicate record sid 0x" + Integer.toHexString(sid).toUpperCase()
+ " for classes (" + recClass.getName() + ") and (" + prev.getName() + ")");
}
result.put(key, constructor);
}
return result;
}
for (int i = 0; i < records.length; i++) /**
{ * Create an array of records from an input stream
Class record; *
short sid; * @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 {
record = records[ i ]; List records = new ArrayList(NUM_RECORDS);
try
{ RecordInputStream recStream = new RecordInputStream(in);
sid = record.getField("sid").getShort(null); DrawingRecord lastDrawingRecord = new DrawingRecord( );
constructor = record.getConstructor(new Class[] Record lastRecord = null;
{ while (recStream.hasNextRecord()) {
RecordInputStream.class recStream.nextRecord();
}); if (recStream.getSid() != 0) {
} Record[] recs = createRecord(recStream); // handle MulRK records
catch (Exception illegalArgumentException)
{ if (recs.length > 1) {
throw new RecordFormatException( for (int k = 0; k < recs.length; k++) {
"Unable to determine record types", illegalArgumentException); records.add(recs[ k ]); // these will be number records
} }
result.put(new Short(sid), constructor); } else {
} Record record = recs[ 0 ];
return result;
} if (record != null) {
if (record.getSid() == DrawingGroupRecord.sid
&& lastRecord instanceof DrawingGroupRecord) {
DrawingGroupRecord lastDGRecord = (DrawingGroupRecord) lastRecord;
lastDGRecord.join((AbstractEscherHolderRecord) record);
} else if (record.getSid() == ContinueRecord.sid &&
((lastRecord instanceof ObjRecord) || (lastRecord instanceof TextObjectRecord))) {
// Drawing records have a very strange continue behaviour.
//There can actually be OBJ records mixed between the continues.
lastDrawingRecord.processContinueRecord( ((ContinueRecord)record).getData() );
//we must remember the position of the continue record.
//in the serialization procedure the original structure of records must be preserved
records.add(record);
} else if (record.getSid() == ContinueRecord.sid &&
(lastRecord instanceof DrawingGroupRecord)) {
((DrawingGroupRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
} else if (record.getSid() == ContinueRecord.sid &&
(lastRecord instanceof StringRecord)) {
((StringRecord)lastRecord).processContinueRecord(((ContinueRecord)record).getData());
} else if (record.getSid() == ContinueRecord.sid) {
if (lastRecord instanceof UnknownRecord) {
//Gracefully handle records that we dont know about,
//that happen to be continued
records.add(record);
} else
throw new RecordFormatException("Unhandled Continue Record");
} else {
lastRecord = record;
if (record instanceof DrawingRecord) {
lastDrawingRecord = (DrawingRecord) record;
}
records.add(record);
}
}
}
}
}
return records;
}
} }

View File

@ -33,8 +33,7 @@ import org.apache.poi.ss.usermodel.Footer;
* <P> * <P>
* @author Shawn Laubach (slaubach at apache dot org) * @author Shawn Laubach (slaubach at apache dot org)
*/ */
public class HSSFFooter implements Footer { public class HSSFFooter implements Footer, HeaderFooter {
FooterRecord footerRecord; FooterRecord footerRecord;
String left; String left;
String center; String center;

View File

@ -33,7 +33,7 @@ import org.apache.poi.ss.usermodel.Header;
* *
* @author Shawn Laubach (slaubach at apache dot org) * @author Shawn Laubach (slaubach at apache dot org)
*/ */
public class HSSFHeader implements Header public class HSSFHeader implements Header, HeaderFooter
{ {
HeaderRecord headerRecord; HeaderRecord headerRecord;

View File

@ -34,7 +34,6 @@ import org.apache.poi.ddf.EscherBSERecord;
import org.apache.poi.ddf.EscherBitmapBlip; import org.apache.poi.ddf.EscherBitmapBlip;
import org.apache.poi.ddf.EscherBlipRecord; import org.apache.poi.ddf.EscherBlipRecord;
import org.apache.poi.ddf.EscherRecord; import org.apache.poi.ddf.EscherRecord;
import org.apache.poi.hssf.eventmodel.EventRecordFactory;
import org.apache.poi.hssf.model.Sheet; import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook; import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.AbstractEscherHolderRecord; import org.apache.poi.hssf.record.AbstractEscherHolderRecord;
@ -264,8 +263,6 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
// it happens to be spelled. // it happens to be spelled.
InputStream stream = directory.createDocumentInputStream(workbookName); InputStream stream = directory.createDocumentInputStream(workbookName);
EventRecordFactory factory = new EventRecordFactory();
List records = RecordFactory.createRecords(stream); List records = RecordFactory.createRecords(stream);
workbook = Workbook.createWorkbook(records); workbook = Workbook.createWorkbook(records);

View File

@ -0,0 +1,24 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.usermodel;
/**
* Common interface for {@link HSSFHeader} and
* {@link HSSFFooter}.
*/
public interface HeaderFooter extends org.apache.poi.ss.usermodel.HeaderFooter {
}

View File

@ -0,0 +1,32 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.ss.usermodel;
/**
* Common interface for {@link Header} and
* {@link Footer}.
*/
public interface HeaderFooter {
public String getLeft();
public void setLeft( String newLeft );
public String getCenter();
public void setCenter( String newCenter );
public String getRight();
public void setRight( String newRight );
}

View File

@ -18,80 +18,24 @@
package org.apache.poi.hssf.eventmodel; package org.apache.poi.hssf.eventmodel;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.util.Iterator;
import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.EOFRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.UnknownRecord;
import org.apache.poi.hssf.record.ContinueRecord;
import org.apache.poi.hssf.record.TestcaseRecordInputStream;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.record.BOFRecord;
import org.apache.poi.hssf.record.ContinueRecord;
import org.apache.poi.hssf.record.EOFRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RecordFactory;
import org.apache.poi.hssf.record.TestcaseRecordInputStream;
import org.apache.poi.hssf.record.UnknownRecord;
/** /**
* enclosing_type describe the purpose here * enclosing_type describe the purpose here
* *
* @author Andrew C. Oliver acoliver@apache.org * @author Andrew C. Oliver acoliver@apache.org
* @author Csaba Nagy (ncsaba at yahoo dot com) * @author Csaba Nagy (ncsaba at yahoo dot com)
*/ */
public class TestEventRecordFactory extends TestCase public final class TestEventRecordFactory extends TestCase {
{
boolean wascalled;
private EventRecordFactory factory;
/**
* Constructor for TestEventRecordFactory.
* @param arg0
*/
public TestEventRecordFactory(String arg0)
{
super(arg0);
}
public static void main(String[] args)
{
junit.textui.TestRunner.run(TestEventRecordFactory.class);
}
protected void setUp() throws Exception
{
super.setUp();
factory = new EventRecordFactory();
}
protected void tearDown() throws Exception
{
super.tearDown();
}
/**
* tests that a listener can be registered and that once
* registered can be returned as expected.
*/
public void testRegisterListener()
{
factory.registerListener(new ERFListener() {
public boolean processRecord(Record rec) {
return true;
}
},null);
Iterator i = factory.listeners();
assertTrue("iterator must have one",i.hasNext());
factory.registerListener(new ERFListener() {
public boolean processRecord(Record rec) {
return true;
}
},null);
i = factory.listeners();
i.next();
assertTrue("iterator must have two",i.hasNext());
factory = new EventRecordFactory();
}
/** /**
* tests that the records can be processed and properly return * tests that the records can be processed and properly return
@ -99,17 +43,17 @@ public class TestEventRecordFactory extends TestCase
*/ */
public void testProcessRecords() public void testProcessRecords()
{ {
byte[] bytes = null; final boolean[] wascalled = { false, }; // hack to pass boolean by ref into inner class
int offset = 0;
//boolean wascalled = false; ERFListener listener = new ERFListener() {
factory.registerListener(new ERFListener() {
public boolean processRecord(Record rec) { public boolean processRecord(Record rec) {
wascalled = true; wascalled[0] = true;
assertTrue("must be BOFRecord got SID="+rec.getSid(), assertTrue("must be BOFRecord got SID="+rec.getSid(),
(rec.getSid() == BOFRecord.sid)); (rec.getSid() == BOFRecord.sid));
return true; return true;
} }
}, new short[] {BOFRecord.sid}); };
EventRecordFactory factory = new EventRecordFactory(listener, new short[] {BOFRecord.sid});
BOFRecord bof = new BOFRecord(); BOFRecord bof = new BOFRecord();
bof.setBuild((short)0); bof.setBuild((short)0);
@ -120,23 +64,20 @@ public class TestEventRecordFactory extends TestCase
bof.setHistoryBitMask(BOFRecord.HISTORY_MASK); bof.setHistoryBitMask(BOFRecord.HISTORY_MASK);
EOFRecord eof = new EOFRecord(); EOFRecord eof = new EOFRecord();
bytes = new byte[bof.getRecordSize() + eof.getRecordSize()]; byte[] bytes = new byte[bof.getRecordSize() + eof.getRecordSize()];
int offset = 0;
offset = bof.serialize(offset,bytes); offset = bof.serialize(offset,bytes);
offset = eof.serialize(offset,bytes); offset = eof.serialize(offset,bytes);
factory.processRecords(new ByteArrayInputStream(bytes)); factory.processRecords(new ByteArrayInputStream(bytes));
assertTrue("The record listener must be called",wascalled); assertTrue("The record listener must be called", wascalled[0]);
} }
/** /**
* tests that the create record function returns a properly * tests that the create record function returns a properly
* constructed record in the simple case. * constructed record in the simple case.
*/ */
public void testCreateRecord() public void testCreateRecord() {
{
byte[] bytes = null;
byte[] nbytes = null;
Record[] records = null;
BOFRecord bof = new BOFRecord(); BOFRecord bof = new BOFRecord();
bof.setBuild((short)0); bof.setBuild((short)0);
bof.setBuildYear((short)1999); bof.setBuildYear((short)1999);
@ -145,11 +86,11 @@ public class TestEventRecordFactory extends TestCase
bof.setVersion((short)0x06); bof.setVersion((short)0x06);
bof.setHistoryBitMask(BOFRecord.HISTORY_MASK); bof.setHistoryBitMask(BOFRecord.HISTORY_MASK);
bytes = bof.serialize(); byte[] bytes = bof.serialize();
nbytes = new byte[bytes.length - 4]; byte[] nbytes = new byte[bytes.length - 4];
System.arraycopy(bytes,4,nbytes,0,nbytes.length); System.arraycopy(bytes,4,nbytes,0,nbytes.length);
records = factory.createRecord(new TestcaseRecordInputStream(bof.getSid(),(short)nbytes.length,nbytes)); Record[] records = RecordFactory.createRecord(new TestcaseRecordInputStream(bof.getSid(),(short)nbytes.length,nbytes));
assertTrue("record.length must be 1, was ="+records.length,records.length == 1); assertTrue("record.length must be 1, was ="+records.length,records.length == 1);
assertTrue("record is the same", compareRec(bof,records[0])); assertTrue("record is the same", compareRec(bof,records[0]));
@ -162,24 +103,19 @@ public class TestEventRecordFactory extends TestCase
* @param second the second record to compare * @param second the second record to compare
* @return boolean whether or not the record where equal * @return boolean whether or not the record where equal
*/ */
private boolean compareRec(Record first, Record second) private static boolean compareRec(Record first, Record second) {
{
boolean retval = true;
byte[] rec1 = first.serialize(); byte[] rec1 = first.serialize();
byte[] rec2 = second.serialize(); byte[] rec2 = second.serialize();
if (rec1.length == rec2.length) { if (rec1.length != rec2.length) {
for (int k=0; k<rec1.length; k++) { return false;
if (rec1[k] != rec2[k]) {
retval = false;
break;
}
}
} else {
retval = false;
} }
for (int k=0; k<rec1.length; k++) {
return retval; if (rec1[k] != rec2[k]) {
return false;
}
}
return true;
} }
@ -202,10 +138,8 @@ public class TestEventRecordFactory extends TestCase
* FAILURE: The wrong records are created or contain the wrong values <P> * FAILURE: The wrong records are created or contain the wrong values <P>
* *
*/ */
public void testContinuedUnknownRecord() public void testContinuedUnknownRecord() {
{ final byte[] data = {
final byte[] data = new byte[]
{
0, -1, 0, 0, // an unknown record with 0 length 0, -1, 0, 0, // an unknown record with 0 length
0x3C , 0, 3, 0, 1, 2, 3, // a continuation record with 3 bytes of data 0x3C , 0, 3, 0, 1, 2, 3, // a continuation record with 3 bytes of data
0x3C , 0, 1, 0, 4 // one more continuation record with 1 byte of data 0x3C , 0, 1, 0, 4 // one more continuation record with 1 byte of data
@ -213,8 +147,7 @@ public class TestEventRecordFactory extends TestCase
final int[] recCnt = { 0 }; final int[] recCnt = { 0 };
final int[] offset = { 0 }; final int[] offset = { 0 };
factory.registerListener( ERFListener listener = new ERFListener() {
new ERFListener() {
private String[] expectedRecordTypes = { private String[] expectedRecordTypes = {
UnknownRecord.class.getName(), UnknownRecord.class.getName(),
ContinueRecord.class.getName(), ContinueRecord.class.getName(),
@ -238,14 +171,11 @@ public class TestEventRecordFactory extends TestCase
assertEquals(message + " data byte " + i, data[offset[0]++], recData[i]); assertEquals(message + " data byte " + i, data[offset[0]++], recData[i]);
} }
} }
}, };
new short[] {-256, 0x3C} EventRecordFactory factory = new EventRecordFactory(listener, new short[] {-256, 0x3C});
);
factory.processRecords(new ByteArrayInputStream(data)); factory.processRecords(new ByteArrayInputStream(data));
assertEquals("nr. of processed records", 3, recCnt[0]); assertEquals("nr. of processed records", 3, recCnt[0]);
assertEquals("nr. of processed bytes", data.length, offset[0]); assertEquals("nr. of processed bytes", data.length, offset[0]);
} }
} }

View File

@ -255,4 +255,20 @@ public final class TestExcelExtractor extends TestCase {
ex.getText()); ex.getText());
assertEquals("Excel With Embeded", ex.getSummaryInformation().getTitle()); assertEquals("Excel With Embeded", ex.getSummaryInformation().getTitle());
} }
/**
* Test that we get text from headers and footers
*/
public void test45538() throws Exception {
String[] files = new String[] {
"45538_classic_Footer.xls", "45538_form_Footer.xls",
"45538_classic_Header.xls", "45538_form_Header.xls"
};
for(int i=0; i<files.length; i++) {
ExcelExtractor extractor = createExtractor(files[i]);
String text = extractor.getText();
assertTrue("Unable to find expected word in text\n" + text, text.contains("testdoc"));
assertTrue("Unable to find expected word in text\n" + text, text.contains("test phrase"));
}
}
} }

View File

@ -416,9 +416,9 @@ public final class TestSheet extends TestCase {
int size = sheet.getSize(); int size = sheet.getSize();
byte[] data = new byte[size]; byte[] data = new byte[size];
sheet.serialize(0, data); sheet.serialize(0, data);
EventRecordFactory erf = new EventRecordFactory();
MyIndexRecordListener myIndexListener = new MyIndexRecordListener(); MyIndexRecordListener myIndexListener = new MyIndexRecordListener();
erf.registerListener(myIndexListener, new short[] { IndexRecord.sid, }); EventRecordFactory erf = new EventRecordFactory(myIndexListener, new short[] { IndexRecord.sid, });
erf.processRecords(new ByteArrayInputStream(data)); erf.processRecords(new ByteArrayInputStream(data));
IndexRecord indexRecord = myIndexListener.getIndexRecord(); IndexRecord indexRecord = myIndexListener.getIndexRecord();
int dbCellRecordPos = indexRecord.getDbcellAt(0); int dbCellRecordPos = indexRecord.getDbcellAt(0);

View File

@ -594,9 +594,9 @@ public final class TestDataValidation extends TestCase {
byte[] wbData = baos.toByteArray(); byte[] wbData = baos.toByteArray();
if (false) { // TODO (Jul 2008) fix EventRecordFactory to process unknown records, (and DV records for that matter) if (false) { // TODO (Jul 2008) fix EventRecordFactory to process unknown records, (and DV records for that matter)
EventRecordFactory erf = new EventRecordFactory();
ERFListener erfListener = null; // new MyERFListener(); ERFListener erfListener = null; // new MyERFListener();
erf.registerListener(erfListener, null); EventRecordFactory erf = new EventRecordFactory(erfListener, null);
try { try {
POIFSFileSystem fs = new POIFSFileSystem(new ByteArrayInputStream(baos.toByteArray())); POIFSFileSystem fs = new POIFSFileSystem(new ByteArrayInputStream(baos.toByteArray()));
erf.processRecords(fs.createDocumentInputStream("Workbook")); erf.processRecords(fs.createDocumentInputStream("Workbook"));