diff --git a/src/java/org/apache/poi/hssf/model/RecordOrderer.java b/src/java/org/apache/poi/hssf/model/RecordOrderer.java new file mode 100644 index 000000000..ae445597d --- /dev/null +++ b/src/java/org/apache/poi/hssf/model/RecordOrderer.java @@ -0,0 +1,326 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hssf.model; + +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.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.EOFRecord; +import org.apache.poi.hssf.record.GridsetRecord; +import org.apache.poi.hssf.record.GutsRecord; +import org.apache.poi.hssf.record.HorizontalPageBreakRecord; +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.PaneRecord; +import org.apache.poi.hssf.record.PrecisionRecord; +import org.apache.poi.hssf.record.PrintGridlinesRecord; +import org.apache.poi.hssf.record.PrintHeadersRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.RecordBase; +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.UncalcedRecord; +import org.apache.poi.hssf.record.VerticalPageBreakRecord; +import org.apache.poi.hssf.record.WindowTwoRecord; +import org.apache.poi.hssf.record.aggregates.ConditionalFormattingTable; +import org.apache.poi.hssf.record.aggregates.DataValidityTable; +import org.apache.poi.hssf.record.aggregates.MergedCellsTable; + +/** + * Finds correct insert positions for records in workbook streams

+ * + * See OOO excelfileformat.pdf sec. 4.2.5 'Record Order in a BIFF8 Workbook Stream' + * + * @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 + + private RecordOrderer() { + // no instances of this class + } + /** + * Adds the specified new record in the correct place in sheet records list + * + */ + public static void addNewSheetRecord(List sheetRecords, RecordBase newRecord) { + int index = findSheetInsertPos(sheetRecords, newRecord.getClass()); + sheetRecords.add(index, newRecord); + } + + private static int findSheetInsertPos(List records, Class recClass) { + if (recClass == DataValidityTable.class) { + return findDataValidationTableInsertPos(records); + } + if (recClass == MergedCellsTable.class) { + return findInsertPosForNewMergedRecordTable(records); + } + if (recClass == ConditionalFormattingTable.class) { + return findInsertPosForNewCondFormatTable(records); + } + if (recClass == GutsRecord.class) { + return getGutsRecordInsertPos(records); + } + if (recClass == HorizontalPageBreakRecord.class) { + return getPageBreakRecordInsertPos(records, true); + } + if (recClass == VerticalPageBreakRecord.class) { + return getPageBreakRecordInsertPos(records, false); + } + throw new RuntimeException("Unexpected record class (" + recClass.getName() + ")"); + } + + private static int getPageBreakRecordInsertPos(List records, boolean isHorizonal) { + int dimensionsIndex = getDimensionsIndex(records); + int i = dimensionsIndex-1; + while (i > 0) { + i--; + Object rb = records.get(i); + if (isPageBreakPriorRecord(rb, isHorizonal)) { + return i+1; + } + } + throw new RuntimeException("Did not find insert point for GUTS"); + } + private static boolean isPageBreakPriorRecord(Object rb, boolean newRecIsHorizontal) { + if (rb instanceof Record) { + Record record = (Record) rb; + switch (record.getSid()) { + case BOFRecord.sid: + case IndexRecord.sid: + // calc settings block + case UncalcedRecord.sid: + case CalcCountRecord.sid: + case CalcModeRecord.sid: + case PrecisionRecord.sid: + case RefModeRecord.sid: + case DeltaRecord.sid: + case IterationRecord.sid: + case DateWindow1904Record.sid: + case SaveRecalcRecord.sid: + // end calc settings + case PrintHeadersRecord.sid: + case PrintGridlinesRecord.sid: + case GridsetRecord.sid: + case DefaultRowHeightRecord.sid: + case 0x0081: // SHEETPR + return true; + } + switch (record.getSid()) { + // page settings block + case HorizontalPageBreakRecord.sid: + if (!newRecIsHorizontal) { + return true; + } + return false; + case VerticalPageBreakRecord.sid: + return false; + // next is case HeaderRecord.sid: case FooterRecord.sid: + // then more records in page settings block + + } + } + return false; + } + /** + * Find correct position to add new CFHeader record + */ + private static int findInsertPosForNewCondFormatTable(List records) { + + for (int i = records.size() - 2; i >= 0; i--) { // -2 to skip EOF record + Object rb = records.get(i); + if (rb instanceof MergedCellsTable) { + return i + 1; + } + Record rec = (Record) rb; + switch (rec.getSid()) { + case WindowTwoRecord.sid: + case SCLRecord.sid: + case PaneRecord.sid: + case SelectionRecord.sid: + case 0x0099:// STANDARDWIDTH + // MergedCellsTable usually here + case 0x015f:// LABELRANGES + case 0x00ef:// PHONETICPR + return i + 1; + } + } + throw new RuntimeException("Did not find Window2 record"); + } + + 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); + Record rec = (Record) rb; + switch (rec.getSid()) { + case WindowTwoRecord.sid: + case SCLRecord.sid: + case PaneRecord.sid: + case SelectionRecord.sid: + case 0x0099:// STANDARDWIDTH + return i + 1; + } + } + throw new RuntimeException("Did not find Window2 record"); + } + + + /** + * Finds the index where the sheet validations header record should be inserted + * @param records the records for this sheet + * + * + WINDOW2 + * o SCL + * o PANE + * oo SELECTION + * o STANDARDWIDTH + * oo MERGEDCELLS + * o LABELRANGES + * o PHONETICPR + * o Conditional Formatting Table + * o Hyperlink Table + * o Data Validity Table + * o SHEETLAYOUT + * o SHEETPROTECTION + * o RANGEPROTECTION + * + EOF + */ + private static int findDataValidationTableInsertPos(List records) { + int i = records.size() - 1; + if (!(records.get(i) instanceof EOFRecord)) { + throw new IllegalStateException("Last sheet record should be EOFRecord"); + } + while (i > 0) { + i--; + Object rb = records.get(i); + if (isDVTPriorRecord(rb)) { + Record nextRec = (Record) records.get(i + 1); + if (!isDVTSubsequentRecord(nextRec.getSid())) { + throw new IllegalStateException("Unexpected (" + nextRec.getClass().getName() + + ") found after (" + rb.getClass().getName() + ")"); + } + return i+1; + } + Record rec = (Record) rb; + if (!isDVTSubsequentRecord(rec.getSid())) { + throw new IllegalStateException("Unexpected (" + rec.getClass().getName() + + ") while looking for DV Table insert pos"); + } + } + return 0; + } + + + private static boolean isDVTPriorRecord(Object rb) { + if (rb instanceof MergedCellsTable || rb instanceof ConditionalFormattingTable) { + return true; + } + short sid = ((Record)rb).getSid(); + switch(sid) { + case WindowTwoRecord.sid: + case 0x00A0: // SCL + case PaneRecord.sid: + case SelectionRecord.sid: + case 0x0099: // STANDARDWIDTH + // MergedCellsTable + case 0x015F: // LABELRANGES + case 0x00EF: // PHONETICPR + // ConditionalFormattingTable + case HyperlinkRecord.sid: + case 0x0800: // QUICKTIP + return true; + } + return false; + } + + private static boolean isDVTSubsequentRecord(short sid) { + switch(sid) { + case 0x0862: // SHEETLAYOUT + case 0x0867: // SHEETPROTECTION + case 0x0868: // RANGEPROTECTION + case EOFRecord.sid: + return true; + } + return false; + } + /** + * DIMENSIONS record is always present + */ + private static int getDimensionsIndex(List records) { + int nRecs = records.size(); + for(int i=0; i 0) { + i--; + Object rb = records.get(i); + if (isGutsPriorRecord(rb)) { + return i+1; + } + } + throw new RuntimeException("Did not find insert point for GUTS"); + } + + private static boolean isGutsPriorRecord(Object rb) { + if (rb instanceof Record) { + Record record = (Record) rb; + switch (record.getSid()) { + case BOFRecord.sid: + case IndexRecord.sid: + // calc settings block + case UncalcedRecord.sid: + case CalcCountRecord.sid: + case CalcModeRecord.sid: + case PrecisionRecord.sid: + case RefModeRecord.sid: + case DeltaRecord.sid: + case IterationRecord.sid: + case DateWindow1904Record.sid: + case SaveRecalcRecord.sid: + // end calc settings + case PrintHeadersRecord.sid: + case PrintGridlinesRecord.sid: + case GridsetRecord.sid: + return true; + // DefaultRowHeightRecord.sid is next + } + } + return false; + } +} diff --git a/src/java/org/apache/poi/hssf/model/Sheet.java b/src/java/org/apache/poi/hssf/model/Sheet.java index 993dfd7b3..3d74c9f88 100644 --- a/src/java/org/apache/poi/hssf/model/Sheet.java +++ b/src/java/org/apache/poi/hssf/model/Sheet.java @@ -17,22 +17,77 @@ package org.apache.poi.hssf.model; -import org.apache.poi.hssf.record.*; // normally I don't do this, buy we literally mean ALL -import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate; -import org.apache.poi.hssf.record.aggregates.DataValidityTable; -import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; -import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate; -import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate; -import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate; -import org.apache.poi.hssf.util.CellRangeAddress; -import org.apache.poi.hssf.util.PaneInformation; - -import org.apache.poi.util.POILogFactory; -import org.apache.poi.util.POILogger; - import java.util.ArrayList; import java.util.Iterator; -import java.util.List; +import java.util.List; + +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.BottomMarginRecord; +import org.apache.poi.hssf.record.CFHeaderRecord; +import org.apache.poi.hssf.record.CalcCountRecord; +import org.apache.poi.hssf.record.CalcModeRecord; +import org.apache.poi.hssf.record.CellValueRecordInterface; +import org.apache.poi.hssf.record.ColumnInfoRecord; +import org.apache.poi.hssf.record.DBCellRecord; +import org.apache.poi.hssf.record.DVALRecord; +import org.apache.poi.hssf.record.DefaultColWidthRecord; +import org.apache.poi.hssf.record.DefaultRowHeightRecord; +import org.apache.poi.hssf.record.DeltaRecord; +import org.apache.poi.hssf.record.DimensionsRecord; +import org.apache.poi.hssf.record.DrawingRecord; +import org.apache.poi.hssf.record.EOFRecord; +import org.apache.poi.hssf.record.EscherAggregate; +import org.apache.poi.hssf.record.FooterRecord; +import org.apache.poi.hssf.record.GridsetRecord; +import org.apache.poi.hssf.record.GutsRecord; +import org.apache.poi.hssf.record.HCenterRecord; +import org.apache.poi.hssf.record.HeaderRecord; +import org.apache.poi.hssf.record.HorizontalPageBreakRecord; +import org.apache.poi.hssf.record.IndexRecord; +import org.apache.poi.hssf.record.IterationRecord; +import org.apache.poi.hssf.record.LeftMarginRecord; +import org.apache.poi.hssf.record.Margin; +import org.apache.poi.hssf.record.MergeCellsRecord; +import org.apache.poi.hssf.record.ObjRecord; +import org.apache.poi.hssf.record.ObjectProtectRecord; +import org.apache.poi.hssf.record.PageBreakRecord; +import org.apache.poi.hssf.record.PaneRecord; +import org.apache.poi.hssf.record.PasswordRecord; +import org.apache.poi.hssf.record.PrintGridlinesRecord; +import org.apache.poi.hssf.record.PrintHeadersRecord; +import org.apache.poi.hssf.record.PrintSetupRecord; +import org.apache.poi.hssf.record.ProtectRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.RecordBase; +import org.apache.poi.hssf.record.RefModeRecord; +import org.apache.poi.hssf.record.RightMarginRecord; +import org.apache.poi.hssf.record.RowRecord; +import org.apache.poi.hssf.record.SCLRecord; +import org.apache.poi.hssf.record.SaveRecalcRecord; +import org.apache.poi.hssf.record.ScenarioProtectRecord; +import org.apache.poi.hssf.record.SelectionRecord; +import org.apache.poi.hssf.record.SharedFormulaRecord; +import org.apache.poi.hssf.record.StringRecord; +import org.apache.poi.hssf.record.TopMarginRecord; +import org.apache.poi.hssf.record.UncalcedRecord; +import org.apache.poi.hssf.record.VCenterRecord; +import org.apache.poi.hssf.record.VerticalPageBreakRecord; +import org.apache.poi.hssf.record.WSBoolRecord; +import org.apache.poi.hssf.record.WindowTwoRecord; +import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate; +import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate; +import org.apache.poi.hssf.record.aggregates.ConditionalFormattingTable; +import org.apache.poi.hssf.record.aggregates.DataValidityTable; +import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; +import org.apache.poi.hssf.record.aggregates.MergedCellsTable; +import org.apache.poi.hssf.record.aggregates.RecordAggregate; +import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate; +import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate; +import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor; +import org.apache.poi.hssf.util.CellRangeAddress; +import org.apache.poi.hssf.util.PaneInformation; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; /** * Low level model implementation of a Sheet (one workbook contains many sheets) @@ -72,30 +127,30 @@ public final class Sheet implements Model { protected DefaultColWidthRecord defaultcolwidth = null; protected DefaultRowHeightRecord defaultrowheight = null; protected GridsetRecord gridset = null; + private GutsRecord _gutsRecord; protected PrintSetupRecord printSetup = null; protected HeaderRecord header = null; protected FooterRecord footer = null; protected PrintGridlinesRecord printGridlines = null; protected WindowTwoRecord windowTwo = null; - protected MergeCellsRecord merged = null; protected Margin[] margins = null; - protected List mergedRecords = new ArrayList(); - protected int numMergedRegions = 0; + private MergedCellsTable _mergedCellsTable; protected SelectionRecord selection = null; - protected ColumnInfoRecordsAggregate columns = null; + /** always present in this POI object, not always written to Excel file */ + /*package*/ColumnInfoRecordsAggregate _columnInfos; protected ValueRecordsAggregate cells = null; - protected RowRecordsAggregate rows = null; + protected RowRecordsAggregate _rowsAggregate = null; private Iterator valueRecIterator = null; private Iterator rowRecIterator = null; protected int eofLoc = 0; protected ProtectRecord protect = null; - protected PageBreakRecord rowBreaks = null; - protected PageBreakRecord colBreaks = null; + protected PageBreakRecord _rowBreaksRecord; + protected PageBreakRecord _columnBreaksRecord; private DataValidityTable _dataValidityTable= null; protected ObjectProtectRecord objprotect = null; protected ScenarioProtectRecord scenprotect = null; protected PasswordRecord password = null; - protected List condFormatting = new ArrayList(); + private ConditionalFormattingTable condFormatting; /** Add an UncalcedRecord if not true indicating formulas have not been calculated */ protected boolean _isUncalced = false; @@ -139,13 +194,70 @@ public final class Sheet implements Model { Sheet retval = new Sheet(); ArrayList records = new ArrayList(recs.size() / 5); boolean isfirstcell = true; - boolean isfirstrow = true; int bofEofNestingLevel = 0; for (int k = offset; k < recs.size(); k++) { Record rec = ( Record ) recs.get(k); + if (rec.isValue() != (rec instanceof CellValueRecordInterface)) { + if (rec instanceof SharedFormulaRecord) { + + } else { + "".length(); + } + } + if ( rec.getSid() == DBCellRecord.sid ) { + continue; + } + if ( rec.getSid() == IndexRecord.sid ) { + // ignore INDEX record because it is only needed by Excel, + // and POI always re-calculates its contents + continue; + } + if ( rec.getSid() == StringRecord.sid ) { + continue; + } + + if ( rec.getSid() == CFHeaderRecord.sid ) { + RecordStream rs = new RecordStream(recs, k); + retval.condFormatting = new ConditionalFormattingTable(rs); + k += rs.getCountRead()-1; + records.add(retval.condFormatting); + continue; + } + + if (rec.getSid() == ColumnInfoRecord.sid) { + RecordStream rs = new RecordStream(recs, 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); + retval._dataValidityTable = new DataValidityTable(rs); + k += rs.getCountRead() - 1; // TODO - convert this method result to be zero based + records.add(retval._dataValidityTable); + continue; // TODO + } + 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 + } + retval._rowsAggregate.insertRow(row); + continue; + } + if (rec.getSid() == MergeCellsRecord.sid) { + RecordStream rs = new RecordStream(recs, k); + retval._mergedCellsTable = new MergedCellsTable(rs); + records.add(retval._mergedCellsTable); + continue; // TODO + } + if (rec.getSid() == BOFRecord.sid) { bofEofNestingLevel++; @@ -169,53 +281,15 @@ public final class Sheet implements Model { else if (rec.getSid() == DimensionsRecord.sid) { // Make a columns aggregate if one hasn't ready been created. - if (retval.columns == null) + if (retval._columnInfos == null) { - retval.columns = new ColumnInfoRecordsAggregate(); - records.add(retval.columns); + retval._columnInfos = new ColumnInfoRecordsAggregate(); + records.add(retval._columnInfos); } retval.dims = ( DimensionsRecord ) rec; retval.dimsloc = records.size(); } - else if (rec.getSid() == MergeCellsRecord.sid) - { - retval.mergedRecords.add(rec); - retval.merged = ( MergeCellsRecord ) rec; - retval.numMergedRegions += retval.merged.getNumAreas(); - } - else if ( rec.getSid() == CFHeaderRecord.sid ) - { - CFRecordsAggregate cfAgg = CFRecordsAggregate.createCFAggregate(recs, k); - retval.condFormatting.add(cfAgg); - rec = cfAgg; - } - else if ( rec.getSid() == CFRuleRecord.sid ) - { - // Skip it since it is processed by CFRecordsAggregate - rec = null; - } - else if (rec.getSid() == ColumnInfoRecord.sid) - { - ColumnInfoRecord col = (ColumnInfoRecord)rec; - if (retval.columns != null) - { - rec = null; //only add the aggregate once - } - else - { - rec = retval.columns = new ColumnInfoRecordsAggregate(); - } - retval.columns.insertColumn(col); - } - else if (rec.getSid() == DefaultColWidthRecord.sid) - { - retval.defaultcolwidth = ( DefaultColWidthRecord ) rec; - } - else if (rec.getSid() == DefaultRowHeightRecord.sid) - { - retval.defaultrowheight = ( DefaultRowHeightRecord ) rec; - } else if ( rec.isValue() && bofEofNestingLevel == 1 ) { if ( isfirstcell ) @@ -230,22 +304,13 @@ public final class Sheet implements Model { rec = null; } } - else if ( rec.getSid() == StringRecord.sid ) + else if (rec.getSid() == DefaultColWidthRecord.sid) { - rec = null; + retval.defaultcolwidth = ( DefaultColWidthRecord ) rec; } - else if ( rec.getSid() == RowRecord.sid ) + else if (rec.getSid() == DefaultRowHeightRecord.sid) { - RowRecord row = (RowRecord)rec; - if (!isfirstrow) rec = null; //only add the aggregate once - - if ( isfirstrow ) - { - retval.rows = new RowRecordsAggregate(); - rec = retval.rows; - isfirstrow = false; - } - retval.rows.insertRow(row); + retval.defaultrowheight = ( DefaultRowHeightRecord ) rec; } else if ( rec.getSid() == PrintGridlinesRecord.sid ) { @@ -291,22 +356,6 @@ public final class Sheet implements Model { { retval.windowTwo = (WindowTwoRecord) rec; } - else if ( rec.getSid() == DBCellRecord.sid ) - { - rec = null; - } - else if ( rec.getSid() == IndexRecord.sid ) - { - // ignore INDEX record because it is only needed by Excel, - // and POI always re-calculates its contents - rec = null; - } - else if ( rec.getSid() == DVALRecord.sid) { - RecordStream rs = new RecordStream(recs, k); - retval._dataValidityTable = new DataValidityTable(rs); - k += rs.getCountRead() - 1; // TODO - convert this method result to be zero based - rec = retval._dataValidityTable; - } else if ( rec.getSid() == ProtectRecord.sid ) { retval.protect = (ProtectRecord) rec; @@ -323,13 +372,13 @@ public final class Sheet implements Model { { retval.password = (PasswordRecord) rec; } - else if (rec.getSid() == PageBreakRecord.HORIZONTAL_SID) + else if (rec.getSid() == HorizontalPageBreakRecord.sid) { - retval.rowBreaks = (PageBreakRecord)rec; + retval._rowBreaksRecord = (HorizontalPageBreakRecord)rec; } - else if (rec.getSid() == PageBreakRecord.VERTICAL_SID) + else if (rec.getSid() == VerticalPageBreakRecord.sid) { - retval.colBreaks = (PageBreakRecord)rec; + retval._columnBreaksRecord = (VerticalPageBreakRecord)rec; } if (rec != null) @@ -337,6 +386,9 @@ public final class Sheet implements Model { records.add(rec); } } + if (retval.dimsloc < 0) { + throw new RuntimeException("DimensionsRecord was not found"); + } retval.records = records; retval.checkRows(); retval.checkCells(); @@ -345,6 +397,17 @@ public final class Sheet implements Model { return retval; } + private static final class RecordCloner implements RecordVisitor { + + private final List _destList; + + public RecordCloner(List destList) { + _destList = destList; + } + public void visitRecord(Record r) { + _destList.add(r.clone()); + } + } /** * Clones the low level records of this sheet and returns the new sheet instance. * This method is implemented by adding methods for deep cloning to all records that @@ -356,7 +419,13 @@ public final class Sheet implements Model { { ArrayList clonedRecords = new ArrayList(this.records.size()); for (int i=0; i= numMergedRegions || mergedRecords.size() == 0) - return; - - int pos = 0; - int startNumRegions = 0; - - //optimisation for current record - if (numMergedRegions - index < merged.getNumAreas()) - { - pos = mergedRecords.size() - 1; - startNumRegions = numMergedRegions - merged.getNumAreas(); - } - else - { - for (int n = 0; n < mergedRecords.size(); n++) - { - MergeCellsRecord record = (MergeCellsRecord) mergedRecords.get(n); - if (startNumRegions + record.getNumAreas() > index) - { - pos = n; - break; - } - startNumRegions += record.getNumAreas(); - } - } - - MergeCellsRecord rec = (MergeCellsRecord) mergedRecords.get(pos); - rec.removeAreaAt(index - startNumRegions); - numMergedRegions--; - if (rec.getNumAreas() == 0) - { - mergedRecords.remove(pos); - //get rid of the record from the sheet - records.remove(merged); - if (merged == rec) { - //pull up the LAST record for operations when we finally - //support continue records for mergedRegions - if (mergedRecords.size() > 0) { - merged = (MergeCellsRecord) mergedRecords.get(mergedRecords.size() - 1); - } else { - merged = null; - } - } - } + MergedCellsTable mrt = getMergedRecords(); + if (index >= mrt.getNumberOfMergedRegions()) { + return; + } + mrt.remove(index); } - public CellRangeAddress getMergedRegionAt(int index) - { + public CellRangeAddress getMergedRegionAt(int index) { //safety checks - if (index >= numMergedRegions || mergedRecords.size() == 0) - return null; - - int pos = 0; - int startNumRegions = 0; - - //optimisation for current record - if (numMergedRegions - index < merged.getNumAreas()) - { - pos = mergedRecords.size() - 1; - startNumRegions = numMergedRegions - merged.getNumAreas(); - } - else - { - for (int n = 0; n < mergedRecords.size(); n++) - { - MergeCellsRecord record = (MergeCellsRecord) mergedRecords.get(n); - if (startNumRegions + record.getNumAreas() > index) - { - pos = n; - break; - } - startNumRegions += record.getNumAreas(); - } - } - return ((MergeCellsRecord) mergedRecords.get(pos)).getAreaAt(index - startNumRegions); + MergedCellsTable mrt = getMergedRecords(); + if (index >= mrt.getNumberOfMergedRegions()) { + return null; + } + return mrt.get(index); } - public int getNumMergedRegions() - { - return numMergedRegions; + public int getNumMergedRegions() { + return getMergedRecords().getNumberOfMergedRegions(); } - // Find correct position to add new CF record - private int findConditionalFormattingPosition() - { - // This is default. - // If the algorithm does not find the right position, - // this one will be used (this is a position before EOF record) - int index = records.size()-2; - - for( int i=index; i>=0; i-- ) - { - Record rec = (Record)records.get(i); - short sid = rec.getSid(); - - // CFRecordsAggregate records already exist, just add to the end - if (rec instanceof CFRecordsAggregate) { return i+1; } - - if( sid == (short)0x00ef ) { return i+1; }// PHONETICPR - if( sid == (short)0x015f ) { return i+1; }// LABELRANGES - if( sid == MergeCellsRecord.sid ) { return i+1; } - if( sid == (short)0x0099 ) { return i+1; }// STANDARDWIDTH - if( sid == SelectionRecord.sid ) { return i+1; } - if( sid == PaneRecord.sid ) { return i+1; } - if( sid == SCLRecord.sid ) { return i+1; } - if( sid == WindowTwoRecord.sid ) { return i+1; } - } - - return index; + private ConditionalFormattingTable getConditionalFormattingTable() { + if (condFormatting == null) { + condFormatting = new ConditionalFormattingTable(); + RecordOrderer.addNewSheetRecord(records, condFormatting); + } + return condFormatting; } - public int addConditionalFormatting(CFRecordsAggregate cfAggregate) - { - int index = findConditionalFormattingPosition(); - records.add(index, cfAggregate); - condFormatting.add(cfAggregate); - return condFormatting.size()-1; + + public int addConditionalFormatting(CFRecordsAggregate cfAggregate) { + ConditionalFormattingTable cft = getConditionalFormattingTable(); + return cft.add(cfAggregate); } - public void removeConditionalFormatting(int index) - { - if (index >= 0 && index <= condFormatting.size()-1 ) - { - CFRecordsAggregate cfAggregate = getCFRecordsAggregateAt(index); - records.remove(cfAggregate); - condFormatting.remove(index); - } + public void removeConditionalFormatting(int index) { + getConditionalFormattingTable().remove(index); } - public CFRecordsAggregate getCFRecordsAggregateAt(int index) - { - if (index >= 0 && index <= condFormatting.size()-1 ) - { - return (CFRecordsAggregate) condFormatting.get(index); - } - return null; + public CFRecordsAggregate getCFRecordsAggregateAt(int index) { + return getConditionalFormattingTable().get(index); } - public int getNumConditionalFormattings() - { - return condFormatting.size(); + public int getNumConditionalFormattings() { + return getConditionalFormattingTable().size(); } /** @@ -699,13 +675,13 @@ public final class Sheet implements Model { log.logFormatted(POILogger.DEBUG, "returning % + % + % - 2 = %", new int[] { records.size(), cells.getPhysicalNumberOfCells(), - rows.getPhysicalNumberOfRows(), + _rowsAggregate.getPhysicalNumberOfRows(), records.size() + cells.getPhysicalNumberOfCells() - + rows.getPhysicalNumberOfRows() - 2 + + _rowsAggregate.getPhysicalNumberOfRows() - 2 }); } return records.size() + cells.getPhysicalNumberOfCells() - + rows.getPhysicalNumberOfRows() - 2; + + _rowsAggregate.getPhysicalNumberOfRows() - 2; } /** @@ -814,7 +790,7 @@ public final class Sheet implements Model { for (int k = 0; k < records.size(); k++) { - Record record = (( Record ) records.get(k)); + RecordBase record = (RecordBase) records.get(k); // Don't write out UncalcedRecord entries, as // we handle those specially just below @@ -833,7 +809,7 @@ public final class Sheet implements Model { } // If the BOF record was just serialized then add the IndexRecord - if (record.getSid() == BOFRecord.sid) { + if (record instanceof BOFRecord) { if (!haveSerializedIndex) { haveSerializedIndex = true; // Add an optional UncalcedRecord. However, we should add @@ -846,7 +822,7 @@ public final class Sheet implements Model { } //Can there be more than one BOF for a sheet? If not then we can //remove this guard. So be safe it is left here. - if (rows != null) { + if (_rowsAggregate != null) { pos += serializeIndexRecord(k, pos, data); } } @@ -865,8 +841,8 @@ public final class Sheet implements Model { private int serializeIndexRecord(final int bofRecordIndex, final int indexRecordOffset, byte[] data) { IndexRecord index = new IndexRecord(); - index.setFirstRow(rows.getFirstRowNum()); - index.setLastRowAdd1(rows.getLastRowNum() + 1); + index.setFirstRow(_rowsAggregate.getFirstRowNum()); + index.setLastRowAdd1(_rowsAggregate.getLastRowNum() + 1); // Calculate the size of the records from the end of the BOF // and up to the RowRecordsAggregate... @@ -874,7 +850,7 @@ public final class Sheet implements Model { int sizeOfInitialSheetRecords = 0; // start just after BOF record (INDEX is not present in this list) for (int j = bofRecordIndex + 1; j < records.size(); j++) { - Record tmpRec = ((Record) records.get(j)); + RecordBase tmpRec = ((RecordBase) records.get(j)); if (tmpRec instanceof UncalcedRecord) { continue; } @@ -891,7 +867,7 @@ public final class Sheet implements Model { // Note: The offsets are relative to the Workbook BOF. Assume that this is // 0 for now..... - int blockCount = rows.getRowBlockCount(); + int blockCount = _rowsAggregate.getRowBlockCount(); // Calculate the size of this IndexRecord int indexRecSize = IndexRecord.getRecordSizeForBlockCount(blockCount); @@ -902,15 +878,15 @@ public final class Sheet implements Model { // The offset of each DBCELL record needs to be updated in the INDEX record // account for row records in this row-block - currentOffset += rows.getRowBlockSize(block); + currentOffset += _rowsAggregate.getRowBlockSize(block); // account for cell value records after those - currentOffset += null == cells ? 0 : cells.getRowCellBlockSize(rows - .getStartRowNumberForBlock(block), rows.getEndRowNumberForBlock(block)); + currentOffset += null == cells ? 0 : cells.getRowCellBlockSize(_rowsAggregate + .getStartRowNumberForBlock(block), _rowsAggregate.getEndRowNumberForBlock(block)); // currentOffset is now the location of the DBCELL record for this row-block index.addDbcell(currentOffset); // Add space required to write the DBCELL record (whose reference was just added). - currentOffset += (8 + (rows.getRowCountForBlock(block) * 2)); + currentOffset += (8 + (_rowsAggregate.getRowCountForBlock(block) * 2)); } return index.serialize(indexRecordOffset, data); } @@ -1031,11 +1007,11 @@ public final class Sheet implements Model { } //IndexRecord index = null; //If the row exists remove it, so that any cells attached to the row are removed - RowRecord existingRow = rows.getRow(row.getRowNumber()); + RowRecord existingRow = _rowsAggregate.getRow(row.getRowNumber()); if (existingRow != null) - rows.removeRow(existingRow); + _rowsAggregate.removeRow(existingRow); - rows.insertRow(row); + _rowsAggregate.insertRow(row); if (log.check( POILogger.DEBUG )) log.log(POILogger.DEBUG, "exit addRow"); @@ -1054,7 +1030,7 @@ public final class Sheet implements Model { checkRows(); setLoc(getDimsLoc()); - rows.removeRow(row); + _rowsAggregate.removeRow(row); } /** @@ -1108,7 +1084,7 @@ public final class Sheet implements Model { log.log(POILogger.DEBUG, "getNextRow loc= " + loc); if (rowRecIterator == null) { - rowRecIterator = rows.getIterator(); + rowRecIterator = _rowsAggregate.getIterator(); } if (!rowRecIterator.hasNext()) { @@ -1136,7 +1112,7 @@ public final class Sheet implements Model { public RowRecord getRow(int rownum) { if (log.check( POILogger.DEBUG )) log.log(POILogger.DEBUG, "getNextRow loc= " + loc); - return rows.getRow(rownum); + return _rowsAggregate.getRow(rownum); } /** @@ -1258,6 +1234,15 @@ public final class Sheet implements Model { retval.setColLevelMax(( short ) 0); return retval; } + private GutsRecord getGutsRecord() { + if (_gutsRecord == null) { + GutsRecord result = createGuts(); + RecordOrderer.addNewSheetRecord(records, result); + _gutsRecord = result; + } + + return _gutsRecord; + } /** * creates the DefaultRowHeight Record and sets its options to 0 and rowheight to 0xff @@ -1422,43 +1407,22 @@ public final class Sheet implements Model { /** * get the width of a given column in units of 1/256th of a character width - * @param column index + * @param columnIndex index * @see org.apache.poi.hssf.record.DefaultColWidthRecord * @see org.apache.poi.hssf.record.ColumnInfoRecord * @see #setColumnWidth(short,short) * @return column width in units of 1/256th of a character width */ - public short getColumnWidth(short column) - { - short retval = 0; - ColumnInfoRecord ci = null; + public short getColumnWidth(short columnIndex) { - if (columns != null) - { - int count=columns.getNumColumns(); - for ( int k=0;k

- * This class is just used so that SID compares work properly in the RecordFactory - * @see PageBreakRecord - * @author Danny Mui (dmui at apache dot org) - */ -public class HorizontalPageBreakRecord extends PageBreakRecord { +import java.util.Iterator; + +/** + * HorizontalPageBreak (0x001B) record that stores page breaks at rows

+ * + * @see PageBreakRecord + * @author Danny Mui (dmui at apache dot org) + */ +public final class HorizontalPageBreakRecord extends PageBreakRecord { + + public static final short sid = 0x001B; - public static final short sid = PageBreakRecord.HORIZONTAL_SID; - /** - * + * Creates an empty horizontal page break record */ public HorizontalPageBreakRecord() { - super(); + // } /** - * @param sid - */ - public HorizontalPageBreakRecord(short sid) { - super(sid); - } - - /** - * @param in the RecordInputstream to read the record from + * @param in + * the RecordInputstream to read the record from */ public HorizontalPageBreakRecord(RecordInputStream in) { super(in); } - /* (non-Javadoc) - * @see org.apache.poi.hssf.record.Record#getSid() - */ + protected void validateSid(short id) { + if (id != getSid()) { + throw new RecordFormatException( + "NOT A HorizontalPageBreak or VerticalPageBreak RECORD!! " + id); + } + } + public short getSid() { return sid; } + public Object clone() { + PageBreakRecord result = new HorizontalPageBreakRecord(); + Iterator iterator = getBreaksIterator(); + while (iterator.hasNext()) { + Break original = (Break) iterator.next(); + result.addBreak(original.main, original.subFrom, original.subTo); + } + return result; + } } diff --git a/src/java/org/apache/poi/hssf/record/MergeCellsRecord.java b/src/java/org/apache/poi/hssf/record/MergeCellsRecord.java index bf41d2fc9..27ebbd118 100644 --- a/src/java/org/apache/poi/hssf/record/MergeCellsRecord.java +++ b/src/java/org/apache/poi/hssf/record/MergeCellsRecord.java @@ -32,68 +32,51 @@ import org.apache.poi.util.LittleEndian; */ public final class MergeCellsRecord extends Record { public final static short sid = 0x00E5; - private CellRangeAddressList _regions; + /** sometimes the regions array is shared with other MergedCellsRecords */ + private CellRangeAddress[] _regions; + private final int _startIndex; + private final int _numberOfRegions; - /** - * Creates an empty MergedCellsRecord - */ - public MergeCellsRecord() { - _regions = new CellRangeAddressList(); + public MergeCellsRecord(CellRangeAddress[] regions, int startIndex, int numberOfRegions) { + _regions = regions; + _startIndex = startIndex; + _numberOfRegions = numberOfRegions; } - /** * Constructs a MergedCellsRecord and sets its fields appropriately * @param in the RecordInputstream to read the record from */ public MergeCellsRecord(RecordInputStream in) { - super(in); + int nRegions = in.readUShort(); + CellRangeAddress[] cras = new CellRangeAddress[nRegions]; + for (int i = 0; i < nRegions; i++) { + cras[i] = new CellRangeAddress(in); + } + _numberOfRegions = nRegions; + _startIndex = 0; + _regions = cras; } - protected void fillFields(RecordInputStream in) { - _regions = new CellRangeAddressList(in); + throw new RuntimeException("obsolete"); } - /** * get the number of merged areas. If this drops down to 0 you should just go * ahead and delete the record. * @return number of areas */ public short getNumAreas() { - return (short)_regions.countRanges(); - } - - /** - * Add an area to consider a merged cell. The index returned is only gauranteed to - * be correct provided you do not add ahead of or remove ahead of it (in which case - * you should increment or decrement appropriately....in other words its an arrayList) - * - * @param firstRow - the upper left hand corner's row - * @param firstCol - the upper left hand corner's col - * @param lastRow - the lower right hand corner's row - * @param lastCol - the lower right hand corner's col - * @return new index of said area (don't depend on it if you add/remove) - */ - public void addArea(int firstRow, int firstCol, int lastRow, int lastCol) { - _regions.addCellRangeAddress(firstRow, firstCol, lastRow, lastCol); - } - - /** - * essentially unmerge the cells in the "area" stored at the passed in index - * @param areaIndex - */ - public void removeAreaAt(int areaIndex) { - _regions.remove(areaIndex); + return (short)_numberOfRegions; } /** * @return MergedRegion at the given index representing the area that is Merged (r1,c1 - r2,c2) */ public CellRangeAddress getAreaAt(int index) { - return _regions.getCellRangeAddress(index); + return _regions[_startIndex + index]; } public int getRecordSize() { - return 4 + _regions.getSize(); + return 4 + CellRangeAddressList.getEncodedSize(_numberOfRegions); } public short getSid() { @@ -101,11 +84,16 @@ public final class MergeCellsRecord extends Record { } public int serialize(int offset, byte [] data) { - int dataSize = _regions.getSize(); + int dataSize = CellRangeAddressList.getEncodedSize(_numberOfRegions); - LittleEndian.putShort(data, offset + 0, sid); + LittleEndian.putUShort(data, offset + 0, sid); LittleEndian.putUShort(data, offset + 2, dataSize); - _regions.serialize(offset + 4, data); + int nItems = _numberOfRegions; + LittleEndian.putUShort(data, offset + 4, nItems); + int pos = 6; + for (int i = 0; i < _numberOfRegions; i++) { + pos += _regions[_startIndex + i].serialize(offset+pos, data); + } return 4 + dataSize; } @@ -113,17 +101,16 @@ public final class MergeCellsRecord extends Record { StringBuffer retval = new StringBuffer(); retval.append("[MERGEDCELLS]").append("\n"); - retval.append(" .sid =").append(sid).append("\n"); retval.append(" .numregions =").append(getNumAreas()) .append("\n"); - for (int k = 0; k < _regions.countRanges(); k++) { - CellRangeAddress region = _regions.getCellRangeAddress(k); + for (int k = 0; k < _numberOfRegions; k++) { + CellRangeAddress region = _regions[_startIndex + k]; retval.append(" .rowfrom =").append(region.getFirstRow()) .append("\n"); - retval.append(" .colfrom =").append(region.getFirstColumn()) - .append("\n"); retval.append(" .rowto =").append(region.getLastRow()) + .append("\n"); + retval.append(" .colfrom =").append(region.getFirstColumn()) .append("\n"); retval.append(" .colto =").append(region.getLastColumn()) .append("\n"); @@ -140,13 +127,11 @@ public final class MergeCellsRecord extends Record { } public Object clone() { - MergeCellsRecord rec = new MergeCellsRecord(); - for (int k = 0; k < _regions.countRanges(); k++) { - CellRangeAddress oldRegion = _regions.getCellRangeAddress(k); - rec.addArea(oldRegion.getFirstRow(), oldRegion.getFirstColumn(), - oldRegion.getLastRow(), oldRegion.getLastColumn()); - } - - return rec; + int nRegions = _numberOfRegions; + CellRangeAddress[] clonedRegions = new CellRangeAddress[nRegions]; + for (int i = 0; i < clonedRegions.length; i++) { + clonedRegions[i] = _regions[_startIndex + i].copy(); + } + return new MergeCellsRecord(clonedRegions, 0, nRegions); } } diff --git a/src/java/org/apache/poi/hssf/record/PageBreakRecord.java b/src/java/org/apache/poi/hssf/record/PageBreakRecord.java index 83eade95d..c86f460b1 100644 --- a/src/java/org/apache/poi/hssf/record/PageBreakRecord.java +++ b/src/java/org/apache/poi/hssf/record/PageBreakRecord.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,11 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.hssf.record; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -38,13 +36,12 @@ import org.apache.poi.util.LittleEndian; * @see VerticalPageBreakRecord * @author Danny Mui (dmui at apache dot org) */ -public class PageBreakRecord extends Record { - public static final short HORIZONTAL_SID = (short)0x1B; - public static final short VERTICAL_SID = (short)0x1A; - public short sid; - private short numBreaks; - private List breaks; - private Map BreakMap; +public abstract class PageBreakRecord extends Record { + private static final boolean IS_EMPTY_RECORD_WRITTEN = false; //TODO - flip + private static final int[] EMPTY_INT_ARRAY = { }; + + private List _breaks; + private Map _breakMap; /** * Since both records store 2byte integers (short), no point in @@ -53,116 +50,105 @@ public class PageBreakRecord extends Record { * The subs (rows or columns, don't seem to be able to set but excel sets * them automatically) */ - public class Break - { + public class Break { - public short main; - public short subFrom; - public short subTo; + public static final int ENCODED_SIZE = 6; + public int main; + public int subFrom; + public int subTo; - public Break(short main, short subFrom, short subTo) + public Break(int main, int subFrom, int subTo) { this.main = main; this.subFrom = subFrom; this.subTo = subTo; } + + public Break(RecordInputStream in) { + main = in.readUShort() - 1; + subFrom = in.readUShort(); + subTo = in.readUShort(); + } + + public int serialize(int offset, byte[] data) { + LittleEndian.putUShort(data, offset + 0, main + 1); + LittleEndian.putUShort(data, offset + 2, subFrom); + LittleEndian.putUShort(data, offset + 4, subTo); + return ENCODED_SIZE; + } } - public PageBreakRecord() - { - + protected PageBreakRecord() { + _breaks = new ArrayList(); + _breakMap = new HashMap(); } - /** - * - * @param sid - */ - public PageBreakRecord(short sid) { - super(); - this.sid = sid; - } - - public PageBreakRecord(RecordInputStream in) - { + protected PageBreakRecord(RecordInputStream in) { super(in); - this.sid = in.getSid(); } protected void fillFields(RecordInputStream in) { - short loadedBreaks = in.readShort(); - setNumBreaks(loadedBreaks); - for(int k = 0; k < loadedBreaks; k++) - { - addBreak((short)(in.readShort()-1), in.readShort(), in.readShort()); + int nBreaks = in.readShort(); + _breaks = new ArrayList(nBreaks + 2); + _breakMap = new HashMap(); + + for(int k = 0; k < nBreaks; k++) { + Break br = new Break(in); + _breaks.add(br); + _breakMap.put(new Integer(br.main), br); } } - - public short getSid() - { - return sid; + + private int getDataSize() { + return 2 + _breaks.size() * Break.ENCODED_SIZE; + } + public int getRecordSize() { + int nBreaks = _breaks.size(); + if (!IS_EMPTY_RECORD_WRITTEN && nBreaks < 1) { + return 0; + } + return 4 + getDataSize(); } - public int serialize(int offset, byte data[]) - { - int recordsize = getRecordSize(); + + public final int serialize(int offset, byte data[]) { + int nBreaks = _breaks.size(); + if (!IS_EMPTY_RECORD_WRITTEN && nBreaks < 1) { + return 0; + } + int dataSize = getDataSize(); + LittleEndian.putUShort(data, offset + 0, getSid()); + LittleEndian.putUShort(data, offset + 2, dataSize); + LittleEndian.putUShort(data, offset + 4, nBreaks); int pos = 6; - LittleEndian.putShort(data, offset + 0, getSid()); - LittleEndian.putShort(data, offset + 2, (short)(recordsize - 4)); - LittleEndian.putShort(data, offset + 4, getNumBreaks()); - for(Iterator iterator = getBreaksIterator(); iterator.hasNext();) - { - Break Break = (Break)iterator.next(); - LittleEndian.putShort(data, offset + pos, (short)(Break.main + 1)); - pos += 2; - LittleEndian.putShort(data, offset + pos, Break.subFrom); - pos += 2; - LittleEndian.putShort(data, offset + pos, Break.subTo); - pos += 2; + for (int i=0; i - * This class is just used so that SID compares work properly in the RecordFactory + * VerticalPageBreak (0x001A) record that stores page breaks at columns

+ * * @see PageBreakRecord - * @author Danny Mui (dmui at apache dot org) + * @author Danny Mui (dmui at apache dot org) */ -public class VerticalPageBreakRecord extends PageBreakRecord { - - public static final short sid = PageBreakRecord.VERTICAL_SID; - +public final class VerticalPageBreakRecord extends PageBreakRecord { + + public static final short sid = 0x001A; + /** - * + * Creates an empty vertical page break record */ public VerticalPageBreakRecord() { - super(); + } /** - * @param sid - */ - public VerticalPageBreakRecord(short sid) { - super(sid); - } - - /** - * @param in the RecordInputstream to read the record from + * @param in the RecordInputstream to read the record from */ public VerticalPageBreakRecord(RecordInputStream in) { super(in); } - /* (non-Javadoc) - * @see org.apache.poi.hssf.record.Record#getSid() - */ + protected void validateSid(short id) { + if (id != getSid()) { + throw new RecordFormatException( + "NOT A HorizontalPageBreak or VerticalPageBreak RECORD!! " + id); + } + } + public short getSid() { return sid; } + public Object clone() { + PageBreakRecord result = new VerticalPageBreakRecord(); + Iterator iterator = getBreaksIterator(); + while (iterator.hasNext()) { + Break original = (Break) iterator.next(); + result.addBreak(original.main, original.subFrom, original.subTo); + } + return result; + } } diff --git a/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java index 71754db96..eb117eae4 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/CFRecordsAggregate.java @@ -14,19 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ + package org.apache.poi.hssf.record.aggregates; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import org.apache.poi.hssf.model.RecordStream; import org.apache.poi.hssf.record.CFHeaderRecord; import org.apache.poi.hssf.record.CFRuleRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.util.CellRangeAddress; -import org.apache.poi.util.POILogFactory; -import org.apache.poi.util.POILogger; /** * CFRecordsAggregate - aggregates Conditional Formatting records CFHeaderRecord @@ -36,15 +36,12 @@ import org.apache.poi.util.POILogger; * @author Dmitriy Kumshayev * */ -public final class CFRecordsAggregate extends Record -{ +public final class CFRecordsAggregate extends Record { /** Excel allows up to 3 conditional formating rules */ private static final int MAX_CONDTIONAL_FORMAT_RULES = 3; public final static short sid = -2008; // not a real BIFF record - private static POILogger log = POILogFactory.getLogger(CFRecordsAggregate.class); - private final CFHeaderRecord header; /** List of CFRuleRecord objects */ @@ -78,9 +75,8 @@ public final class CFRecordsAggregate extends Record * @param offset - position of {@link CFHeaderRecord} object in the list of Record objects * @return CFRecordsAggregate object */ - public static CFRecordsAggregate createCFAggregate(List recs, int pOffset) - { - Record rec = ( Record ) recs.get(pOffset); + public static CFRecordsAggregate createCFAggregate(RecordStream rs) { + Record rec = rs.getNext(); if (rec.getSid() != CFHeaderRecord.sid) { throw new IllegalStateException("next record sid was " + rec.getSid() + " instead of " + CFHeaderRecord.sid + " as expected"); @@ -90,35 +86,10 @@ public final class CFRecordsAggregate extends Record int nRules = header.getNumberOfConditionalFormats(); CFRuleRecord[] rules = new CFRuleRecord[nRules]; - int offset = pOffset; - int countFound = 0; - while (countFound < rules.length) { - offset++; - if(offset>=recs.size()) { - break; - } - rec = (Record)recs.get(offset); - if(rec instanceof CFRuleRecord) { - rules[countFound] = (CFRuleRecord) rec; - countFound++; - } else { - break; - } - } - - if (countFound < nRules) - { // TODO -(MAR-2008) can this ever happen? write junit - - if (log.check(POILogger.DEBUG)) - { - log.log(POILogger.DEBUG, "Expected " + nRules + " Conditional Formats, " - + "but found " + countFound + " rules"); - } - header.setNumberOfConditionalFormats(nRules); - CFRuleRecord[] lessRules = new CFRuleRecord[countFound]; - System.arraycopy(rules, 0, lessRules, 0, countFound); - rules = lessRules; + for (int i = 0; i < rules.length; i++) { + rules[i] = (CFRuleRecord) rs.getNext(); } + return new CFRecordsAggregate(header, rules); } diff --git a/src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java b/src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java index 6df796c2a..b24d8c5b4 100644 --- a/src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java +++ b/src/java/org/apache/poi/hssf/record/aggregates/ColumnInfoRecordsAggregate.java @@ -1,72 +1,52 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + package org.apache.poi.hssf.record.aggregates; +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.hssf.model.RecordStream; import org.apache.poi.hssf.record.ColumnInfoRecord; import org.apache.poi.hssf.record.Record; -import org.apache.poi.hssf.record.RecordInputStream; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; /** * @author Glen Stampoultzis * @version $Id$ */ -public class ColumnInfoRecordsAggregate - extends Record -{ -// int size = 0; - List records = null; +public final class ColumnInfoRecordsAggregate extends RecordAggregate { + private final List records; - public ColumnInfoRecordsAggregate() - { - records = new ArrayList(); - } - - /** You never fill an aggregate */ - protected void fillFields(RecordInputStream in) - { - } - - /** Not required by an aggregate */ - protected void validateSid(short id) - { - } - - /** It's an aggregate... just made something up */ - public short getSid() - { - return -1012; - } - - public int getRecordSize() - { - int size = 0; - for ( Iterator iterator = records.iterator(); iterator.hasNext(); ) - size += ( (ColumnInfoRecord) iterator.next() ).getRecordSize(); - return size; - } - - public Iterator getIterator() - { - return records.iterator(); - } + /** + * Creates an empty aggregate + */ + public ColumnInfoRecordsAggregate() { + records = new ArrayList(); + } + public ColumnInfoRecordsAggregate(RecordStream rs) { + this(); + + while(rs.peekNextClass() == ColumnInfoRecord.class) { + records.add(rs.getNext()); + } + if (records.size() < 1) { + throw new RuntimeException("No column info records found"); + } + } /** * Performs a deep clone of the record @@ -105,25 +85,14 @@ public class ColumnInfoRecordsAggregate return records.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 offset to begin writing at - * @param data byte array containing instance data - * @return number of bytes written - */ - public int serialize(int offset, byte [] data) - { - Iterator itr = records.iterator(); - int pos = offset; - - while (itr.hasNext()) - { - pos += (( Record ) itr.next()).serialize(pos, data); - } - return pos - offset; + public void visitContainedRecords(RecordVisitor rv) { + int nItems = records.size(); + if (nItems < 1) { + return; + } + for(int i=0; i