From: Nick Burch Date: Mon, 4 Aug 2008 21:31:51 +0000 (+0000) Subject: Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-63924... X-Git-Tag: REL_3_5_BETA2~39 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=1ad91423db1937d685c2dcad005eb2fd12d63fb1;p=poi.git 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 --- diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 8e167940bc..569691b6cf 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -52,6 +52,8 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + 45538 - Include excel headers and footers in the output of ExcelExtractor + 44894 - refactor duplicate logic from EventRecordFactory to RecordFactory Support for Headers / Footers in HSLF 44953 - Extensive fixes for data validation 45519 - Fixed to keep datavalidation records together diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 790390b028..1b665f1ae7 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -49,6 +49,8 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + 45538 - Include excel headers and footers in the output of ExcelExtractor + 44894 - refactor duplicate logic from EventRecordFactory to RecordFactory Support for Headers / Footers in HSLF 44953 - Extensive fixes for data validation 45519 - Fixed to keep datavalidation records together diff --git a/src/java/org/apache/poi/hssf/eventmodel/EventRecordFactory.java b/src/java/org/apache/poi/hssf/eventmodel/EventRecordFactory.java index 8c4abb76ad..5cdd813700 100644 --- a/src/java/org/apache/poi/hssf/eventmodel/EventRecordFactory.java +++ b/src/java/org/apache/poi/hssf/eventmodel/EventRecordFactory.java @@ -18,241 +18,60 @@ package org.apache.poi.hssf.eventmodel; import java.io.InputStream; -import java.lang.reflect.Constructor; -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 java.util.Arrays; +import org.apache.poi.hssf.record.*; /** * Event-based record factory. As opposed to RecordFactory - * this refactored version throws record events as it comes - * accross the records. I throws the "lazily" one record behind - * to ensure that ContinueRecords are processed first. + * this version sends {@link ERFListener#processRecord(Record) } messages to + * the supplied listener. Record notifications are sent one record behind + * 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 Marc Johnson (mjohnson at apache dot org) - methods taken from RecordFactory * @author Glen Stampoultzis (glens at apache.org) - methods taken from RecordFactory * @author Csaba Nagy (ncsaba at yahoo dot com) */ -public class EventRecordFactory -{ - - /** - * contains the classes for all the records we want to parse. - */ - private static final Class[] records; +public final class EventRecordFactory { - 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; + private final ERFListener _listener; + private final short[] _sids; /** - * 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 * 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) { - if (sids == null) - sids = sidscache; - ERFListener wrapped = new ListenerWrapper(listener, sids, abortable); - listeners.add(wrapped); - } - - /** - * used for unit tests to test the registration of record listeners. - * @return Iterator of ERFListeners - */ - protected Iterator listeners() { - return listeners.iterator(); + public EventRecordFactory(ERFListener listener, short[] sids) { + _listener = listener; + if (sids == null) { + _sids = null; + } 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; + } + /** * sends the record event to all registered listeners. * @param record the record to be thrown. - * @return boolean abort. If exitability is turned on this aborts - * out of the event loop should any listener specify to do so. + * @return false to abort. This aborts + * out of the event loop should the listener return false */ - private boolean throwRecordEvent(Record record) - { - boolean result = true; - Iterator i = listeners.iterator(); - - while (i.hasNext()) { - result = ((ERFListener) i.next()).processRecord(record); - if (abortable == true && result == false) { - break; - } - } - return result; + private boolean processRecord(Record record) { + if (!isSidIncluded(record.getSid())) { + return true; + } + return _listener.processRecord(record); } /** @@ -264,220 +83,39 @@ public class EventRecordFactory * @exception RecordFormatException on error processing the * InputStream */ - public void processRecords(InputStream in) - throws RecordFormatException - { - Record last_record = null; + public void processRecords(InputStream in) throws RecordFormatException { + Record last_record = null; RecordInputStream recStream = new RecordInputStream(in); while (recStream.hasNextRecord()) { - recStream.nextRecord(); - Record[] recs = createRecord(recStream); // handle MulRK records - if (recs.length > 1) - { - for (int k = 0; k < recs.length; k++) - { - if ( last_record != null ) { - 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. + recStream.nextRecord(); + Record[] recs = RecordFactory.createRecord(recStream); // handle MulRK records + if (recs.length > 1) { + for (int k = 0; k < recs.length; k++) { + if ( last_record != null ) { + if (!processRecord(last_record)) { + return; + } } - 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 (!processRecord(last_record)) { + return; } } - - - if (last_record != null) { - throwRecordEvent(last_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"); + last_record = record; + } } - 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; - } - } + if (last_record != null) { + processRecord(last_record); } - return result; - } -} + } +} \ No newline at end of file diff --git a/src/java/org/apache/poi/hssf/eventmodel/ModelFactory.java b/src/java/org/apache/poi/hssf/eventmodel/ModelFactory.java index ade4532042..d75ca6c2e6 100644 --- a/src/java/org/apache/poi/hssf/eventmodel/ModelFactory.java +++ b/src/java/org/apache/poi/hssf/eventmodel/ModelFactory.java @@ -65,8 +65,7 @@ public class ModelFactory implements ERFListener * Start processing the Workbook stream into Model events. */ public void run(InputStream stream) { - EventRecordFactory factory = new EventRecordFactory(true); - factory.registerListener(this,null); + EventRecordFactory factory = new EventRecordFactory(this,null); lastEOF = true; factory.processRecords(stream); } diff --git a/src/java/org/apache/poi/hssf/extractor/ExcelExtractor.java b/src/java/org/apache/poi/hssf/extractor/ExcelExtractor.java index 75a73c654d..efa75e0d52 100644 --- a/src/java/org/apache/poi/hssf/extractor/ExcelExtractor.java +++ b/src/java/org/apache/poi/hssf/extractor/ExcelExtractor.java @@ -19,8 +19,11 @@ package org.apache.poi.hssf.extractor; import java.io.IOException; 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.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.HSSFRow; 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 lastRow = sheet.getLastRowNum(); for(int j=firstRow;j<=lastRow;j++) { @@ -154,7 +164,36 @@ public class ExcelExtractor extends POIOLE2TextExtractor { // Finish off the row text.append("\n"); } + + // Finally Feader text, if there is any + if(sheet.getFooter() != null) { + text.append( + extractHeaderFooter(sheet.getFooter()) + ); + } + } + + 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(); } diff --git a/src/java/org/apache/poi/hssf/record/RecordFactory.java b/src/java/org/apache/poi/hssf/record/RecordFactory.java index aaee94991d..6f03c2c11c 100644 --- a/src/java/org/apache/poi/hssf/record/RecordFactory.java +++ b/src/java/org/apache/poi/hssf/record/RecordFactory.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,294 +14,359 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record; import java.io.InputStream; 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

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

* - * @deprecated use {@link org.apache.poi.hssf.eventmodel.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) */ +public final class RecordFactory { + private static final int NUM_RECORDS = 512; -public class RecordFactory -{ - private static int NUM_RECORDS = 10000; - 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, - 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, - WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class, - NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class, - FileSharingRecord.class, ChartTitleFormatRecord.class, - DVRecord.class, DVALRecord.class, UncalcedRecord.class, - ChartRecord.class, LegendRecord.class, ChartTitleFormatRecord.class, - SeriesRecord.class, SeriesTextRecord.class, - HyperlinkRecord.class, - ExternalNameRecord.class, // TODO - same changes in non-@deprecated version of this class - SupBookRecord.class, - CRNCountRecord.class, - CRNRecord.class, - CFHeaderRecord.class, - CFRuleRecord.class, - TableRecord.class - }; - } - private static Map recordsMap = recordsToMap(records); - - /** - * changes the default capacity (10000) to handle larger files - */ + private static final Class[] CONSTRUCTOR_ARGS = { RecordInputStream.class, }; - public static void setCapacity(int capacity) - { - NUM_RECORDS = capacity; - } + /** + * contains the classes for all the records we want to parse.
+ * Note - this most but not *every* subclass of Record. + */ + private static final Class[] records = { + BackupRecord.class, + BlankRecord.class, + BOFRecord.class, + BookBoolRecord.class, + BoolErrRecord.class, + BottomMarginRecord.class, + BoundSheetRecord.class, + CalcCountRecord.class, + CalcModeRecord.class, + CFHeaderRecord.class, + CFRuleRecord.class, + ChartRecord.class, + ChartTitleFormatRecord.class, + CodepageRecord.class, + ColumnInfoRecord.class, + ContinueRecord.class, + CountryRecord.class, + CRNCountRecord.class, + CRNRecord.class, + DateWindow1904Record.class, + DBCellRecord.class, + DefaultColWidthRecord.class, + DefaultRowHeightRecord.class, + DeltaRecord.class, + DimensionsRecord.class, + DrawingGroupRecord.class, + DrawingRecord.class, + DrawingSelectionRecord.class, + DSFRecord.class, + DVALRecord.class, + DVRecord.class, + EOFRecord.class, + ExtendedFormatRecord.class, + ExternalNameRecord.class, + ExternSheetRecord.class, + ExtSSTRecord.class, + FilePassRecord.class, + FileSharingRecord.class, + FnGroupCountRecord.class, + FontRecord.class, + FooterRecord.class, + FormatRecord.class, + FormulaRecord.class, + GridsetRecord.class, + 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, + }; + + /** + * cache of the recordsToMap(); + */ + private static Map recordsMap = recordsToMap(records); - /** - * 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 - */ + private static short[] _allKnownRecordSIDs; + + /** + * 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) { + Constructor constructor = (Constructor) recordsMap.get(new Short(in.getSid())); - public static List createRecords(InputStream in) - throws RecordFormatException - { - ArrayList records = new ArrayList(NUM_RECORDS); + if (constructor == null) { + return new Record[] { new UnknownRecord(in), }; + } + + Record retval; - RecordInputStream recStream = new RecordInputStream(in); - DrawingRecord lastDrawingRecord = new DrawingRecord( ); - Record lastRecord = null; - while (recStream.hasNextRecord()) { - recStream.nextRecord(); - if (recStream.getSid() != 0) - { - Record[] recs = createRecord(recStream); // handle MulRK records + try { + retval = ( Record ) constructor.newInstance(new Object[] { in }); + } catch (InvocationTargetException e) { + throw new RecordFormatException("Unable to construct record instance" , e.getTargetException()); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + + if (retval instanceof RKRecord) { + // RK record is a slightly smaller alternative to NumberRecord + // POI likes NumberRecord better + RKRecord rk = ( RKRecord ) retval; + NumberRecord num = new NumberRecord(); - if (recs.length > 1) - { - for (int k = 0; k < recs.length; k++) - { - records.add( - recs[ k ]); // these will be number records - } - } - else - { - Record record = recs[ 0 ]; + 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; - 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 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); - } - } - } - } - } + Record[] mulRecs = new Record[ mrk.getNumColumns() ]; + for (int k = 0; k < mrk.getNumColumns(); k++) { + NumberRecord nr = new NumberRecord(); - return records; - } + nr.setColumn(( short ) (k + mrk.getFirstColumn())); + nr.setRow(mrk.getRow()); + nr.setXFIndex(mrk.getXFAt(k)); + nr.setValue(mrk.getRKNumberAt(k)); + mulRecs[ k ] = nr; + } + return mulRecs; + } + if (retval instanceof MulBlankRecord) { + MulBlankRecord mb = ( MulBlankRecord ) retval; - public static Record [] createRecord(RecordInputStream in) - { - Record retval; - Record[] realretval = null; + Record[] mulRecs = new Record[ mb.getNumColumns() ]; + for (int k = 0; k < mb.getNumColumns(); k++) { + BlankRecord br = new BlankRecord(); - try - { - Constructor constructor = - ( Constructor ) recordsMap.get(new Short(in.getSid())); + br.setColumn(( short ) (k + mb.getFirstColumn())); + br.setRow(mb.getRow()); + br.setXFIndex(mb.getXFAt(k)); + mulRecs[ k ] = br; + } + return mulRecs; + } + return new Record[] { retval, }; + } - 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(); + /** + * @return an array of all the SIDS for all known records + */ + public static short[] getAllKnownRecordSIDs() { + if (_allKnownRecordSIDs == null) { + short[] results = new short[ recordsMap.size() ]; + int i = 0; - 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; + for (Iterator iterator = recordsMap.keySet().iterator(); iterator.hasNext(); ) { + Short sid = (Short) iterator.next(); - realretval = new Record[ mrk.getNumColumns() ]; - for (int k = 0; k < mrk.getNumColumns(); k++) - { - NumberRecord nr = new NumberRecord(); + results[i++] = sid.shortValue(); + } + Arrays.sort(results); + _allKnownRecordSIDs = results; + } - 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; + return (short[]) _allKnownRecordSIDs.clone(); + } - realretval = new Record[ mb.getNumColumns() ]; - for (int k = 0; k < mb.getNumColumns(); k++) - { - BlankRecord br = new BlankRecord(); + /** + * 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(); + Set uniqueRecClasses = new HashSet(records.length * 3 / 2); - 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; - } + for (int i = 0; i < records.length; i++) { - public static short [] getAllKnownRecordSIDs() - { - short[] results = new short[ recordsMap.size() ]; - int i = 0; + Class recClass = records[ i ]; + if(!Record.class.isAssignableFrom(recClass)) { + 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() + ")"); + } + + short sid; + Constructor constructor; + try { + 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 (Iterator iterator = recordsMap.keySet().iterator(); - iterator.hasNext(); ) - { - Short sid = ( Short ) iterator.next(); + /** + * 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 { - results[ i++ ] = sid.shortValue(); - } - return results; - } + List records = new ArrayList(NUM_RECORDS); - private static Map recordsToMap(Class [] records) - { - Map result = new HashMap(); - Constructor constructor; + RecordInputStream recStream = new RecordInputStream(in); + DrawingRecord lastDrawingRecord = new DrawingRecord( ); + Record lastRecord = null; + while (recStream.hasNextRecord()) { + recStream.nextRecord(); + if (recStream.getSid() != 0) { + Record[] recs = createRecord(recStream); // handle MulRK records - for (int i = 0; i < records.length; i++) - { - Class record; - short sid; + if (recs.length > 1) { + for (int k = 0; k < recs.length; k++) { + records.add(recs[ k ]); // these will be number records + } + } else { + Record record = recs[ 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", illegalArgumentException); - } - result.put(new Short(sid), constructor); - } - 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; + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFooter.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFooter.java index 4e62e8b474..d85f1b60e3 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFFooter.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFFooter.java @@ -33,8 +33,7 @@ import org.apache.poi.ss.usermodel.Footer; *

* @author Shawn Laubach (slaubach at apache dot org) */ -public class HSSFFooter implements Footer { - +public class HSSFFooter implements Footer, HeaderFooter { FooterRecord footerRecord; String left; String center; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFHeader.java b/src/java/org/apache/poi/hssf/usermodel/HSSFHeader.java index e1cd0b0091..535b70efa1 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFHeader.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFHeader.java @@ -33,7 +33,7 @@ import org.apache.poi.ss.usermodel.Header; * * @author Shawn Laubach (slaubach at apache dot org) */ -public class HSSFHeader implements Header +public class HSSFHeader implements Header, HeaderFooter { HeaderRecord headerRecord; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index d5b801c5c1..5422776fc1 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -34,7 +34,6 @@ import org.apache.poi.ddf.EscherBSERecord; import org.apache.poi.ddf.EscherBitmapBlip; import org.apache.poi.ddf.EscherBlipRecord; 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.Workbook; 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. InputStream stream = directory.createDocumentInputStream(workbookName); - EventRecordFactory factory = new EventRecordFactory(); - List records = RecordFactory.createRecords(stream); workbook = Workbook.createWorkbook(records); diff --git a/src/java/org/apache/poi/hssf/usermodel/HeaderFooter.java b/src/java/org/apache/poi/hssf/usermodel/HeaderFooter.java new file mode 100644 index 0000000000..44dfd79567 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HeaderFooter.java @@ -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 { +} diff --git a/src/java/org/apache/poi/ss/usermodel/HeaderFooter.java b/src/java/org/apache/poi/ss/usermodel/HeaderFooter.java new file mode 100644 index 0000000000..c58f929e43 --- /dev/null +++ b/src/java/org/apache/poi/ss/usermodel/HeaderFooter.java @@ -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 ); +} diff --git a/src/testcases/org/apache/poi/hssf/data/45538_classic_Footer.xls b/src/testcases/org/apache/poi/hssf/data/45538_classic_Footer.xls new file mode 100644 index 0000000000..2ab1d241ca Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/45538_classic_Footer.xls differ diff --git a/src/testcases/org/apache/poi/hssf/data/45538_classic_Header.xls b/src/testcases/org/apache/poi/hssf/data/45538_classic_Header.xls new file mode 100644 index 0000000000..fe67b84f85 Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/45538_classic_Header.xls differ diff --git a/src/testcases/org/apache/poi/hssf/data/45538_form_Footer.xls b/src/testcases/org/apache/poi/hssf/data/45538_form_Footer.xls new file mode 100644 index 0000000000..6d1731d6a8 Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/45538_form_Footer.xls differ diff --git a/src/testcases/org/apache/poi/hssf/data/45538_form_Header.xls b/src/testcases/org/apache/poi/hssf/data/45538_form_Header.xls new file mode 100644 index 0000000000..c2a28219ca Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/45538_form_Header.xls differ diff --git a/src/testcases/org/apache/poi/hssf/eventmodel/TestEventRecordFactory.java b/src/testcases/org/apache/poi/hssf/eventmodel/TestEventRecordFactory.java index 0449795b5d..c4a5ec84b3 100644 --- a/src/testcases/org/apache/poi/hssf/eventmodel/TestEventRecordFactory.java +++ b/src/testcases/org/apache/poi/hssf/eventmodel/TestEventRecordFactory.java @@ -18,16 +18,16 @@ package org.apache.poi.hssf.eventmodel; import java.io.ByteArrayInputStream; -import java.util.Iterator; + +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.UnknownRecord; -import org.apache.poi.hssf.record.ContinueRecord; +import org.apache.poi.hssf.record.RecordFactory; import org.apache.poi.hssf.record.TestcaseRecordInputStream; - -import junit.framework.TestCase; +import org.apache.poi.hssf.record.UnknownRecord; /** * enclosing_type describe the purpose here @@ -35,63 +35,7 @@ import junit.framework.TestCase; * @author Andrew C. Oliver acoliver@apache.org * @author Csaba Nagy (ncsaba at yahoo dot com) */ -public 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(); - } +public final class TestEventRecordFactory extends TestCase { /** * tests that the records can be processed and properly return @@ -99,17 +43,17 @@ public class TestEventRecordFactory extends TestCase */ public void testProcessRecords() { - byte[] bytes = null; - int offset = 0; - //boolean wascalled = false; - factory.registerListener(new ERFListener() { + final boolean[] wascalled = { false, }; // hack to pass boolean by ref into inner class + + ERFListener listener = new ERFListener() { public boolean processRecord(Record rec) { - wascalled = true; + wascalled[0] = true; assertTrue("must be BOFRecord got SID="+rec.getSid(), (rec.getSid() == BOFRecord.sid)); return true; } - }, new short[] {BOFRecord.sid}); + }; + EventRecordFactory factory = new EventRecordFactory(listener, new short[] {BOFRecord.sid}); BOFRecord bof = new BOFRecord(); bof.setBuild((short)0); @@ -120,23 +64,20 @@ public class TestEventRecordFactory extends TestCase bof.setHistoryBitMask(BOFRecord.HISTORY_MASK); 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 = eof.serialize(offset,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 * constructed record in the simple case. */ - public void testCreateRecord() - { - byte[] bytes = null; - byte[] nbytes = null; - Record[] records = null; + public void testCreateRecord() { BOFRecord bof = new BOFRecord(); bof.setBuild((short)0); bof.setBuildYear((short)1999); @@ -145,11 +86,11 @@ public class TestEventRecordFactory extends TestCase bof.setVersion((short)0x06); bof.setHistoryBitMask(BOFRecord.HISTORY_MASK); - bytes = bof.serialize(); - nbytes = new byte[bytes.length - 4]; + byte[] bytes = bof.serialize(); + byte[] nbytes = new byte[bytes.length - 4]; 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 is the same", compareRec(bof,records[0])); @@ -162,24 +103,19 @@ public class TestEventRecordFactory extends TestCase * @param second the second record to compare * @return boolean whether or not the record where equal */ - private boolean compareRec(Record first, Record second) - { - boolean retval = true; + private static boolean compareRec(Record first, Record second) { byte[] rec1 = first.serialize(); byte[] rec2 = second.serialize(); - if (rec1.length == rec2.length) { - for (int k=0; k * */ - public void testContinuedUnknownRecord() - { - final byte[] data = new byte[] - { + public void testContinuedUnknownRecord() { + final byte[] data = { 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, 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[] offset = { 0 }; - factory.registerListener( - new ERFListener() { + ERFListener listener = new ERFListener() { private String[] expectedRecordTypes = { UnknownRecord.class.getName(), ContinueRecord.class.getName(), @@ -238,14 +171,11 @@ public class TestEventRecordFactory extends TestCase 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)); assertEquals("nr. of processed records", 3, recCnt[0]); assertEquals("nr. of processed bytes", data.length, offset[0]); } - - } diff --git a/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java b/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java index 9bb137ff69..0913ab7250 100644 --- a/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java +++ b/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java @@ -255,4 +255,20 @@ public final class TestExcelExtractor extends TestCase { ex.getText()); 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