From: Nick Burch Date: Thu, 28 Aug 2008 13:26:12 +0000 (+0000) Subject: Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-63924... X-Git-Tag: REL_3_5_BETA3~39 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=9958668438ee7553982d44f9f9f7f07b89c51f70;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-682507,682509,682512-682517,682519-682532,682534-682619,682622-682777,682779-682998,683000-683019,683021-683022,683024-683080,683082-683092,683094-683095,683097-683127,683129-683131,683133-683166,683168-683698,683700-683705,683707-683757,683759-683787,683789-683870,683872-683879,683881-683900,683902-684066,684068-684074,684076-684222,684224-684254,684257-684281,684283-684286,684288-684292,684294-684298,684300-684301,684303-684308,684310-684317,684320,684323-684335,684337-684348,684350-684354,684356-684361,684363-684369,684371-684453,684455-684883,684885-684937,684940-684958,684960-684970,684972-684985,684987-685053,685055-685063,685065-685259,685261-685262,685264-685266,685268-685282,685285-686035,686037-686045,686047-686052,686054-686206,686208-686215,686217-686277,686279-686289,686291-686620,686622-686623,686626-686627,686629-686639,686641-686843,686845-686976,686978-687402,687404-687422,687424-687428,687430-687442,687444-688425,688427-688641,688643-688649,688651-688654,688656-688824,688826-688909,688911-689779 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r689544 | josh | 2008-08-27 19:13:46 +0100 (Wed, 27 Aug 2008) | 1 line Fixed size of TblPtg ........ r689559 | josh | 2008-08-27 19:51:03 +0100 (Wed, 27 Aug 2008) | 1 line added names of known but uniterpreted BIFF records ........ r689636 | josh | 2008-08-27 22:54:20 +0100 (Wed, 27 Aug 2008) | 1 line should have been submitted with c689559 ........ r689704 | josh | 2008-08-28 03:54:47 +0100 (Thu, 28 Aug 2008) | 1 line Fix for bug 45698 - allow LinkTable to read EXTERNSHEET records ........ r689716 | josh | 2008-08-28 05:27:41 +0100 (Thu, 28 Aug 2008) | 1 line Fix for bug 45699 - RowRecordsAggregate needs to tolerate MergeCellsRecords between row/cell records ........ r689719 | josh | 2008-08-28 06:13:31 +0100 (Thu, 28 Aug 2008) | 1 line Should have been submitted with c689716 (bug 45699) ........ r689721 | josh | 2008-08-28 06:25:24 +0100 (Thu, 28 Aug 2008) | 1 line Removed unused methods from CellValueRecordInterface. Converted FormulaRecordAggregate to RecordAggregate subclass ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@689811 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index bfc44a828f..3d49ffca0c 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -64,6 +64,8 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + 45699 - Fix RowRecordsAggregate to tolerate intervening MERGEDCELLS records + 45698 - Fix LinkTable to tolerate multiple EXTERNSHEET records 45682 - Fix for cloning of CFRecordsAggregate Initial support for evaluating external add-in functions like YEARFRAC 45672 - Fix for MissingRecordAwareHSSFListener to prevent multiple LastCellOfRowDummyRecords when shared formulas are present diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 5ed54a515b..05475a52ee 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -61,6 +61,8 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + 45699 - Fix RowRecordsAggregate to tolerate intervening MERGEDCELLS records + 45698 - Fix LinkTable to tolerate multiple EXTERNSHEET records 45682 - Fix for cloning of CFRecordsAggregate Initial support for evaluating external add-in functions like YEARFRAC 45672 - Fix for MissingRecordAwareHSSFListener to prevent multiple LastCellOfRowDummyRecords when shared formulas are present diff --git a/src/java/org/apache/poi/hssf/model/LinkTable.java b/src/java/org/apache/poi/hssf/model/LinkTable.java index 9d1707558d..a64f134fed 100755 --- a/src/java/org/apache/poi/hssf/model/LinkTable.java +++ b/src/java/org/apache/poi/hssf/model/LinkTable.java @@ -159,8 +159,7 @@ final class LinkTable { if (_externalBookBlocks.length > 0) { // If any ExternalBookBlock present, there is always 1 of ExternSheetRecord - Record next = rs.getNext(); - _externSheetRecord = (ExternSheetRecord) next; + _externSheetRecord = readExtSheetRecord(rs); } else { _externSheetRecord = null; } @@ -176,6 +175,28 @@ final class LinkTable { _workbookRecordList.getRecords().addAll(inputList.subList(startIndex, startIndex + _recordCount)); } + private static ExternSheetRecord readExtSheetRecord(RecordStream rs) { + List temp = new ArrayList(2); + while(rs.peekNextClass() == ExternSheetRecord.class) { + temp.add(rs.getNext()); + } + + int nItems = temp.size(); + if (nItems < 1) { + throw new RuntimeException("Expected an EXTERNSHEET record but got (" + + rs.peekNextClass().getName() + ")"); + } + if (nItems == 1) { + // this is the normal case. There should be just one ExternSheetRecord + return (ExternSheetRecord) temp.get(0); + } + // Some apps generate multiple ExternSheetRecords (see bug 45698). + // It seems like the best thing to do might be to combine these into one + ExternSheetRecord[] esrs = new ExternSheetRecord[nItems]; + temp.toArray(esrs); + return ExternSheetRecord.combine(esrs); + } + public LinkTable(short numberOfSheets, WorkbookRecordList workbookRecordList) { _workbookRecordList = workbookRecordList; _definedNames = new ArrayList(); diff --git a/src/java/org/apache/poi/hssf/model/RecordOrderer.java b/src/java/org/apache/poi/hssf/model/RecordOrderer.java index 887497a911..291bd07715 100644 --- a/src/java/org/apache/poi/hssf/model/RecordOrderer.java +++ b/src/java/org/apache/poi/hssf/model/RecordOrderer.java @@ -22,16 +22,20 @@ import java.util.List; import org.apache.poi.hssf.record.BOFRecord; import org.apache.poi.hssf.record.CalcCountRecord; import org.apache.poi.hssf.record.CalcModeRecord; +import org.apache.poi.hssf.record.DVALRecord; import org.apache.poi.hssf.record.DateWindow1904Record; 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.DrawingRecord; +import org.apache.poi.hssf.record.DrawingSelectionRecord; import org.apache.poi.hssf.record.EOFRecord; import org.apache.poi.hssf.record.GridsetRecord; import org.apache.poi.hssf.record.GutsRecord; import org.apache.poi.hssf.record.HyperlinkRecord; import org.apache.poi.hssf.record.IndexRecord; import org.apache.poi.hssf.record.IterationRecord; +import org.apache.poi.hssf.record.ObjRecord; import org.apache.poi.hssf.record.PaneRecord; import org.apache.poi.hssf.record.PrecisionRecord; import org.apache.poi.hssf.record.PrintGridlinesRecord; @@ -42,7 +46,10 @@ import org.apache.poi.hssf.record.RefModeRecord; import org.apache.poi.hssf.record.SCLRecord; import org.apache.poi.hssf.record.SaveRecalcRecord; import org.apache.poi.hssf.record.SelectionRecord; +import org.apache.poi.hssf.record.TextObjectRecord; import org.apache.poi.hssf.record.UncalcedRecord; +import org.apache.poi.hssf.record.UnknownRecord; +import org.apache.poi.hssf.record.WindowOneRecord; import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.hssf.record.aggregates.ConditionalFormattingTable; import org.apache.poi.hssf.record.aggregates.DataValidityTable; @@ -57,8 +64,6 @@ import org.apache.poi.hssf.record.aggregates.PageSettingsBlock; * @author Josh Micich */ final class RecordOrderer { - // TODO - add UninterpretedRecord as base class for many of these - // unimplemented sids // TODO - simplify logic using a generalised record ordering @@ -126,7 +131,7 @@ final class RecordOrderer { case PrintGridlinesRecord.sid: case GridsetRecord.sid: case DefaultRowHeightRecord.sid: - case 0x0081: // SHEETPR + case UnknownRecord.SHEETPR_0081: return true; // next is the 'Worksheet Protection Block' } @@ -149,10 +154,10 @@ final class RecordOrderer { case SCLRecord.sid: case PaneRecord.sid: case SelectionRecord.sid: - case 0x0099:// STANDARDWIDTH + case UnknownRecord.STANDARDWIDTH_0099: // MergedCellsTable usually here - case 0x015f:// LABELRANGES - case 0x00ef:// PHONETICPR + case UnknownRecord.LABELRANGES_015F: + case UnknownRecord.PHONETICPR_00EF: return i + 1; } } @@ -162,13 +167,20 @@ final class RecordOrderer { private static int findInsertPosForNewMergedRecordTable(List records) { for (int i = records.size() - 2; i >= 0; i--) { // -2 to skip EOF record Object rb = records.get(i); + if (!(rb instanceof Record)) { + // DataValidityTable, ConditionalFormattingTable, + // even PageSettingsBlock (which doesn't normally appear after 'View Settings') + continue; + } Record rec = (Record) rb; switch (rec.getSid()) { + // 'View Settings' (4 records) case WindowTwoRecord.sid: case SCLRecord.sid: case PaneRecord.sid: case SelectionRecord.sid: - case 0x0099:// STANDARDWIDTH + + case UnknownRecord.STANDARDWIDTH_0099: return i + 1; } } @@ -229,16 +241,16 @@ final class RecordOrderer { short sid = ((Record)rb).getSid(); switch(sid) { case WindowTwoRecord.sid: - case 0x00A0: // SCL + case UnknownRecord.SCL_00A0: case PaneRecord.sid: case SelectionRecord.sid: - case 0x0099: // STANDARDWIDTH + case UnknownRecord.STANDARDWIDTH_0099: // MergedCellsTable - case 0x015F: // LABELRANGES - case 0x00EF: // PHONETICPR + case UnknownRecord.LABELRANGES_015F: + case UnknownRecord.PHONETICPR_00EF: // ConditionalFormattingTable case HyperlinkRecord.sid: - case 0x0800: // QUICKTIP + case UnknownRecord.QUICKTIP_0800: return true; } return false; @@ -246,9 +258,9 @@ final class RecordOrderer { private static boolean isDVTSubsequentRecord(short sid) { switch(sid) { - case 0x0862: // SHEETLAYOUT - case 0x0867: // SHEETPROTECTION - case 0x0868: // RANGEPROTECTION + case UnknownRecord.SHEETEXT_0862: + case UnknownRecord.SHEETPROTECTION_0867: + case UnknownRecord.RANGEPROTECTION_0868: case EOFRecord.sid: return true; } @@ -307,4 +319,29 @@ final class RecordOrderer { } return false; } + /** + * @return true if the specified record ID terminates a sequence of Row block records + * It is assumed that at least one row or cell value record has been found prior to the current + * record + */ + public static boolean isEndOfRowBlock(short sid) { + switch(sid) { + case DrawingRecord.sid: + case DrawingSelectionRecord.sid: + case ObjRecord.sid: + case TextObjectRecord.sid: + + case WindowOneRecord.sid: + // should really be part of workbook stream, but some apps seem to put this before WINDOW2 + case WindowTwoRecord.sid: + return true; + + case DVALRecord.sid: + return true; + case EOFRecord.sid: + // WINDOW2 should always be present, so shouldn't have got this far + throw new RuntimeException("Found EOFRecord before WindowTwoRecord was encountered"); + } + return PageSettingsBlock.isComponentRecord(sid); + } } diff --git a/src/java/org/apache/poi/hssf/model/Sheet.java b/src/java/org/apache/poi/hssf/model/Sheet.java index 336b1dbd46..b5db343d8e 100644 --- a/src/java/org/apache/poi/hssf/model/Sheet.java +++ b/src/java/org/apache/poi/hssf/model/Sheet.java @@ -122,7 +122,8 @@ public final class Sheet implements Model { protected WindowTwoRecord windowTwo = null; protected SelectionRecord selection = null; - private MergedCellsTable _mergedCellsTable; + /** java object always present, but if empty no BIFF records are written */ + private final MergedCellsTable _mergedCellsTable; /** always present in this POI object, not always written to Excel file */ /*package*/ColumnInfoRecordsAggregate _columnInfos; /** the DimensionsRecord is always present */ @@ -146,8 +147,8 @@ public final class Sheet implements Model { * Creates new Sheet with no initialization --useless at this point * @see #createSheet(List,int,int) */ - public Sheet() - { + public Sheet() { + _mergedCellsTable = new MergedCellsTable(); } /** @@ -158,7 +159,7 @@ public final class Sheet implements Model { * to the passed in records and references to those records held. This function * is normally called via Workbook. * - * @param recs array containing those records in the sheet in sequence (normally obtained from RecordFactory) + * @param inRecs array containing those records in the sheet in sequence (normally obtained from RecordFactory) * @param sheetnum integer specifying the sheet's number (0,1 or 2 in this release) * @param offset of the sheet's BOF record * @@ -167,19 +168,19 @@ public final class Sheet implements Model { * @see org.apache.poi.hssf.model.Workbook * @see org.apache.poi.hssf.record.Record */ - public static Sheet createSheet(List recs, int sheetnum, int offset) + public static Sheet createSheet(List inRecs, int sheetnum, int offset) { if (log.check( POILogger.DEBUG )) log.logFormatted(POILogger.DEBUG, "Sheet createSheet (existing file) with %", - new Integer(recs.size())); + new Integer(inRecs.size())); Sheet retval = new Sheet(); - ArrayList records = new ArrayList(recs.size() / 5); - boolean isfirstcell = true; - int bofEofNestingLevel = 0; + ArrayList records = new ArrayList(inRecs.size() / 5); + // TODO - take chart streams off into separate java objects + int bofEofNestingLevel = 0; // nesting level can only get to 2 (when charts are present) - for (int k = offset; k < recs.size(); k++) { - Record rec = ( Record ) recs.get(k); + for (int k = offset; k < inRecs.size(); k++) { + Record rec = ( Record ) inRecs.get(k); if ( rec.getSid() == DBCellRecord.sid ) { continue; } @@ -193,7 +194,7 @@ public final class Sheet implements Model { } if ( rec.getSid() == CFHeaderRecord.sid ) { - RecordStream rs = new RecordStream(recs, k); + RecordStream rs = new RecordStream(inRecs, k); retval.condFormatting = new ConditionalFormattingTable(rs); k += rs.getCountRead()-1; records.add(retval.condFormatting); @@ -201,43 +202,34 @@ public final class Sheet implements Model { } if (rec.getSid() == ColumnInfoRecord.sid) { - RecordStream rs = new RecordStream(recs, k); + RecordStream rs = new RecordStream(inRecs, k); retval._columnInfos = new ColumnInfoRecordsAggregate(rs); k += rs.getCountRead()-1; records.add(retval._columnInfos); continue; } if ( rec.getSid() == DVALRecord.sid) { - RecordStream rs = new RecordStream(recs, k); + RecordStream rs = new RecordStream(inRecs, k); retval._dataValidityTable = new DataValidityTable(rs); k += rs.getCountRead() - 1; // TODO - convert this method result to be zero based records.add(retval._dataValidityTable); continue; } // TODO construct RowRecordsAggregate from RecordStream - if ( rec.getSid() == RowRecord.sid ) { - RowRecord row = (RowRecord)rec; - if (retval._rowsAggregate == null) { - retval._rowsAggregate = new RowRecordsAggregate(); - records.add(retval._rowsAggregate); //only add the aggregate once + if ((rec.getSid() == RowRecord.sid || rec.isValue()) && bofEofNestingLevel == 1 ) { + //only add the aggregate once + if (retval._rowsAggregate != null) { + throw new RuntimeException("row/cell records found in the wrong place"); } - retval._rowsAggregate.insertRow(row); + int lastRowCellRec = findEndOfRowBlock(inRecs, k, retval._mergedCellsTable); + retval._rowsAggregate = new RowRecordsAggregate(inRecs, k, lastRowCellRec); + records.add(retval._rowsAggregate); //only add the aggregate once + k = lastRowCellRec -1; continue; } - if ( rec.isValue() && bofEofNestingLevel == 1 ) { - if (isfirstcell) { - isfirstcell = false; - if (retval._rowsAggregate == null) { - retval._rowsAggregate = new RowRecordsAggregate(); - records.add(retval._rowsAggregate); //only add the aggregate once - } - retval._rowsAggregate.constructCellValues( k, recs ); - } - continue; - } if (PageSettingsBlock.isComponentRecord(rec.getSid())) { - RecordStream rs = new RecordStream(recs, k); + RecordStream rs = new RecordStream(inRecs, k); PageSettingsBlock psb = new PageSettingsBlock(rs); if (bofEofNestingLevel == 1) { if (retval._psBlock == null) { @@ -253,9 +245,10 @@ public final class Sheet implements Model { } if (rec.getSid() == MergeCellsRecord.sid) { - RecordStream rs = new RecordStream(recs, k); - retval._mergedCellsTable = new MergedCellsTable(rs); - records.add(retval._mergedCellsTable); + // when the MergedCellsTable is found in the right place, we expect those records to be contiguous + RecordStream rs = new RecordStream(inRecs, k); + retval._mergedCellsTable.read(rs); + k += rs.getCountRead()-1; continue; } @@ -337,6 +330,11 @@ public final class Sheet implements Model { if (retval._dimensions == null) { throw new RuntimeException("DimensionsRecord was not found"); } + if (retval.windowTwo == null) { + throw new RuntimeException("WINDOW2 was not found"); + } + // put merged cells table in the right place (regardless of where the first MergedCellsRecord was found */ + RecordOrderer.addNewSheetRecord(records, retval._mergedCellsTable); retval.records = records; retval.checkRows(); if (log.check( POILogger.DEBUG )) @@ -344,6 +342,26 @@ public final class Sheet implements Model { return retval; } + /** + * Also collects any rogue MergeCellRecords + * @return the index one after the last row/cell record + */ + private static int findEndOfRowBlock(List recs, int startIx, MergedCellsTable mergedCellsTable) { + for(int i=startIx; i i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() > i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isAfter(CellValueRecordInterface i) - { - if (this.getRow() < i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() < i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isEqual(CellValueRecordInterface i) - { - return ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())); - } - public boolean isInValueSection() { return true; @@ -254,50 +205,6 @@ public class BlankRecord return 10; } - public int compareTo(Object obj) - { - CellValueRecordInterface loc = ( CellValueRecordInterface ) obj; - - if ((this.getRow() == loc.getRow()) - && (this.getColumn() == loc.getColumn())) - { - return 0; - } - if (this.getRow() < loc.getRow()) - { - return -1; - } - if (this.getRow() > loc.getRow()) - { - return 1; - } - if (this.getColumn() < loc.getColumn()) - { - return -1; - } - if (this.getColumn() > loc.getColumn()) - { - return 1; - } - return -1; - } - - public boolean equals(Object obj) - { - if (!(obj instanceof CellValueRecordInterface)) - { - return false; - } - CellValueRecordInterface loc = ( CellValueRecordInterface ) obj; - - if ((this.getRow() == loc.getRow()) - && (this.getColumn() == loc.getColumn())) - { - return true; - } - return false; - } - public Object clone() { BlankRecord rec = new BlankRecord(); rec.field_1_row = field_1_row; diff --git a/src/java/org/apache/poi/hssf/record/BoolErrRecord.java b/src/java/org/apache/poi/hssf/record/BoolErrRecord.java index f2b9d928ab..43bdda900c 100644 --- a/src/java/org/apache/poi/hssf/record/BoolErrRecord.java +++ b/src/java/org/apache/poi/hssf/record/BoolErrRecord.java @@ -33,14 +33,9 @@ import org.apache.poi.util.LittleEndian; * @author Jason Height (jheight at chariot dot net dot au) * @version 2.0-pre */ - -public class BoolErrRecord - extends Record - implements CellValueRecordInterface, Comparable -{ +public final class BoolErrRecord extends Record implements CellValueRecordInterface { public final static short sid = 0x205; - //private short field_1_row; - private int field_1_row; + private int field_1_row; private short field_2_column; private short field_3_xf_index; private byte field_4_bBoolErr; @@ -273,50 +268,6 @@ public class BoolErrRecord return sid; } - public boolean isBefore(CellValueRecordInterface i) - { - if (this.getRow() > i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() > i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isAfter(CellValueRecordInterface i) - { - if (this.getRow() < i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() < i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isEqual(CellValueRecordInterface i) - { - return ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())); - } - public boolean isInValueSection() { return true; @@ -327,50 +278,6 @@ public class BoolErrRecord return true; } - public int compareTo(Object obj) - { - CellValueRecordInterface loc = ( CellValueRecordInterface ) obj; - - if ((this.getRow() == loc.getRow()) - && (this.getColumn() == loc.getColumn())) - { - return 0; - } - if (this.getRow() < loc.getRow()) - { - return -1; - } - if (this.getRow() > loc.getRow()) - { - return 1; - } - if (this.getColumn() < loc.getColumn()) - { - return -1; - } - if (this.getColumn() > loc.getColumn()) - { - return 1; - } - return -1; - } - - public boolean equals(Object obj) - { - if (!(obj instanceof CellValueRecordInterface)) - { - return false; - } - CellValueRecordInterface loc = ( CellValueRecordInterface ) obj; - - if ((this.getRow() == loc.getRow()) - && (this.getColumn() == loc.getColumn())) - { - return true; - } - return false; - } - public Object clone() { BoolErrRecord rec = new BoolErrRecord(); rec.field_1_row = field_1_row; diff --git a/src/java/org/apache/poi/hssf/record/CellValueRecordInterface.java b/src/java/org/apache/poi/hssf/record/CellValueRecordInterface.java index 1cc6b7ae79..4bbee79aec 100644 --- a/src/java/org/apache/poi/hssf/record/CellValueRecordInterface.java +++ b/src/java/org/apache/poi/hssf/record/CellValueRecordInterface.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,13 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - -/* - * CellValueRecordInterface.java - * - * Created on October 2, 2001, 8:27 PM - */ package org.apache.poi.hssf.record; /** @@ -36,73 +29,29 @@ package org.apache.poi.hssf.record; * @see org.apache.poi.hssf.record.Record * @see org.apache.poi.hssf.record.RecordFactory */ - -public interface CellValueRecordInterface -{ +public interface CellValueRecordInterface { /** - * get the row this cell occurs on - * - * @return the row + * @return the row this cell occurs on */ - - //public short getRow(); - public int getRow(); + int getRow(); /** - * get the column this cell defines within the row - * - * @return the column + * @return the column this cell defines within the row */ - - public short getColumn(); + short getColumn(); /** - * set the row this cell occurs on * @param row the row this cell occurs within */ - - //public void setRow(short row); - public void setRow(int row); + void setRow(int row); /** - * set the column this cell defines within the row - * * @param col the column this cell defines */ + void setColumn(short col); - public void setColumn(short col); - - public void setXFIndex(short xf); - - public short getXFIndex(); - - /** - * returns whether this cell is before the passed in cell - * - * @param i another cell interface record to compare - * @return true if the cells is before, or false if not - */ - - public boolean isBefore(CellValueRecordInterface i); - - /** - * returns whether this cell is after the passed in cell - * - * @param i record to compare - * @return true if the cell is after, false if not - */ - - public boolean isAfter(CellValueRecordInterface i); - - /** - * returns whether this cell represents the same cell (NOT VALUE) - * - * @param i record to compare - * @return true if the cells are the same cell (positionally), false if not. - */ - - public boolean isEqual(CellValueRecordInterface i); + void setXFIndex(short xf); - public Object clone(); + short getXFIndex(); } diff --git a/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java b/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java index 2b0744a91e..c4f00581b2 100644 --- a/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java +++ b/src/java/org/apache/poi/hssf/record/ExternSheetRecord.java @@ -29,212 +29,212 @@ import org.apache.poi.util.LittleEndian; * @author Libin Roman (Vista Portal LDT. Developer) */ public class ExternSheetRecord extends Record { - public final static short sid = 0x0017; - private List _list; - - private final class RefSubRecord { - public static final int ENCODED_SIZE = 6; + public final static short sid = 0x0017; + private List _list; + + private final class RefSubRecord { + public static final int ENCODED_SIZE = 6; - /** index to External Book Block (which starts with a EXTERNALBOOK record) */ - private int _extBookIndex; - private int _firstSheetIndex; // may be -1 (0xFFFF) - private int _lastSheetIndex; // may be -1 (0xFFFF) - - - /** a Constructor for making new sub record - */ - public RefSubRecord(int extBookIndex, int firstSheetIndex, int lastSheetIndex) { - _extBookIndex = extBookIndex; - _firstSheetIndex = firstSheetIndex; - _lastSheetIndex = lastSheetIndex; - } - - /** - * @param in the RecordInputstream to read the record from - */ - public RefSubRecord(RecordInputStream in) { - this(in.readShort(), in.readShort(), in.readShort()); - } - public int getExtBookIndex(){ - return _extBookIndex; - } - public int getFirstSheetIndex(){ - return _firstSheetIndex; - } - public int getLastSheetIndex(){ - return _lastSheetIndex; - } - - public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append("extBook=").append(_extBookIndex); - buffer.append(" firstSheet=").append(_firstSheetIndex); - buffer.append(" lastSheet=").append(_lastSheetIndex); - return buffer.toString(); - } - - /** - * called by the class that is responsible for writing this sucker. - * Subclasses should implement this so that their data is passed back in a - * byte array. - * - * @param offset to begin writing at - * @param data byte array containing instance data - * @return number of bytes written - */ - public void serialize(int offset, byte [] data) { - LittleEndian.putUShort(data, 0 + offset, _extBookIndex); - LittleEndian.putUShort(data, 2 + offset, _firstSheetIndex); - LittleEndian.putUShort(data, 4 + offset, _lastSheetIndex); - } - } - - - - public ExternSheetRecord() { - _list = new ArrayList(); - } - - /** - * Constructs a Extern Sheet record and sets its fields appropriately. - * @param in the RecordInputstream to read the record from - */ - - public ExternSheetRecord(RecordInputStream in) { - super(in); - } - - /** - * called by constructor, should throw runtime exception in the event of a - * record passed with a differing ID. - * - * @param id alleged id for this record - */ - protected void validateSid(short id) { - if (id != sid) { - throw new RecordFormatException("NOT An ExternSheet RECORD"); - } - } - - /** - * called by the constructor, should set class level fields. Should throw - * runtime exception for bad/icomplete data. - * - * @param in the RecordInputstream to read the record from - */ - protected void fillFields(RecordInputStream in) { - _list = new ArrayList(); - - int nItems = in.readShort(); - - for (int i = 0 ; i < nItems ; ++i) { - RefSubRecord rec = new RefSubRecord(in); - - _list.add( rec); - } - } - + /** index to External Book Block (which starts with a EXTERNALBOOK record) */ + private int _extBookIndex; + private int _firstSheetIndex; // may be -1 (0xFFFF) + private int _lastSheetIndex; // may be -1 (0xFFFF) + + + /** a Constructor for making new sub record + */ + public RefSubRecord(int extBookIndex, int firstSheetIndex, int lastSheetIndex) { + _extBookIndex = extBookIndex; + _firstSheetIndex = firstSheetIndex; + _lastSheetIndex = lastSheetIndex; + } + + /** + * @param in the RecordInputstream to read the record from + */ + public RefSubRecord(RecordInputStream in) { + this(in.readShort(), in.readShort(), in.readShort()); + } + public int getExtBookIndex(){ + return _extBookIndex; + } + public int getFirstSheetIndex(){ + return _firstSheetIndex; + } + public int getLastSheetIndex(){ + return _lastSheetIndex; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("extBook=").append(_extBookIndex); + buffer.append(" firstSheet=").append(_firstSheetIndex); + buffer.append(" lastSheet=").append(_lastSheetIndex); + return buffer.toString(); + } + + /** + * called by the class that is responsible for writing this sucker. + * Subclasses should implement this so that their data is passed back in a + * byte array. + * + * @param offset to begin writing at + * @param data byte array containing instance data + * @return number of bytes written + */ + public void serialize(int offset, byte [] data) { + LittleEndian.putUShort(data, 0 + offset, _extBookIndex); + LittleEndian.putUShort(data, 2 + offset, _firstSheetIndex); + LittleEndian.putUShort(data, 4 + offset, _lastSheetIndex); + } + } + + + + public ExternSheetRecord() { + _list = new ArrayList(); + } + + /** + * Constructs a Extern Sheet record and sets its fields appropriately. + * @param in the RecordInputstream to read the record from + */ + + public ExternSheetRecord(RecordInputStream in) { + super(in); + } + + /** + * called by constructor, should throw runtime exception in the event of a + * record passed with a differing ID. + * + * @param id alleged id for this record + */ + protected void validateSid(short id) { + if (id != sid) { + throw new RecordFormatException("NOT An ExternSheet RECORD"); + } + } + + /** + * called by the constructor, should set class level fields. Should throw + * runtime exception for bad/icomplete data. + * + * @param in the RecordInputstream to read the record from + */ + protected void fillFields(RecordInputStream in) { + _list = new ArrayList(); + + int nItems = in.readShort(); + + for (int i = 0 ; i < nItems ; ++i) { + RefSubRecord rec = new RefSubRecord(in); + + _list.add( rec); + } + } + - /** - * @return number of REF structures - */ - public int getNumOfRefs() { - return _list.size(); - } - - /** - * adds REF struct (ExternSheetSubRecord) - * @param rec REF struct - */ - public void addREFRecord(RefSubRecord rec) { - _list.add(rec); - } - - /** returns the number of REF Records, which is in model - * @return number of REF records - */ - public int getNumOfREFRecords() { - return _list.size(); - } - - - public String toString() { - StringBuffer sb = new StringBuffer(); - int nItems = _list.size(); - sb.append("[EXTERNSHEET]\n"); - sb.append(" numOfRefs = ").append(nItems).append("\n"); - for (int i=0; i < nItems; i++) { - sb.append("refrec #").append(i).append(": "); - sb.append(getRef(i).toString()); - sb.append('\n'); - } - sb.append("[/EXTERNSHEET]\n"); - - - return sb.toString(); - } - - - private int getDataSize() { - return 2 + _list.size() * RefSubRecord.ENCODED_SIZE; - } - - /** - * called by the class that is responsible for writing this sucker. - * Subclasses should implement this so that their data is passed back in a - * byte array. - * - * @param offset to begin writing at - * @param data byte array containing instance data - * @return number of bytes written - */ - public int serialize(int offset, byte [] data) { - int dataSize = getDataSize(); - - int nItems = _list.size(); + /** + * @return number of REF structures + */ + public int getNumOfRefs() { + return _list.size(); + } + + /** + * adds REF struct (ExternSheetSubRecord) + * @param rec REF struct + */ + public void addREFRecord(RefSubRecord rec) { + _list.add(rec); + } + + /** returns the number of REF Records, which is in model + * @return number of REF records + */ + public int getNumOfREFRecords() { + return _list.size(); + } + + + public String toString() { + StringBuffer sb = new StringBuffer(); + int nItems = _list.size(); + sb.append("[EXTERNSHEET]\n"); + sb.append(" numOfRefs = ").append(nItems).append("\n"); + for (int i=0; i < nItems; i++) { + sb.append("refrec #").append(i).append(": "); + sb.append(getRef(i).toString()); + sb.append('\n'); + } + sb.append("[/EXTERNSHEET]\n"); + + + return sb.toString(); + } + + + private int getDataSize() { + return 2 + _list.size() * RefSubRecord.ENCODED_SIZE; + } + + /** + * called by the class that is responsible for writing this sucker. + * Subclasses should implement this so that their data is passed back in a + * byte array. + * + * @param offset to begin writing at + * @param data byte array containing instance data + * @return number of bytes written + */ + public int serialize(int offset, byte [] data) { + int dataSize = getDataSize(); + + int nItems = _list.size(); - LittleEndian.putShort(data, 0 + offset, sid); + LittleEndian.putShort(data, 0 + offset, sid); LittleEndian.putUShort(data, 2 + offset, dataSize); - LittleEndian.putUShort(data, 4 + offset, nItems); - - int pos = 6 ; - - for (int i = 0; i < nItems; i++) { - getRef(i).serialize(offset + pos, data); - pos +=6; - } - return dataSize + 4; - } + LittleEndian.putUShort(data, 4 + offset, nItems); + + int pos = 6 ; + + for (int i = 0; i < nItems; i++) { + getRef(i).serialize(offset + pos, data); + pos +=6; + } + return dataSize + 4; + } private RefSubRecord getRef(int i) { return (RefSubRecord) _list.get(i); } - - public int getRecordSize() { - return 4 + getDataSize(); - } - - /** - * return the non static version of the id for this record. - */ - public short getSid() { - return sid; - } + + public int getRecordSize() { + return 4 + getDataSize(); + } + + /** + * return the non static version of the id for this record. + */ + public short getSid() { + return sid; + } public int getExtbookIndexFromRefIndex(int refIndex) { - return getRef(refIndex).getExtBookIndex(); + return getRef(refIndex).getExtBookIndex(); } /** * @return -1 if not found */ public int findRefIndexFromExtBookIndex(int extBookIndex) { - int nItems = _list.size(); - for (int i = 0; i < nItems; i++) { - if (getRef(i).getExtBookIndex() == extBookIndex) { - return i; - } - } + int nItems = _list.size(); + for (int i = 0; i < nItems; i++) { + if (getRef(i).getExtBookIndex() == extBookIndex) { + return i; + } + } return -1; } @@ -251,13 +251,25 @@ public class ExternSheetRecord extends Record { } public int getRefIxForSheet(int sheetIndex) { - int nItems = _list.size(); - for (int i = 0; i < nItems; i++) { - RefSubRecord ref = getRef(i); + int nItems = _list.size(); + for (int i = 0; i < nItems; i++) { + RefSubRecord ref = getRef(i); if (ref.getFirstSheetIndex() == sheetIndex && ref.getLastSheetIndex() == sheetIndex) { - return i; - } - } + return i; + } + } return -1; } + + public static ExternSheetRecord combine(ExternSheetRecord[] esrs) { + ExternSheetRecord result = new ExternSheetRecord(); + for (int i = 0; i < esrs.length; i++) { + ExternSheetRecord esr = esrs[i]; + int nRefs = esr.getNumOfREFRecords(); + for (int j=0; j i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() > i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isAfter(CellValueRecordInterface i) - { - if (this.getRow() < i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() < i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isEqual(CellValueRecordInterface i) - { - return ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())); - } - public boolean isInValueSection() { return true; @@ -433,51 +386,6 @@ public final class FormulaRecord { return true; } - - public int compareTo(Object obj) - { - CellValueRecordInterface loc = ( CellValueRecordInterface ) obj; - - if ((this.getRow() == loc.getRow()) - && (this.getColumn() == loc.getColumn())) - { - return 0; - } - if (this.getRow() < loc.getRow()) - { - return -1; - } - if (this.getRow() > loc.getRow()) - { - return 1; - } - if (this.getColumn() < loc.getColumn()) - { - return -1; - } - if (this.getColumn() > loc.getColumn()) - { - return 1; - } - return -1; - } - - public boolean equals(Object obj) - { - if (!(obj instanceof CellValueRecordInterface)) - { - return false; - } - CellValueRecordInterface loc = ( CellValueRecordInterface ) obj; - - if ((this.getRow() == loc.getRow()) - && (this.getColumn() == loc.getColumn())) - { - return true; - } - return false; - } - public String toString() { diff --git a/src/java/org/apache/poi/hssf/record/LabelRecord.java b/src/java/org/apache/poi/hssf/record/LabelRecord.java index c3fd5fb3dd..ae3b82f36f 100644 --- a/src/java/org/apache/poi/hssf/record/LabelRecord.java +++ b/src/java/org/apache/poi/hssf/record/LabelRecord.java @@ -180,51 +180,6 @@ public final class LabelRecord extends Record implements CellValueRecordInterfac return buffer.toString(); } - - public boolean isBefore(CellValueRecordInterface i) - { - if (this.getRow() > i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() > i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isAfter(CellValueRecordInterface i) - { - if (this.getRow() < i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() < i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isEqual(CellValueRecordInterface i) - { - return ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())); - } - public boolean isInValueSection() { return true; diff --git a/src/java/org/apache/poi/hssf/record/LabelSSTRecord.java b/src/java/org/apache/poi/hssf/record/LabelSSTRecord.java index 610f85522d..a8d68bac2c 100644 --- a/src/java/org/apache/poi/hssf/record/LabelSSTRecord.java +++ b/src/java/org/apache/poi/hssf/record/LabelSSTRecord.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,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record; @@ -30,13 +28,8 @@ import org.apache.poi.util.LittleEndian; * @author Jason Height (jheight at chariot dot net dot au) * @version 2.0-pre */ - -public class LabelSSTRecord - extends Record - implements CellValueRecordInterface, Comparable -{ +public final class LabelSSTRecord extends Record implements CellValueRecordInterface { public final static short sid = 0xfd; - //private short field_1_row; private int field_1_row; private short field_2_column; private short field_3_xf_index; @@ -183,50 +176,6 @@ public class LabelSSTRecord return sid; } - public boolean isBefore(CellValueRecordInterface i) - { - if (this.getRow() > i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() > i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isAfter(CellValueRecordInterface i) - { - if (this.getRow() < i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() < i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isEqual(CellValueRecordInterface i) - { - return ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())); - } - public boolean isInValueSection() { return true; @@ -237,50 +186,6 @@ public class LabelSSTRecord return true; } - public int compareTo(Object obj) - { - CellValueRecordInterface loc = ( CellValueRecordInterface ) obj; - - if ((this.getRow() == loc.getRow()) - && (this.getColumn() == loc.getColumn())) - { - return 0; - } - if (this.getRow() < loc.getRow()) - { - return -1; - } - if (this.getRow() > loc.getRow()) - { - return 1; - } - if (this.getColumn() < loc.getColumn()) - { - return -1; - } - if (this.getColumn() > loc.getColumn()) - { - return 1; - } - return -1; - } - - public boolean equals(Object obj) - { - if (!(obj instanceof CellValueRecordInterface)) - { - return false; - } - CellValueRecordInterface loc = ( CellValueRecordInterface ) obj; - - if ((this.getRow() == loc.getRow()) - && (this.getColumn() == loc.getColumn())) - { - return true; - } - return false; - } - public Object clone() { LabelSSTRecord rec = new LabelSSTRecord(); rec.field_1_row = field_1_row; diff --git a/src/java/org/apache/poi/hssf/record/NumberRecord.java b/src/java/org/apache/poi/hssf/record/NumberRecord.java index b21e488ed7..eeb5cf62ad 100644 --- a/src/java/org/apache/poi/hssf/record/NumberRecord.java +++ b/src/java/org/apache/poi/hssf/record/NumberRecord.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,13 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - -/* - * NumberRecord.java - * - * Created on October 1, 2001, 8:01 PM - */ package org.apache.poi.hssf.record; import org.apache.poi.util.LittleEndian; @@ -34,13 +27,8 @@ import org.apache.poi.hssf.record.Record; * @author Jason Height (jheight at chariot dot net dot au) * @version 2.0-pre */ - -public class NumberRecord - extends Record - implements CellValueRecordInterface, Comparable -{ +public final class NumberRecord extends Record implements CellValueRecordInterface { public static final short sid = 0x203; - //private short field_1_row; private int field_1_row; private short field_2_col; private short field_3_xf; @@ -203,50 +191,6 @@ public class NumberRecord return sid; } - public boolean isBefore(CellValueRecordInterface i) - { - if (this.getRow() > i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() > i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isAfter(CellValueRecordInterface i) - { - if (this.getRow() < i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() < i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isEqual(CellValueRecordInterface i) - { - return ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())); - } - public boolean isInValueSection() { return true; @@ -257,50 +201,6 @@ public class NumberRecord return true; } - public int compareTo(Object obj) - { - CellValueRecordInterface loc = ( CellValueRecordInterface ) obj; - - if ((this.getRow() == loc.getRow()) - && (this.getColumn() == loc.getColumn())) - { - return 0; - } - if (this.getRow() < loc.getRow()) - { - return -1; - } - if (this.getRow() > loc.getRow()) - { - return 1; - } - if (this.getColumn() < loc.getColumn()) - { - return -1; - } - if (this.getColumn() > loc.getColumn()) - { - return 1; - } - return -1; - } - - public boolean equals(Object obj) - { - if (!(obj instanceof CellValueRecordInterface)) - { - return false; - } - CellValueRecordInterface loc = ( CellValueRecordInterface ) obj; - - if ((this.getRow() == loc.getRow()) - && (this.getColumn() == loc.getColumn())) - { - return true; - } - return false; - } - public Object clone() { NumberRecord rec = new NumberRecord(); rec.field_1_row = field_1_row; diff --git a/src/java/org/apache/poi/hssf/record/RKRecord.java b/src/java/org/apache/poi/hssf/record/RKRecord.java index 4d30ddf4c9..2a99e16047 100644 --- a/src/java/org/apache/poi/hssf/record/RKRecord.java +++ b/src/java/org/apache/poi/hssf/record/RKRecord.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,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record; @@ -38,17 +36,12 @@ import org.apache.poi.hssf.util.RKUtil; * @version 2.0-pre * @see org.apache.poi.hssf.record.NumberRecord */ - -public class RKRecord - extends Record - implements CellValueRecordInterface -{ +public final class RKRecord extends Record implements CellValueRecordInterface { public final static short sid = 0x27e; public final static short RK_IEEE_NUMBER = 0; public final static short RK_IEEE_NUMBER_TIMES_100 = 1; public final static short RK_INTEGER = 2; public final static short RK_INTEGER_TIMES_100 = 3; - //private short field_1_row; private int field_1_row; private short field_2_col; private short field_3_xf_index; @@ -216,50 +209,6 @@ public class RKRecord return sid; } - public boolean isBefore(CellValueRecordInterface i) - { - if (this.getRow() > i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() > i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isAfter(CellValueRecordInterface i) - { - if (this.getRow() < i.getRow()) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() < i.getColumn())) - { - return false; - } - if ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())) - { - return false; - } - return true; - } - - public boolean isEqual(CellValueRecordInterface i) - { - return ((this.getRow() == i.getRow()) - && (this.getColumn() == i.getColumn())); - } - public boolean isInValueSection() { return true; diff --git a/src/java/org/apache/poi/hssf/record/UnknownRecord.java b/src/java/org/apache/poi/hssf/record/UnknownRecord.java index d3e76a020f..851dbfbf55 100644 --- a/src/java/org/apache/poi/hssf/record/UnknownRecord.java +++ b/src/java/org/apache/poi/hssf/record/UnknownRecord.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,10 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record; +import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; /** @@ -31,125 +30,259 @@ import org.apache.poi.util.LittleEndian; * @author Jason Height (jheight at chariot dot net dot au) * @author Glen Stampoultzis (glens at apache.org) */ +public final class UnknownRecord extends Record { + + /* + * Some Record IDs used by POI as 'milestones' in the record stream + */ + public static final int PLS_004D = 0x004D; + public static final int SHEETPR_0081 = 0x0081; + public static final int STANDARDWIDTH_0099 = 0x0099; + public static final int SCL_00A0 = 0x00A0; + public static final int BITMAP_00E9 = 0x00E9; + public static final int PHONETICPR_00EF = 0x00EF; + public static final int LABELRANGES_015F = 0x015F; + public static final int QUICKTIP_0800 = 0x0800; + public static final int SHEETEXT_0862 = 0x0862; // OOO calls this SHEETLAYOUT + public static final int SHEETPROTECTION_0867 = 0x0867; + public static final int RANGEPROTECTION_0868 = 0x0868; + + private int _sid; + private byte[] _rawData; + + /** + * @param id id of the record -not validated, just stored for serialization + * @param data the data + */ + public UnknownRecord(int id, byte[] data) { + _sid = id & 0xFFFF; + _rawData = data; + } + + + /** + * construct an unknown record. No fields are interpreted and the record will + * be serialized in its original form more or less + * @param in the RecordInputstream to read the record from + */ + public UnknownRecord(RecordInputStream in) { + _sid = in.getSid(); + _rawData = in.readRemainder(); + if (false && getBiffName(_sid) == null) { + // unknown sids in the range 0x0004-0x0013 are probably 'sub-records' of ObjectRecord + // those sids are in a different number space. + // TODO - put unknown OBJ sub-records in a different class + System.out.println("Unknown record 0x" + Integer.toHexString(_sid).toUpperCase()); + } + } + + /** + * spit the record out AS IS. no interpretation or identification + */ + public final int serialize(int offset, byte[] data) { + LittleEndian.putUShort(data, 0 + offset, _sid); + int dataSize = _rawData.length; + LittleEndian.putUShort(data, 2 + offset, dataSize); + System.arraycopy(_rawData, 0, data, 4 + offset, dataSize); + return 4 + dataSize; + } + + public final int getRecordSize() { + return 4 + _rawData.length; + } + + /** + * NO OP! + */ + protected void validateSid(short id) { + // if we had a valid sid we wouldn't be using the "Unknown Record" record now would we? + } + + /** + * print a sort of string representation ([UNKNOWN RECORD] id = x [/UNKNOWN RECORD]) + */ + public final String toString() { + String biffName = getBiffName(_sid); + if (biffName == null) { + biffName = "UNKNOWNRECORD"; + } + StringBuffer sb = new StringBuffer(); + + sb.append("[").append(biffName).append("] (0x"); + sb.append(Integer.toHexString(_sid).toUpperCase() + ")\n"); + if (_rawData.length > 0) { + sb.append(" rawData=").append(HexDump.toHex(_rawData)).append("\n"); + } + sb.append("[/").append(biffName).append("]\n"); + return sb.toString(); + } + + public final short getSid() { + return (short) _sid; + } + + /** + * These BIFF record types are known but still uninterpreted by POI + * + * @return the documented name of this BIFF record type + */ + private static String getBiffName(int sid) { + // Note to POI developers: + // Make sure you delete the corresponding entry from + // this method any time a new Record subclass is created. + switch (sid) { + case PLS_004D: return "PLS"; + case 0x0050: return "DCON"; + case 0x007F: return "IMDATA"; + case SHEETPR_0081: return "SHEETPR"; + case 0x0090: return "SORT"; + case 0x0094: return "LHRECORD"; + case STANDARDWIDTH_0099: return "STANDARDWIDTH"; + case 0x009D: return "AUTOFILTERINFO"; + case SCL_00A0: return "SCL"; + case 0x00AE: return "SCENMAN"; + case 0x00D3: return "OBPROJ"; + case 0x00DC: return "PARAMQRY"; + case 0x00DE: return "OLESIZE"; + case BITMAP_00E9: return "BITMAP"; + case PHONETICPR_00EF: return "PHONETICPR"; + + case LABELRANGES_015F: return "LABELRANGES"; + case 0x01BA: return "CODENAME"; + case 0x01A9: return "USERBVIEW"; + case 0x01AA: return "USERSVIEWBEGIN"; + case 0x01AB: return "USERSVIEWEND"; + case 0x01AD: return "QSI"; + + case 0x01C0: return "EXCEL9FILE"; + + case 0x0802: return "QSISXTAG"; + case 0x0803: return "DBQUERYEXT"; + case 0x0805: return "TXTQUERY"; + + case QUICKTIP_0800: return "QUICKTIP"; + case 0x0850: return "CHARTFRTINFO"; + case 0x0852: return "STARTBLOCK"; + case 0x0853: return "ENDBLOCK"; + case 0x0856: return "CATLAB"; + case SHEETEXT_0862: return "SHEETEXT"; + case 0x0863: return "BOOKEXT"; + case SHEETPROTECTION_0867: return "SHEETPROTECTION"; + case RANGEPROTECTION_0868: return "RANGEPROTECTION"; + case 0x086B: return "DATALABEXTCONTENTS"; + case 0x086C: return "CELLWATCH"; + case 0x0874: return "DROPDOWNOBJIDS"; + case 0x0876: return "DCONN"; + case 0x087B: return "CFEX"; + case 0x087C: return "XFCRC"; + case 0x087D: return "XFEXT"; + case 0x088B: return "PLV"; + case 0x088C: return "COMPAT12"; + case 0x088D: return "DXF"; + case 0x088E: return "TABLESTYLES"; + case 0x0892: return "STYLEEXT"; + case 0x0896: return "THEME"; + case 0x0897: return "GUIDTYPELIB"; + case 0x089A: return "MTRSETTINGS"; + case 0x089B: return "COMPRESSPICTURES"; + case 0x089C: return "HEADERFOOTER"; + case 0x08A3: return "FORCEFULLCALCULATION"; + case 0x08A4: return "SHAPEPROPSSTREAM"; + case 0x08A5: return "TEXTPROPSSTREAM"; + case 0x08A6: return "RICHTEXTSTREAM"; + + case 0x08C8: return "PLV{Mac Excel}"; + + case 0x1051: return "SHAPEPROPSSTREAM"; + + } + if (isObservedButUnknown(sid)) { + return "UNKNOWN-" + Integer.toHexString(sid).toUpperCase(); + } + + return null; + } + + /** + * + * @return true if the unknown record id has been observed in POI unit tests + */ + private static boolean isObservedButUnknown(int sid) { + switch (sid) { + case 0x0033: + // contains 2 bytes of data: 0x0001 or 0x0003 + case 0x0034: + // Seems to be written by MSAccess + // contains text "[Microsoft JET Created Table]0021010" + // appears after last cell value record and before WINDOW2 + case 0x01BD: + case 0x01C2: + // Written by Excel 2007 + // rawData is multiple of 12 bytes long + // appears after last cell value record and before WINDOW2 or drawing records + case 0x089D: + case 0x089E: + case 0x08A7: + + case 0x1001: + case 0x1006: + case 0x1007: + case 0x1009: + case 0x100A: + case 0x100B: + case 0x100C: + case 0x1014: + case 0x1017: + case 0x1018: + case 0x1019: + case 0x101A: + case 0x101B: + case 0x101D: + case 0x101E: + case 0x101F: + case 0x1020: + case 0x1021: + case 0x1022: + case 0x1024: + case 0x1025: + case 0x1026: + case 0x1027: + case 0x1032: + case 0x1033: + case 0x1034: + case 0x1035: + case 0x103A: + case 0x1041: + case 0x1043: + case 0x1044: + case 0x1045: + case 0x1046: + case 0x104A: + case 0x104B: + case 0x104E: + case 0x104F: + case 0x1051: + case 0x105C: + case 0x105D: + case 0x105F: + case 0x1060: + case 0x1062: + case 0x1063: + case 0x1064: + case 0x1065: + case 0x1066: + return true; + } + return false; + } + + protected final void fillFields(RecordInputStream in) { + throw new RecordFormatException( + "Unknown record cannot be constructed via offset -- we need a copy of the data"); + } -public class UnknownRecord - extends Record -{ - private short sid = 0; - private byte[] thedata = null; - - public UnknownRecord() - { - } - - /** - * @param id id of the record -not validated, just stored for serialization - * @param data the data - */ - public UnknownRecord(short id, byte[] data) - { - this.sid = id; - this.thedata = data; - } - - - /** - * construct an unknown record. No fields are interperated and the record will - * be serialized in its original form more or less - * @param in the RecordInputstream to read the record from - */ - - public UnknownRecord(RecordInputStream in) - { - sid = in.getSid(); - thedata = in.readRemainder(); - - //System.out.println("UnknownRecord: 0x"+Integer.toHexString(sid)); - } - - /** - * spit the record out AS IS. no interpretation or identification - */ - public int serialize(int offset, byte [] data) - { - if (thedata == null) - { - thedata = new byte[ 0 ]; - } - LittleEndian.putShort(data, 0 + offset, sid); - LittleEndian.putShort(data, 2 + offset, ( short ) (thedata.length)); - if (thedata.length > 0) - { - System.arraycopy(thedata, 0, data, 4 + offset, thedata.length); - } - return getRecordSize(); - } - - public int getRecordSize() - { - int retval = 4; - - if (thedata != null) - { - retval += thedata.length; - } - return retval; - } - - protected void fillFields(byte [] data, short sid) - { - this.sid = sid; - thedata = data; - } - - /** - * NO OP! - */ - - protected void validateSid(short id) - { - - // if we had a valid sid we wouldn't be using the "Unknown Record" record now would we? - } - - /** - * print a sort of string representation ([UNKNOWN RECORD] id = x [/UNKNOWN RECORD]) - */ - - public String toString() - { - StringBuffer buffer = new StringBuffer(); - - buffer.append("[UNKNOWN RECORD:" + Integer.toHexString(sid) + "]\n"); - buffer.append(" .id = ").append(Integer.toHexString(sid)) - .append("\n"); - buffer.append("[/UNKNOWN RECORD]\n"); - return buffer.toString(); - } - - public short getSid() - { - return sid; - } - - /** - * called by the constructor, should set class level fields. Should throw - * runtime exception for bad/icomplete data. - * - * @param in the RecordInputstream to read the record from - */ - - protected void fillFields(RecordInputStream in) - { - throw new RecordFormatException( - "Unknown record cannot be constructed via offset -- we need a copy of the data"); - } - - /** Unlike the other Record.clone methods this is a shallow clone*/ - public Object clone() { - UnknownRecord rec = new UnknownRecord(); - rec.sid = sid; - rec.thedata = thedata; - return rec; - } + public final Object clone() { + // immutable - ok to return this + return this; + } } diff --git a/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java index 7840d32562..3359ca55a4 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/FormulaRecordAggregate.java @@ -15,10 +15,11 @@ limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record.aggregates; -import org.apache.poi.hssf.record.*; +import org.apache.poi.hssf.record.CellValueRecordInterface; +import org.apache.poi.hssf.record.FormulaRecord; +import org.apache.poi.hssf.record.StringRecord; /** * The formula record aggregate is used to join together the formula record and it's @@ -26,171 +27,81 @@ import org.apache.poi.hssf.record.*; * * @author Glen Stampoultzis (glens at apache.org) */ -public class FormulaRecordAggregate - extends Record - implements CellValueRecordInterface, Comparable -{ - public final static short sid = -2000; +public final class FormulaRecordAggregate extends RecordAggregate implements CellValueRecordInterface { - private FormulaRecord formulaRecord; - private StringRecord stringRecord; + private FormulaRecord _formulaRecord; + private StringRecord _stringRecord; public FormulaRecordAggregate( FormulaRecord formulaRecord, StringRecord stringRecord ) { - this.formulaRecord = formulaRecord; - this.stringRecord = stringRecord; - } - - protected void validateSid( short id ) - { - } - - protected void fillFields( RecordInputStream in ) - { - } - - /** - * called by the class that is responsible for writing this sucker. - * Subclasses should implement this so that their data is passed back in a - * byte array. - * - * @param offset to begin writing at - * @param data byte array containing instance data - * @return number of bytes written - */ - - public int serialize( int offset, byte[] data ) - { - int pos = offset; - pos += formulaRecord.serialize(pos, data); - - if (stringRecord != null) - { - pos += stringRecord.serialize(pos, data); - } - return pos - offset; - + _formulaRecord = formulaRecord; + _stringRecord = stringRecord; } - /** - * gives the current serialized size of the record. Should include the sid and reclength (4 bytes). - */ - public int getRecordSize() - { - int size = formulaRecord.getRecordSize() + (stringRecord == null ? 0 : stringRecord.getRecordSize()); - return size; - } - - - /** - * return the non static version of the id for this record. - */ - public short getSid() - { - return sid; - } - - public void setStringRecord( StringRecord stringRecord ) - { - this.stringRecord = stringRecord; + public void setStringRecord( StringRecord stringRecord ) { + _stringRecord = stringRecord; } public void setFormulaRecord( FormulaRecord formulaRecord ) { - this.formulaRecord = formulaRecord; + _formulaRecord = formulaRecord; } public FormulaRecord getFormulaRecord() { - return formulaRecord; + return _formulaRecord; } public StringRecord getStringRecord() { - return stringRecord; + return _stringRecord; } - public boolean isEqual(CellValueRecordInterface i) - { - return formulaRecord.isEqual( i ); - } - - public boolean isAfter(CellValueRecordInterface i) - { - return formulaRecord.isAfter( i ); - } - - public boolean isBefore(CellValueRecordInterface i) - { - return formulaRecord.isBefore( i ); - } - public short getXFIndex() { - return formulaRecord.getXFIndex(); + return _formulaRecord.getXFIndex(); } public void setXFIndex(short xf) { - formulaRecord.setXFIndex( xf ); + _formulaRecord.setXFIndex( xf ); } public void setColumn(short col) { - formulaRecord.setColumn( col ); + _formulaRecord.setColumn( col ); } public void setRow(int row) { - formulaRecord.setRow( row ); + _formulaRecord.setRow( row ); } public short getColumn() { - return formulaRecord.getColumn(); + return _formulaRecord.getColumn(); } public int getRow() { - return formulaRecord.getRow(); + return _formulaRecord.getRow(); } - public int compareTo(Object o) - { - return formulaRecord.compareTo( o ); + public String toString() { + return _formulaRecord.toString(); } - - public boolean equals(Object obj) - { - return formulaRecord.equals( obj ); - } - - public String toString() - { - return formulaRecord.toString(); - } - - /** - * @see java.lang.Object#clone() - */ - public Object clone() { - StringRecord clonedString = (stringRecord == null) ? null : (StringRecord)stringRecord.clone(); - - return new FormulaRecordAggregate((FormulaRecord) this.formulaRecord.clone(), clonedString); + + public void visitContainedRecords(RecordVisitor rv) { + rv.visitRecord(_formulaRecord); + if (_stringRecord != null) { + rv.visitRecord(_stringRecord); + } } - - /* - * Setting to true so that this value does not abort the whole ValueAggregation - * (non-Javadoc) - * @see org.apache.poi.hssf.record.Record#isInValueSection() - */ - public boolean isInValueSection() { - - return true; - } - public String getStringValue() { - if(stringRecord==null) return null; - return stringRecord.getString(); - } + public String getStringValue() { + if(_stringRecord==null) { + return null; + } + return _stringRecord.getString(); + } } diff --git a/src/java/org/apache/poi/hssf/record/aggregates/MergedCellsTable.java b/src/java/org/apache/poi/hssf/record/aggregates/MergedCellsTable.java index b7384a0194..6a457fe48e 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/MergedCellsTable.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/MergedCellsTable.java @@ -41,8 +41,12 @@ public final class MergedCellsTable extends RecordAggregate { _mergedRegions = new ArrayList(); } - public MergedCellsTable(RecordStream rs) { - List temp = new ArrayList(); + /** + * reads zero or more consecutive {@link MergeCellsRecord}s + * @param rs + */ + public void read(RecordStream rs) { + List temp = _mergedRegions; while (rs.peekNextClass() == MergeCellsRecord.class) { MergeCellsRecord mcr = (MergeCellsRecord) rs.getNext(); int nRegions = mcr.getNumAreas(); @@ -50,7 +54,6 @@ public final class MergedCellsTable extends RecordAggregate { temp.add(mcr.getAreaAt(i)); } } - _mergedRegions = temp; } public int getRecordSize() { @@ -92,7 +95,10 @@ public final class MergedCellsTable extends RecordAggregate { } public void add(MergeCellsRecord mcr) { - _mergedRegions.add(mcr); + int nRegions = mcr.getNumAreas(); + for (int i = 0; i < nRegions; i++) { + _mergedRegions.add(mcr.getAreaAt(i)); + } } public CellRangeAddress get(int index) { diff --git a/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java b/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java index cc8be41791..819940e12e 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/PageSettingsBlock.java @@ -35,6 +35,7 @@ import org.apache.poi.hssf.record.PrintSetupRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RightMarginRecord; import org.apache.poi.hssf.record.TopMarginRecord; +import org.apache.poi.hssf.record.UnknownRecord; import org.apache.poi.hssf.record.VCenterRecord; import org.apache.poi.hssf.record.VerticalPageBreakRecord; @@ -99,9 +100,9 @@ public final class PageSettingsBlock extends RecordAggregate { case RightMarginRecord.sid: case TopMarginRecord.sid: case BottomMarginRecord.sid: - case 0x004D: // PLS + case UnknownRecord.PLS_004D: case PrintSetupRecord.sid: - case 0x00E9: // BITMAP + case UnknownRecord.BITMAP_00E9: return true; } return false; diff --git a/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java index c9a302b614..d839ecfab6 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java @@ -26,8 +26,10 @@ import java.util.TreeMap; import org.apache.poi.hssf.record.CellValueRecordInterface; import org.apache.poi.hssf.record.DBCellRecord; import org.apache.poi.hssf.record.IndexRecord; +import org.apache.poi.hssf.record.MergeCellsRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RowRecord; +import org.apache.poi.hssf.record.UnknownRecord; /** * @@ -39,6 +41,7 @@ public final class RowRecordsAggregate extends RecordAggregate { private int _lastrow = -1; private final Map _rowRecords; private final ValueRecordsAggregate _valuesAgg; + private final List _unknownRecords; /** Creates a new instance of ValueRecordsAggregate */ @@ -48,8 +51,54 @@ public final class RowRecordsAggregate extends RecordAggregate { private RowRecordsAggregate(TreeMap rowRecords, ValueRecordsAggregate valuesAgg) { _rowRecords = rowRecords; _valuesAgg = valuesAgg; + _unknownRecords = new ArrayList(); } + public RowRecordsAggregate(List recs, int startIx, int endIx) { + this(); + // First up, locate all the shared formulas for this sheet + SharedFormulaHolder sfh = SharedFormulaHolder.create(recs, startIx, endIx); + for(int i=startIx; iRowRecordsAggregate + * + * @author Josh Micich + */ +final class SharedFormulaHolder { + + private static final SharedFormulaHolder EMPTY = new SharedFormulaHolder(new SharedFormulaRecord[0]); + private final SharedFormulaRecord[] _sfrs; + + /** + * @param recs list of sheet records (possibly contains records for other parts of the Excel file) + * @param startIx index of first row/cell record for current sheet + * @param endIx one past index of last row/cell record for current sheet. It is important + * that this code does not inadvertently collect SharedFormulaRecords from any other + * sheet (which could happen if endIx is chosen poorly). (see bug 44449) + */ + public static SharedFormulaHolder create(List recs, int startIx, int endIx) { + List temp = new ArrayList(); + for (int k = startIx; k < endIx; k++) + { + Record rec = ( Record ) recs.get(k); + if (rec instanceof SharedFormulaRecord) { + temp.add(rec); + } + } + if (temp.size() < 1) { + return EMPTY; + } + SharedFormulaRecord[] sfrs = new SharedFormulaRecord[temp.size()]; + temp.toArray(sfrs); + return new SharedFormulaHolder(sfrs); + + } + private SharedFormulaHolder(SharedFormulaRecord[] sfrs) { + _sfrs = sfrs; + } + public void convertSharedFormulaRecord(FormulaRecord formula) { + // Traverse the list of shared formulas in + // reverse order, and try to find the correct one + // for us + for (int i=0; i<_sfrs.length; i++) { + SharedFormulaRecord shrd = _sfrs[i]; + if (shrd.isFormulaInShared(formula)) { + shrd.convertSharedFormulaRecord(formula); + return; + } + } + // not found + handleMissingSharedFormulaRecord(formula); + } + + /** + * Sometimes the shared formula flag "seems" to be erroneously set, in which case there is no + * call to SharedFormulaRecord.convertSharedFormulaRecord and hence the + * parsedExpression field of this FormulaRecord will not get updated.
+ * As it turns out, this is not a problem, because in these circumstances, the existing value + * for parsedExpression is perfectly OK.

+ * + * This method may also be used for setting breakpoints to help diagnose issues regarding the + * abnormally-set 'shared formula' flags. + * (see TestValueRecordsAggregate.testSpuriousSharedFormulaFlag()).

+ * + * The method currently does nothing but do not delete it without finding a nice home for this + * comment. + */ + private static void handleMissingSharedFormulaRecord(FormulaRecord formula) { + // could log an info message here since this is a fairly unusual occurrence. + } +} diff --git a/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java index 8d5764187f..0db1201432 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java @@ -22,11 +22,15 @@ import java.util.Iterator; import java.util.List; import org.apache.poi.hssf.record.CellValueRecordInterface; -import org.apache.poi.hssf.record.EOFRecord; +import org.apache.poi.hssf.record.DBCellRecord; import org.apache.poi.hssf.record.FormulaRecord; +import org.apache.poi.hssf.record.MergeCellsRecord; import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.RecordBase; +import org.apache.poi.hssf.record.RowRecord; import org.apache.poi.hssf.record.SharedFormulaRecord; import org.apache.poi.hssf.record.StringRecord; +import org.apache.poi.hssf.record.TableRecord; import org.apache.poi.hssf.record.UnknownRecord; import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor; @@ -39,8 +43,8 @@ import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor; * @author Jason Height (jheight at chariot dot net dot au) */ public final class ValueRecordsAggregate { - private int firstcell = -1; - private int lastcell = -1; + private int firstcell = -1; + private int lastcell = -1; private CellValueRecordInterface[][] records; /** Creates a new instance of ValueRecordsAggregate */ @@ -137,92 +141,81 @@ public final class ValueRecordsAggregate { return lastcell; } - public int construct(int offset, List records) - { + /** + * Processes a sequential group of cell value records. Stops at endIx or the first + * non-value record encountered. + * @param sfh used to resolve any shared formulas for the current sheet + * @return the number of records consumed + */ + public int construct(List records, int offset, int endIx, SharedFormulaHolder sfh) { int k = 0; FormulaRecordAggregate lastFormulaAggregate = null; - // First up, locate all the shared formulas for this sheet - List sharedFormulas = new java.util.ArrayList(); - for (k = offset; k < records.size(); k++) - { + // Now do the main processing sweep + for (k = offset; k < endIx; k++) { Record rec = ( Record ) records.get(k); + + if (rec instanceof StringRecord) { + if (lastFormulaAggregate == null) { + throw new RuntimeException("StringRecord found without preceding FormulaRecord"); + } + if (lastFormulaAggregate.getStringRecord() != null) { + throw new RuntimeException("Multiple StringRecords found after FormulaRecord"); + } + lastFormulaAggregate.setStringRecord((StringRecord)rec); + lastFormulaAggregate = null; + continue; + } + + if (rec instanceof TableRecord) { + // TODO - don't loose this record + // DATATABLE probably belongs in formula record aggregate + if (lastFormulaAggregate == null) { + throw new RuntimeException("No preceding formula record found"); + } + lastFormulaAggregate = null; + continue; + } + if (rec instanceof SharedFormulaRecord) { - sharedFormulas.add(rec); + // Already handled, not to worry + continue; } - if(rec instanceof EOFRecord) { - // End of current sheet. Ignore all subsequent shared formula records (Bugzilla 44449) + + if (rec instanceof UnknownRecord) { break; } - } - - // Now do the main processing sweep - for (k = offset; k < records.size(); k++) - { - Record rec = ( Record ) records.get(k); - - if (rec instanceof StringRecord == false && !rec.isInValueSection() && !(rec instanceof UnknownRecord)) - { + if (rec instanceof RowRecord) { + break; + } + if (rec instanceof DBCellRecord) { + // end of 'Row Block'. This record is ignored by POI break; - } else if (rec instanceof SharedFormulaRecord) { - // Already handled, not to worry - } else if (rec instanceof FormulaRecord) - { - FormulaRecord formula = (FormulaRecord)rec; - if (formula.isSharedFormula()) { - // Traverse the list of shared formulas in - // reverse order, and try to find the correct one - // for us - boolean found = false; - for (int i=sharedFormulas.size()-1;i>=0;i--) { - // TODO - there is no junit test case to justify this reversed loop - // perhaps it could just run in the normal direction? - SharedFormulaRecord shrd = (SharedFormulaRecord)sharedFormulas.get(i); - if (shrd.isFormulaInShared(formula)) { - shrd.convertSharedFormulaRecord(formula); - found = true; - break; - } - } - if (!found) { - handleMissingSharedFormulaRecord(formula); - } - } - - lastFormulaAggregate = new FormulaRecordAggregate((FormulaRecord)rec, null); - insertCell( lastFormulaAggregate ); } - else if (rec instanceof StringRecord) - { - lastFormulaAggregate.setStringRecord((StringRecord)rec); + if (rec instanceof MergeCellsRecord) { + // doesn't really belong here + // can safely be ignored, because it has been processed in a higher method + continue; + } + if (!rec.isValue()) { + throw new RuntimeException("bad record type"); } - else if (rec.isValue()) - { - insertCell(( CellValueRecordInterface ) rec); + if (rec instanceof FormulaRecord) { + FormulaRecord formula = (FormulaRecord)rec; + if (formula.isSharedFormula()) { + sfh.convertSharedFormulaRecord(formula); + } + + lastFormulaAggregate = new FormulaRecordAggregate((FormulaRecord)rec, null); + insertCell( lastFormulaAggregate ); + continue; } + insertCell(( CellValueRecordInterface ) rec); } - return k; + return k - offset - 1; } - /** - * Sometimes the shared formula flag "seems" to be erroneously set, in which case there is no - * call to SharedFormulaRecord.convertSharedFormulaRecord and hence the - * parsedExpression field of this FormulaRecord will not get updated.
- * As it turns out, this is not a problem, because in these circumstances, the existing value - * for parsedExpression is perfectly OK.

- * - * This method may also be used for setting breakpoints to help diagnose issues regarding the - * abnormally-set 'shared formula' flags. - * (see TestValueRecordsAggregate.testSpuriousSharedFormulaFlag()).

- * - * The method currently does nothing but do not delete it without finding a nice home for this - * comment. - */ - private static void handleMissingSharedFormulaRecord(FormulaRecord formula) { - // could log an info message here since this is a fairly unusual occurrence. - } - /** Tallies a count of the size of the cell records * that are attached to the rows in the range specified. */ @@ -235,7 +228,7 @@ public final class ValueRecordsAggregate { if (row > endRow) break; if ((row >=startRow) && (row <= endRow)) - size += ((Record)cell).getRecordSize(); + size += ((RecordBase)cell).getRecordSize(); } return size; } @@ -263,7 +256,7 @@ public final class ValueRecordsAggregate { CellValueRecordInterface cell = (CellValueRecordInterface)itr.next(); if (cell.getRow() != row) break; - pos += (( Record ) cell).serialize(pos, data); + pos += (( RecordBase ) cell).serialize(pos, data); } return pos - offset; } @@ -321,16 +314,6 @@ public final class ValueRecordsAggregate { { return new MyIterator(); } - - /** Performs a deep clone of the record*/ - public Object clone() { - ValueRecordsAggregate rec = new ValueRecordsAggregate(); - for (Iterator valIter = getIterator(); valIter.hasNext();) { - CellValueRecordInterface val = (CellValueRecordInterface)((CellValueRecordInterface)valIter.next()).clone(); - rec.insertCell(val); - } - return rec; - } private final class MyIterator implements Iterator { short nextColumn=-1; diff --git a/src/java/org/apache/poi/hssf/record/formula/TblPtg.java b/src/java/org/apache/poi/hssf/record/formula/TblPtg.java index 05d8391b29..9cc7334605 100644 --- a/src/java/org/apache/poi/hssf/record/formula/TblPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/TblPtg.java @@ -25,63 +25,59 @@ import org.apache.poi.util.LittleEndian; /** * This ptg indicates a data table. - * It only occurs in a FORMULA record, never in an - * ARRAY or NAME record. When ptgTbl occurs in a - * formula, it is the only token in the formula. - * (TODO - check this when processing) - * This indicates that the cell containing the - * formula is an interior cell in a data table; + * It only occurs in a FORMULA record, never in an + * ARRAY or NAME record. When ptgTbl occurs in a + * formula, it is the only token in the formula. + * + * This indicates that the cell containing the + * formula is an interior cell in a data table; * the table description is found in a TABLE - * record. Rows and columns which contain input - * values to be substituted in the table do + * record. Rows and columns which contain input + * values to be substituted in the table do * not contain ptgTbl. * See page 811 of the june 08 binary docs. */ public final class TblPtg extends ControlPtg { - private final static int SIZE = 4; - public final static short sid = 0x2; + private final static int SIZE = 5; + public final static short sid = 0x02; /** The row number of the upper left corner */ - private final short field_1_first_row; + private final int field_1_first_row; /** The column number of the upper left corner */ - private final short field_2_first_col; + private final int field_2_first_col; - public TblPtg(RecordInputStream in) - { - field_1_first_row = in.readShort(); - field_2_first_col = in.readUByte(); + public TblPtg(RecordInputStream in) { + field_1_first_row = in.readUShort(); + field_2_first_col = in.readUShort(); } - - public void writeBytes(byte [] array, int offset) - { - array[offset+0]= (byte) (sid); - LittleEndian.putShort(array,offset+1,field_1_first_row); - LittleEndian.putByte(array,offset+3,field_2_first_col); + + public void writeBytes(byte [] array, int offset) { + LittleEndian.putByte(array, offset+0, sid); + LittleEndian.putUShort(array, offset+1, field_1_first_row); + LittleEndian.putUShort(array, offset+3, field_2_first_col); } - public int getSize() - { + public int getSize() { return SIZE; } - - public short getRow() { + + public int getRow() { return field_1_first_row; } - public short getColumn() { + public int getColumn() { return field_2_first_col; - } + } public String toFormulaString(Workbook book) { - // table(....)[][] + // table(....)[][] throw new RecordFormatException("Table and Arrays are not yet supported"); } - - public String toString() - { + + public String toString() { StringBuffer buffer = new StringBuffer("[Data Table - Parent cell is an interior cell in a data table]\n"); buffer.append("top left row = ").append(getRow()).append("\n"); buffer.append("top left col = ").append(getColumn()).append("\n"); return buffer.toString(); - } + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java index f66baf3024..7f8f7aa3b9 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java @@ -189,40 +189,25 @@ public class HSSFCell implements Cell { /** * used internally -- given a cell value record, figure out its type */ - private int determineType(CellValueRecordInterface cval) - { + private static int determineType(CellValueRecordInterface cval) { + if (cval instanceof FormulaRecordAggregate) { + return HSSFCell.CELL_TYPE_FORMULA; + } + // all others are plain BIFF records Record record = ( Record ) cval; - int sid = record.getSid(); - int retval = 0; - - switch (sid) - { - - case NumberRecord.sid : - retval = HSSFCell.CELL_TYPE_NUMERIC; - break; - - case BlankRecord.sid : - retval = HSSFCell.CELL_TYPE_BLANK; - break; - - case LabelSSTRecord.sid : - retval = HSSFCell.CELL_TYPE_STRING; - break; - - case FormulaRecordAggregate.sid : - retval = HSSFCell.CELL_TYPE_FORMULA; - break; + switch (record.getSid()) { + case NumberRecord.sid : return HSSFCell.CELL_TYPE_NUMERIC; + case BlankRecord.sid : return HSSFCell.CELL_TYPE_BLANK; + case LabelSSTRecord.sid : return HSSFCell.CELL_TYPE_STRING; case BoolErrRecord.sid : BoolErrRecord boolErrRecord = ( BoolErrRecord ) record; - retval = (boolErrRecord.isBoolean()) + return boolErrRecord.isBoolean() ? HSSFCell.CELL_TYPE_BOOLEAN : HSSFCell.CELL_TYPE_ERROR; - break; } - return retval; + throw new RuntimeException("Bad cell value rec (" + cval.getClass().getName() + ")"); } /** diff --git a/src/testcases/org/apache/poi/hssf/data/ex45698-22488.xls b/src/testcases/org/apache/poi/hssf/data/ex45698-22488.xls new file mode 100644 index 0000000000..615e6c5d8c Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/ex45698-22488.xls differ diff --git a/src/testcases/org/apache/poi/hssf/model/TestSheet.java b/src/testcases/org/apache/poi/hssf/model/TestSheet.java index 6c6dd9fb25..97635e5362 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestSheet.java +++ b/src/testcases/org/apache/poi/hssf/model/TestSheet.java @@ -24,6 +24,7 @@ import java.util.List; import junit.framework.AssertionFailedError; import junit.framework.TestCase; +import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.eventmodel.ERFListener; import org.apache.poi.hssf.eventmodel.EventRecordFactory; import org.apache.poi.hssf.record.BOFRecord; @@ -32,6 +33,7 @@ import org.apache.poi.hssf.record.CellValueRecordInterface; import org.apache.poi.hssf.record.ColumnInfoRecord; import org.apache.poi.hssf.record.DimensionsRecord; import org.apache.poi.hssf.record.EOFRecord; +import org.apache.poi.hssf.record.FormulaRecord; import org.apache.poi.hssf.record.GutsRecord; import org.apache.poi.hssf.record.IndexRecord; import org.apache.poi.hssf.record.MergeCellsRecord; @@ -39,9 +41,13 @@ import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RowRecord; import org.apache.poi.hssf.record.StringRecord; import org.apache.poi.hssf.record.UncalcedRecord; +import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate; +import org.apache.poi.hssf.record.aggregates.MergedCellsTable; import org.apache.poi.hssf.record.aggregates.PageSettingsBlock; import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate; +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.util.CellRangeAddress; @@ -57,6 +63,7 @@ public final class TestSheet extends TestCase { List records = new ArrayList(); records.add( new BOFRecord() ); records.add( new DimensionsRecord() ); + records.add(createWindow2Record()); records.add(EOFRecord.instance); Sheet sheet = Sheet.createSheet( records, 0, 0 ); @@ -65,9 +72,22 @@ public final class TestSheet extends TestCase { assertTrue( sheet.records.get(pos++) instanceof ColumnInfoRecordsAggregate ); assertTrue( sheet.records.get(pos++) instanceof DimensionsRecord ); assertTrue( sheet.records.get(pos++) instanceof RowRecordsAggregate ); + assertTrue( sheet.records.get(pos++) instanceof WindowTwoRecord ); + assertTrue( sheet.records.get(pos++) instanceof MergedCellsTable ); assertTrue( sheet.records.get(pos++) instanceof EOFRecord ); } + private static Record createWindow2Record() { + WindowTwoRecord result = new WindowTwoRecord(); + result.setOptions(( short ) 0x6b6); + result.setTopRow(( short ) 0); + result.setLeftCol(( short ) 0); + result.setHeaderColor(0x40); + result.setPageBreakZoom(( short ) 0); + result.setNormalZoom(( short ) 0); + return result; + } + private static final class MergedCellListener implements ERFListener { private int _count; @@ -168,6 +188,8 @@ public final class TestSheet extends TestCase { records.add(new RowRecord(0)); records.add(new RowRecord(1)); records.add(new RowRecord(2)); + records.add(createWindow2Record()); + records.add(EOFRecord.instance); records.add(merged); Sheet sheet = Sheet.createSheet(records, 0); @@ -193,11 +215,15 @@ public final class TestSheet extends TestCase { public void testRowAggregation() { List records = new ArrayList(); + records.add(Sheet.createBOF()); records.add(new DimensionsRecord()); records.add(new RowRecord(0)); records.add(new RowRecord(1)); + records.add(new FormulaRecord()); records.add(new StringRecord()); records.add(new RowRecord(2)); + records.add(createWindow2Record()); + records.add(EOFRecord.instance); Sheet sheet = Sheet.createSheet(records, 0); assertNotNull("Row [2] was skipped", sheet.getRow(2)); @@ -400,6 +426,7 @@ public final class TestSheet extends TestCase { records.add(new BOFRecord()); records.add(new UncalcedRecord()); records.add(new DimensionsRecord()); + records.add(createWindow2Record()); records.add(EOFRecord.instance); Sheet sheet = Sheet.createSheet(records, 0, 0); @@ -408,7 +435,7 @@ public final class TestSheet extends TestCase { if (serializedSize != estimatedSize) { throw new AssertionFailedError("Identified bug 45066 b"); } - assertEquals(68, serializedSize); + assertEquals(90, serializedSize); } /** @@ -502,5 +529,17 @@ public final class TestSheet extends TestCase { } assertEquals(1, count); } + + public void testMisplacedMergedCellsRecords_bug45699() { + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex45698-22488.xls"); + + HSSFSheet sheet = wb.getSheetAt(0); + HSSFRow row = sheet.getRow(3); + HSSFCell cell = row.getCell(4); + if (cell == null) { + throw new AssertionFailedError("Identified bug 45699"); + } + assertEquals("Informations", cell.getRichStringCellValue().getString()); + } } diff --git a/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java b/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java index cdf74cabb2..88b5477783 100644 --- a/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java +++ b/src/testcases/org/apache/poi/hssf/record/aggregates/TestFormulaRecordAggregate.java @@ -29,23 +29,13 @@ import org.apache.poi.hssf.record.StringRecord; * * @author avik */ -public class TestFormulaRecordAggregate extends junit.framework.TestCase { +public final class TestFormulaRecordAggregate extends junit.framework.TestCase { - /** Creates a new instance of TestFormulaRecordAggregate */ - public TestFormulaRecordAggregate(String arg) { - super(arg); - } - - public void testClone() { + public void testBasic() throws Exception { FormulaRecord f = new FormulaRecord(); StringRecord s = new StringRecord(); + s.setString("abc"); FormulaRecordAggregate fagg = new FormulaRecordAggregate(f,s); - FormulaRecordAggregate newFagg = (FormulaRecordAggregate) fagg.clone(); - assertTrue("objects are different", fagg!=newFagg); - assertTrue("deep clone", fagg.getFormulaRecord() != newFagg.getFormulaRecord()); - assertTrue("deep clone", fagg.getStringRecord() != newFagg.getStringRecord()); - - + assertEquals("abc", fagg.getStringValue()); } - } diff --git a/src/testcases/org/apache/poi/hssf/record/aggregates/TestValueRecordsAggregate.java b/src/testcases/org/apache/poi/hssf/record/aggregates/TestValueRecordsAggregate.java index 3464c407d0..7ea2e85f43 100755 --- a/src/testcases/org/apache/poi/hssf/record/aggregates/TestValueRecordsAggregate.java +++ b/src/testcases/org/apache/poi/hssf/record/aggregates/TestValueRecordsAggregate.java @@ -17,8 +17,6 @@ package org.apache.poi.hssf.record.aggregates; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -32,31 +30,27 @@ import junit.framework.TestCase; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.record.BlankRecord; import org.apache.poi.hssf.record.FormulaRecord; -import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.RecordBase; import org.apache.poi.hssf.record.SharedFormulaRecord; -import org.apache.poi.hssf.record.UnknownRecord; -import org.apache.poi.hssf.record.WindowOneRecord; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -public class TestValueRecordsAggregate extends TestCase -{ +public final class TestValueRecordsAggregate extends TestCase { private static final String ABNORMAL_SHARED_FORMULA_FLAG_TEST_FILE = "AbnormalSharedFormulaFlag.xls"; - ValueRecordsAggregate valueRecord = new ValueRecordsAggregate(); + private final ValueRecordsAggregate valueRecord = new ValueRecordsAggregate(); /** * Make sure the shared formula DOESNT makes it to the FormulaRecordAggregate when being parsed * as part of the value records */ - public void testSharedFormula() - { + public void testSharedFormula() { List records = new ArrayList(); records.add( new FormulaRecord() ); records.add( new SharedFormulaRecord() ); - valueRecord.construct( 0, records ); + constructValueRecord(records); Iterator iterator = valueRecord.getIterator(); - Record record = (Record) iterator.next(); + RecordBase record = (RecordBase) iterator.next(); assertNotNull( "Row contains a value", record ); assertTrue( "First record is a FormulaRecordsAggregate", ( record instanceof FormulaRecordAggregate ) ); //Ensure that the SharedFormulaRecord has been converted @@ -64,39 +58,25 @@ public class TestValueRecordsAggregate extends TestCase } - public void testUnknownRecordsIgnored() - { - List records = testData(); - valueRecord.construct( 0, records ); - Iterator iterator = valueRecord.getIterator(); - Record record1 = (Record) iterator.next(); - Record record2 = (Record) iterator.next(); - assertNotNull( "No record found", record1 ); - assertNotNull( "No record found", record2 ); - assertFalse( iterator.hasNext() ); - + private void constructValueRecord(List records) { + SharedFormulaHolder sfrh = SharedFormulaHolder.create(records, 0, records.size()); + valueRecord.construct(records, 0, records.size(), sfrh ); } - private List testData(){ + private static List testData() { List records = new ArrayList(); FormulaRecord formulaRecord = new FormulaRecord(); - UnknownRecord unknownRecord = new UnknownRecord(); BlankRecord blankRecord = new BlankRecord(); - WindowOneRecord windowOneRecord = new WindowOneRecord(); formulaRecord.setRow( 1 ); formulaRecord.setColumn( (short) 1 ); blankRecord.setRow( 2 ); blankRecord.setColumn( (short) 2 ); records.add( formulaRecord ); - records.add( unknownRecord ); records.add( blankRecord ); - records.add( windowOneRecord ); return records; } - public void testInsertCell() - throws Exception - { + public void testInsertCell() { Iterator iterator = valueRecord.getIterator(); assertFalse( iterator.hasNext() ); @@ -118,8 +98,7 @@ public class TestValueRecordsAggregate extends TestCase valueRecord.removeCell( blankRecord2 ); } - public void testGetPhysicalNumberOfCells() throws Exception - { + public void testGetPhysicalNumberOfCells() { assertEquals(0, valueRecord.getPhysicalNumberOfCells()); BlankRecord blankRecord1 = newBlankRecord(); valueRecord.insertCell( blankRecord1 ); @@ -128,8 +107,7 @@ public class TestValueRecordsAggregate extends TestCase assertEquals(0, valueRecord.getPhysicalNumberOfCells()); } - public void testGetFirstCellNum() throws Exception - { + public void testGetFirstCellNum() { assertEquals( -1, valueRecord.getFirstCellNum() ); valueRecord.insertCell( newBlankRecord( 2, 2 ) ); assertEquals( 2, valueRecord.getFirstCellNum() ); @@ -141,8 +119,7 @@ public class TestValueRecordsAggregate extends TestCase assertEquals( 2, valueRecord.getFirstCellNum() ); } - public void testGetLastCellNum() throws Exception - { + public void testGetLastCellNum() { assertEquals( -1, valueRecord.getLastCellNum() ); valueRecord.insertCell( newBlankRecord( 2, 2 ) ); assertEquals( 2, valueRecord.getLastCellNum() ); @@ -155,8 +132,7 @@ public class TestValueRecordsAggregate extends TestCase } - public void testSerialize() throws Exception - { + public void testSerialize() { byte[] actualArray = new byte[36]; byte[] expectedArray = new byte[] { @@ -171,7 +147,7 @@ public class TestValueRecordsAggregate extends TestCase (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, }; List records = testData(); - valueRecord.construct( 0, records ); + constructValueRecord(records); int bytesWritten = valueRecord.serializeCellRow(1, 0, actualArray ); bytesWritten += valueRecord.serializeCellRow(2, bytesWritten, actualArray ); assertEquals( 36, bytesWritten ); @@ -179,18 +155,12 @@ public class TestValueRecordsAggregate extends TestCase assertEquals( expectedArray[i], actualArray[i] ); } - public static void main( String[] args ) - { - System.out.println( "Testing org.apache.poi.hssf.record.aggregates.TestValueRecordAggregate" ); - junit.textui.TestRunner.run( TestValueRecordsAggregate.class ); - } - - private BlankRecord newBlankRecord() + private static BlankRecord newBlankRecord() { return newBlankRecord( 2, 2 ); } - private BlankRecord newBlankRecord( int col, int row) + private static BlankRecord newBlankRecord( int col, int row) { BlankRecord blankRecord = new BlankRecord(); blankRecord.setRow( row ); @@ -300,5 +270,4 @@ public class TestValueRecordsAggregate extends TestCase return crc.getValue(); } - } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestLinkTable.java b/src/testcases/org/apache/poi/hssf/usermodel/TestLinkTable.java index 7d1082e861..d8b4e95212 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestLinkTable.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestLinkTable.java @@ -41,4 +41,19 @@ public final class TestLinkTable extends TestCase { assertEquals("ipcSummenproduktIntern($C5,N$2,$A$9,N$1)", formula); } + + public void testMultipleExternSheetRecords_bug45698() { + HSSFWorkbook wb; + + try { + wb = HSSFTestDataSamples.openSampleWorkbook("ex45698-22488.xls"); + } catch (RuntimeException e) { + if ("Extern sheet is part of LinkTable".equals(e.getMessage())) { + throw new AssertionFailedError("Identified bug 45698"); + } + throw e; + } + // some other sanity checks + assertEquals(7, wb.getNumberOfSheets()); + } }