--- /dev/null
+/* ====================================================================
+ 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<p/>
+ *
+ * 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<nRecs; i++) {
+ if(records.get(i) instanceof DimensionsRecord) {
+ return i;
+ }
+ }
+ // worksheet stream is seriously broken
+ throw new RuntimeException("DimensionsRecord not found");
+ }
+
+ private static int getGutsRecordInsertPos(List records) {
+ int dimensionsIndex = getDimensionsIndex(records);
+ int i = dimensionsIndex-1;
+ while (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;
+ }
+}
package org.apache.poi.hssf.model;
-import org.apache.poi.hssf.record.*; // normally I don't do this, buy we literally mean ALL
+import java.util.ArrayList;
+import java.util.Iterator;
+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.CFRecordsAggregate;
+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;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
/**
* Low level model implementation of a Sheet (one workbook contains many sheets)
* This file contains the low level binary records starting at the sheets BOF and
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;
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++;
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 )
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 )
{
{
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;
{
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)
records.add(rec);
}
}
+ if (retval.dimsloc < 0) {
+ throw new RuntimeException("DimensionsRecord was not found");
+ }
retval.records = records;
retval.checkRows();
retval.checkCells();
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
{
ArrayList clonedRecords = new ArrayList(this.records.size());
for (int i=0; i<this.records.size();i++) {
- Record rec = (Record)((Record)this.records.get(i)).clone();
+ RecordBase rb = (RecordBase) this.records.get(i);
+ if (rb instanceof RecordAggregate) {
+ ((RecordAggregate)rb).visitContainedRecords(new RecordCloner(clonedRecords));
+ // TODO - make sure this logic works for the other RecordAggregates
+ continue;
+ }
+ Record rec = (Record)((Record)rb).clone();
//Need to pull out the Row record and the Value records from their
//Aggregates.
//This is probably the best way to do it since we probably dont want the createSheet
records.add( retval.createWSBool() );
// 'Page Settings Block'
- retval.rowBreaks = new PageBreakRecord(PageBreakRecord.HORIZONTAL_SID);
- records.add(retval.rowBreaks);
- retval.colBreaks = new PageBreakRecord(PageBreakRecord.VERTICAL_SID);
- records.add(retval.colBreaks);
+ retval._rowBreaksRecord = new HorizontalPageBreakRecord();
+ records.add(retval._rowBreaksRecord);
+ retval._columnBreaksRecord = new VerticalPageBreakRecord();
+ records.add(retval._columnBreaksRecord);
retval.header = createHeader();
records.add( retval.header );
records.add( retval.defaultcolwidth);
ColumnInfoRecordsAggregate columns = new ColumnInfoRecordsAggregate();
records.add( columns );
- retval.columns = columns;
+ retval._columnInfos = columns;
retval.dims = createDimensions();
records.add(retval.dims);
retval.dimsloc = records.size()-1;
private void checkRows()
{
- if (rows == null)
+ if (_rowsAggregate == null)
{
- rows = new RowRecordsAggregate();
- records.add(getDimsLoc() + 1, rows);
+ _rowsAggregate = new RowRecordsAggregate();
+ records.add(getDimsLoc() + 1, _rowsAggregate);
}
}
+ private MergedCellsTable getMergedRecords() {
+ if (_mergedCellsTable == null) {
+ MergedCellsTable mct = new MergedCellsTable();
+ RecordOrderer.addNewSheetRecord(records, mct);
+ _mergedCellsTable = mct;
+ }
+ return _mergedCellsTable;
+ }
- public int addMergedRegion(int rowFrom, int colFrom, int rowTo, int colTo) {
+
+ public int addMergedRegion(int rowFrom, int colFrom, int rowTo, int colTo) {
// Validate input
if (rowTo < rowFrom) {
throw new IllegalArgumentException("The 'to' row (" + rowTo
+ ") must not be less than the 'from' col (" + colFrom + ")");
}
- if (merged == null || merged.getNumAreas() == 1027)
- {
- merged = createMergedCells();
- mergedRecords.add(merged);
- records.add(records.size() - 1, merged);
- }
- merged.addArea(rowFrom, colFrom, rowTo, colTo);
- return numMergedRegions++;
+ MergedCellsTable mrt = getMergedRecords();
+ mrt.addArea(rowFrom, colFrom, rowTo, colTo);
+ return mrt.getNumberOfMergedRegions()-1;
}
public void removeMergedRegion(int index)
{
//safety checks
- if (index >= 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();
+ }
+ private ConditionalFormattingTable getConditionalFormattingTable() {
+ if (condFormatting == null) {
+ condFormatting = new ConditionalFormattingTable();
+ RecordOrderer.addNewSheetRecord(records, condFormatting);
+ }
+ return condFormatting;
}
- // 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;
+ public int addConditionalFormatting(CFRecordsAggregate cfAggregate) {
+ ConditionalFormattingTable cft = getConditionalFormattingTable();
+ return cft.add(cfAggregate);
}
- public int addConditionalFormatting(CFRecordsAggregate cfAggregate)
- {
- int index = findConditionalFormattingPosition();
- records.add(index, cfAggregate);
- condFormatting.add(cfAggregate);
- return condFormatting.size()-1;
+ public void removeConditionalFormatting(int index) {
+ getConditionalFormattingTable().remove(index);
}
- public void removeConditionalFormatting(int index)
- {
- if (index >= 0 && index <= condFormatting.size()-1 )
- {
- CFRecordsAggregate cfAggregate = getCFRecordsAggregateAt(index);
- records.remove(cfAggregate);
- condFormatting.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();
}
/**
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;
}
/**
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
}
// 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
}
//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);
}
}
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...
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;
}
// 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);
// 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);
}
}
//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");
checkRows();
setLoc(getDimsLoc());
- rows.removeRow(row);
+ _rowsAggregate.removeRow(row);
}
/**
log.log(POILogger.DEBUG, "getNextRow loc= " + loc);
if (rowRecIterator == null)
{
- rowRecIterator = rows.getIterator();
+ rowRecIterator = _rowsAggregate.getIterator();
}
if (!rowRecIterator.hasNext())
{
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);
}
/**
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
/**
* 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<count;k++ )
- {
- ci = columns.getColInfo(k);
- if ((ci.getFirstColumn() <= column)
- && (column <= ci.getLastColumn()))
- {
- break;
- }
- ci = null;
- }
+ ColumnInfoRecord ci = _columnInfos.findColumnInfo(columnIndex);
+ if (ci != null) {
+ return ci.getColumnWidth();
}
- if (ci != null)
- {
- retval = ci.getColumnWidth();
- }
- else
- {
- //default column width is measured in characters
- //multiply
- retval = (short)(256*defaultcolwidth.getColWidth());
- }
- return retval;
+ //default column width is measured in characters
+ //multiply
+ return (short)(256*defaultcolwidth.getColWidth());
}
/**
* Returns the index to the default ExtendedFormatRecord (0xF)
* if no ColumnInfoRecord exists that includes the column
* index specified.
- * @param column
+ * @param columnIndex
* @return index of ExtendedFormatRecord associated with
* ColumnInfoRecord that includes the column index or the
* index of the default ExtendedFormatRecord (0xF)
*/
- public short getXFIndexForColAt(short column) {
- short retval = 0;
- ColumnInfoRecord ci = null;
- if (columns != null) {
- int count=columns.getNumColumns();
- for ( int k=0;k<count;k++ )
- {
- ci = columns.getColInfo(k);
- if ((ci.getFirstColumn() <= column)
- && (column <= ci.getLastColumn())) {
- break;
- }
- ci = null;
- }
- }
- retval = (ci != null) ? ci.getXFIndex() : 0xF;
- return retval;
- }
-
- /**
- * set the width for a given column in 1/256th of a character width units
- * @param column - the column number
- * @param width (in units of 1/256th of a character width)
- */
- public void setColumnWidth(short column, short width)
- {
+ public short getXFIndexForColAt(short columnIndex) {
+ ColumnInfoRecord ci = _columnInfos.findColumnInfo(columnIndex);
+ if (ci != null) {
+ return ci.getXFIndex();
+ }
+ return 0xF;
+ }
+
+ /**
+ * set the width for a given column in 1/256th of a character width units
+ *
+ * @param column -
+ * the column number
+ * @param width
+ * (in units of 1/256th of a character width)
+ */
+ public void setColumnWidth(short column, short width) {
setColumn( column, new Short(width), null, null, null);
}
* @see #setColumnHidden(short,boolean)
* @return whether the column is hidden or not.
*/
-
- public boolean isColumnHidden(short column)
- {
- boolean retval = false;
- ColumnInfoRecord ci = null;
-
- if (columns != null)
- {
- for ( Iterator iterator = columns.getIterator(); iterator.hasNext(); )
- {
- ci = ( ColumnInfoRecord ) iterator.next();
- if ((ci.getFirstColumn() <= column)
- && (column <= ci.getLastColumn()))
- {
- break;
- }
- ci = null;
- }
- }
- if (ci != null)
- {
- retval = ci.getHidden();
- }
- return retval;
+ public boolean isColumnHidden(short columnIndex) {
+ ColumnInfoRecord cir = _columnInfos.findColumnInfo(columnIndex);
+ if (cir == null) {
+ return false;
+ }
+ return cir.getHidden();
}
/**
setColumn( column, null, null, new Boolean(hidden), null);
}
- public void setColumn(short column, Short width, Integer level, Boolean hidden, Boolean collapsed)
- {
- if (columns == null)
- columns = new ColumnInfoRecordsAggregate();
-
- columns.setColumn( column, null, width, level, hidden, collapsed );
+ public void setColumn(short column, Short width, Integer level, Boolean hidden, Boolean collapsed) {
+ _columnInfos.setColumn( column, null, width, level, hidden, collapsed );
}
- public void setColumn(short column, Short xfStyle, Short width, Integer level, Boolean hidden, Boolean collapsed)
- {
- if (columns == null)
- columns = new ColumnInfoRecordsAggregate();
-
- columns.setColumn( column, xfStyle, width, level, hidden, collapsed );
+ public void setColumn(short column, Short xfStyle, Short width, Integer level, Boolean hidden, Boolean collapsed) {
+ _columnInfos.setColumn( column, xfStyle, width, level, hidden, collapsed );
}
{
// Set the level for each column
- columns.groupColumnRange( fromColumn, toColumn, indent);
+ _columnInfos.groupColumnRange( fromColumn, toColumn, indent);
// Determine the maximum overall level
- int maxLevel = 0;
- int count=columns.getNumColumns();
- for ( int k=0;k<count;k++ )
- {
- ColumnInfoRecord columnInfoRecord = columns.getColInfo(k);
- maxLevel = Math.max(columnInfoRecord.getOutlineLevel(), maxLevel);
- }
+ int maxLevel = _columnInfos.getMaxOutlineLevel();
- GutsRecord guts = (GutsRecord) findFirstRecordBySid( GutsRecord.sid );
+ GutsRecord guts = getGutsRecord();
guts.setColLevelMax( (short) ( maxLevel+1 ) );
if (maxLevel == 0) {
guts.setTopColGutter( (short)0 );
}
}
- private static MergeCellsRecord createMergedCells() {
- return new MergeCellsRecord();
- }
-
/**
* get the location of the DimensionsRecord (which is the last record before the value section)
* @return location in the array of records of the DimensionsRecord
}
}
+ /**
+ * @return the serialized size of this sheet
+ */
public int getSize()
{
int retval = 0;
for ( int k = 0; k < records.size(); k++) {
- Record record = (Record) records.get(k);
+ RecordBase record = (RecordBase) records.get(k);
if (record instanceof UncalcedRecord) {
// skip the UncalcedRecord if present, it's only encoded if the isUncalced flag is set
continue;
}
retval += record.getRecordSize();
}
- if (rows != null) {
+ if (_rowsAggregate != null) {
// Add space for the IndexRecord and DBCell records
- final int nBlocks = rows.getRowBlockCount();
+ final int nBlocks = _rowsAggregate.getRowBlockCount();
int nRows = 0;
if (cells != null) {
- for (Iterator itr = rows.getIterator(); itr.hasNext();) {
+ for (Iterator itr = _rowsAggregate.getIterator(); itr.hasNext();) {
RowRecord row = (RowRecord)itr.next();
if (cells.rowHasCells(row.getRowNumber())) {
nRows++;
public Record findFirstRecordBySid(short sid)
{
- for (Iterator iterator = records.iterator(); iterator.hasNext(); )
- {
- Record record = ( Record ) iterator.next();
-
- if (record.getSid() == sid)
- {
- return record;
- }
- }
- return null;
+ int ix = findFirstRecordLocBySid(sid);
+ if (ix < 0) {
+ return null;
+ }
+ return (Record) records.get(ix);
}
/**
}
/**
- * Finds the first occurance of a record matching a particular sid and
+ * Finds the first occurrence of a record matching a particular sid and
* returns it's position.
* @param sid the sid to search for
* @return the record position of the matching record or -1 if no match
* is made.
*/
- public int findFirstRecordLocBySid( short sid )
- {
- int index = 0;
- for (Iterator iterator = records.iterator(); iterator.hasNext(); )
- {
- Record record = ( Record ) iterator.next();
-
- if (record.getSid() == sid)
- {
- return index;
+ public int findFirstRecordLocBySid( short sid ) { // TODO - remove this method
+ int max = records.size();
+ for (int i=0; i< max; i++) {
+ Object rb = records.get(i);
+ if (!(rb instanceof Record)) {
+ continue;
+ }
+ Record record = (Record) rb;
+ if (record.getSid() == sid) {
+ return i;
}
- index++;
}
return -1;
}
{
for ( Iterator iterator = getRecords().iterator(); iterator.hasNext(); )
{
- Record r = (Record) iterator.next();
+ RecordBase r = (RecordBase) iterator.next();
if (r instanceof EscherAggregate)
r.getRecordSize(); // Trigger flatterning of user model and corresponding update of dgg record.
}
* @param stop Ending "main" value to shift breaks
* @param count number of units (rows/columns) to shift by
*/
- public void shiftBreaks(PageBreakRecord breaks, short start, short stop, int count) {
+ private static void shiftBreaks(PageBreakRecord breaks, int start, int stop, int count) {
- if(rowBreaks == null)
- return;
Iterator iterator = breaks.getBreaksIterator();
List shiftedBreak = new ArrayList();
while(iterator.hasNext())
{
PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
- short breakLocation = breakItem.main;
+ int breakLocation = breakItem.main;
boolean inStart = (breakLocation >= start);
boolean inEnd = (breakLocation <= stop);
if(inStart && inEnd)
}
}
+ private PageBreakRecord getRowBreaksRecord() {
+ if (_rowBreaksRecord == null) {
+ _rowBreaksRecord = new HorizontalPageBreakRecord();
+ RecordOrderer.addNewSheetRecord(records, _rowBreaksRecord);
+ dimsloc++;
+ }
+ return _rowBreaksRecord;
+ }
+
+ private PageBreakRecord getColumnBreaksRecord() {
+ if (_columnBreaksRecord == null) {
+ _columnBreaksRecord = new VerticalPageBreakRecord();
+ RecordOrderer.addNewSheetRecord(records, _columnBreaksRecord);
+ dimsloc++;
+ }
+ return _columnBreaksRecord;
+ }
+
+
/**
* Sets a page break at the indicated row
* @param row
*/
public void setRowBreak(int row, short fromCol, short toCol) {
- if (rowBreaks == null) {
- int loc = findFirstRecordLocBySid(WindowTwoRecord.sid);
- rowBreaks = new PageBreakRecord(PageBreakRecord.HORIZONTAL_SID);
- records.add(loc, rowBreaks);
- }
- rowBreaks.addBreak((short)row, fromCol, toCol);
+ getRowBreaksRecord().addBreak((short)row, fromCol, toCol);
}
/**
* @param row
*/
public void removeRowBreak(int row) {
- if (rowBreaks == null)
+ if (getRowBreaks() == null)
throw new IllegalArgumentException("Sheet does not define any row breaks");
- rowBreaks.removeBreak((short)row);
+ getRowBreaksRecord().removeBreak((short)row);
}
/**
* @return true if the specified row has a page break
*/
public boolean isRowBroken(int row) {
- return (rowBreaks == null) ? false : rowBreaks.getBreak((short)row) != null;
+ return getRowBreaksRecord().getBreak(row) != null;
}
/**
*
*/
public void setColumnBreak(short column, short fromRow, short toRow) {
- if (colBreaks == null) {
- int loc = findFirstRecordLocBySid(WindowTwoRecord.sid);
- colBreaks = new PageBreakRecord(PageBreakRecord.VERTICAL_SID);
- records.add(loc, colBreaks);
- }
- colBreaks.addBreak(column, fromRow, toRow);
+ getColumnBreaksRecord().addBreak(column, fromRow, toRow);
}
/**
*
*/
public void removeColumnBreak(short column) {
- if (colBreaks == null)
- throw new IllegalArgumentException("Sheet does not define any column breaks");
-
- colBreaks.removeBreak(column);
+ getColumnBreaksRecord().removeBreak(column);
}
/**
* Queries if the specified column has a page break
*
- * @return true if the specified column has a page break
+ * @return <code>true</code> if the specified column has a page break
*/
public boolean isColumnBroken(short column) {
- return (colBreaks == null) ? false : colBreaks.getBreak(column) != null;
+ return getColumnBreaksRecord().getBreak(column) != null;
}
/**
* @param count
*/
public void shiftRowBreaks(int startingRow, int endingRow, int count) {
- shiftBreaks(rowBreaks, (short)startingRow, (short)endingRow, (short)count);
+ shiftBreaks(getRowBreaksRecord(), startingRow, endingRow, count);
}
/**
* @param count
*/
public void shiftColumnBreaks(short startingCol, short endingCol, short count) {
- shiftBreaks(colBreaks, startingCol, endingCol, count);
+ shiftBreaks(getColumnBreaksRecord(), startingCol, endingCol, count);
}
/**
- * Returns all the row page breaks
- * @return all the row page breaks
+ * @return all the horizontal page breaks, never <code>null</code>
*/
- public Iterator getRowBreaks() {
- return rowBreaks.getBreaksIterator();
+ public int[] getRowBreaks() {
+ return getRowBreaksRecord().getBreaks();
}
/**
- * Returns the number of row page breaks
* @return the number of row page breaks
*/
public int getNumRowBreaks(){
- return (rowBreaks == null) ? 0 : (int)rowBreaks.getNumBreaks();
+ return getRowBreaksRecord().getNumBreaks();
}
/**
- * Returns all the column page breaks
- * @return all the column page breaks
+ * @return all the column page breaks, never <code>null</code>
*/
- public Iterator getColumnBreaks(){
- return colBreaks.getBreaksIterator();
+ public int[] getColumnBreaks(){
+ return getColumnBreaksRecord().getBreaks();
}
/**
- * Returns the number of column page breaks
* @return the number of column page breaks
*/
public int getNumColumnBreaks(){
- return (colBreaks == null) ? 0 : (int)colBreaks.getNumBreaks();
+ return getColumnBreaksRecord().getNumBreaks();
}
public void setColumnGroupCollapsed( short columnNumber, boolean collapsed )
{
if (collapsed)
{
- columns.collapseColumn( columnNumber );
+ _columnInfos.collapseColumn( columnNumber );
}
else
{
- columns.expandColumn( columnNumber );
+ _columnInfos.expandColumn( columnNumber );
}
}
private void recalcRowGutter()
{
int maxLevel = 0;
- Iterator iterator = rows.getIterator();
+ Iterator iterator = _rowsAggregate.getIterator();
while ( iterator.hasNext() )
{
RowRecord rowRecord = (RowRecord) iterator.next();
}
// Grab the guts record, adding if needed
- GutsRecord guts = (GutsRecord) findFirstRecordBySid( GutsRecord.sid );
- if(guts == null) {
- guts = new GutsRecord();
- records.add(guts);
- }
+ GutsRecord guts = getGutsRecord();
// Set the levels onto it
guts.setRowLevelMax( (short) ( maxLevel + 1 ) );
guts.setLeftRowGutter( (short) ( 29 + (12 * (maxLevel)) ) );
{
if (collapse)
{
- rows.collapseRow( row );
+ _rowsAggregate.collapseRow( row );
}
else
{
- rows.expandRow( row );
+ _rowsAggregate.expandRow( row );
}
}
public DataValidityTable getOrCreateDataValidityTable() {
if (_dataValidityTable == null) {
- _dataValidityTable = DataValidityTable.createForSheet(records);
+ DataValidityTable result = new DataValidityTable();
+ RecordOrderer.addNewSheetRecord(records, result);
+ _dataValidityTable = result;
}
return _dataValidityTable;
}
-
/* ====================================================================
- 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;
+import java.util.Iterator;
+
/**
- * HorizontalPageBreak record that stores page breaks at rows
- * <p>
- * This class is just used so that SID compares work properly in the RecordFactory
+ * HorizontalPageBreak (0x001B) record that stores page breaks at rows <p/>
+ *
* @see PageBreakRecord
- * @author Danny Mui (dmui at apache dot org)
+ * @author Danny Mui (dmui at apache dot org)
*/
-public class HorizontalPageBreakRecord extends PageBreakRecord {
+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;
+ }
}
*/
public final class MergeCellsRecord extends Record {
public final static short sid = 0x00E5;
- private CellRangeAddressList _regions;
-
- /**
- * Creates an empty <tt>MergedCellsRecord</tt>
- */
- public MergeCellsRecord() {
- _regions = new CellRangeAddressList();
+ /** sometimes the regions array is shared with other MergedCellsRecords */
+ private CellRangeAddress[] _regions;
+ private final int _startIndex;
+ private final int _numberOfRegions;
+
+ 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() {
}
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;
}
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");
}
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);
}
}
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
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;
* @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
* 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()
- {
-
- }
-
- /**
- *
- * @param sid
- */
- public PageBreakRecord(short sid) {
- super();
- this.sid = sid;
+ protected PageBreakRecord() {
+ _breaks = new ArrayList();
+ _breakMap = new HashMap();
}
- 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 serialize(int offset, byte data[])
- {
- int recordsize = getRecordSize();
- 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;
+ public int getRecordSize() {
+ int nBreaks = _breaks.size();
+ if (!IS_EMPTY_RECORD_WRITTEN && nBreaks < 1) {
+ return 0;
}
-
- return recordsize;
+ return 4 + getDataSize();
}
- protected void validateSid(short id)
- {
- if(id != HORIZONTAL_SID && id != VERTICAL_SID)
- throw new RecordFormatException("NOT A HorizontalPageBreak or VerticalPageBreak RECORD!! " + id);
- else
- return;
- }
- public short getNumBreaks()
- {
- return breaks != null ? (short)breaks.size() : numBreaks;
+ 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;
+ for (int i=0; i<nBreaks; i++) {
+ Break br = (Break)_breaks.get(i);
+ pos += br.serialize(offset+pos, data);
+ }
+
+ return 4 + dataSize;
}
- public void setNumBreaks(short numBreaks)
- {
- this.numBreaks = numBreaks;
+ public int getNumBreaks() {
+ return _breaks.size();
}
- public Iterator getBreaksIterator()
- {
- if(breaks == null)
- return Collections.EMPTY_LIST.iterator();
- else
- return breaks.iterator();
+ public final Iterator getBreaksIterator() {
+ return _breaks.iterator();
}
public String toString()
{
StringBuffer retval = new StringBuffer();
- if (getSid() != HORIZONTAL_SID && getSid()!= VERTICAL_SID)
- return "[INVALIDPAGEBREAK]\n .sid ="+getSid()+"[INVALIDPAGEBREAK]";
-
+
String label;
String mainLabel;
String subLabel;
- if (getSid() == HORIZONTAL_SID) {
+ if (getSid() == HorizontalPageBreakRecord.sid) {
label = "HORIZONTALPAGEBREAK";
mainLabel = "row";
subLabel = "col";
/**
* Adds the page break at the specified parameters
* @param main Depending on sid, will determine row or column to put page break (zero-based)
- * @param subFrom No user-interface to set (defaults to minumum, 0)
+ * @param subFrom No user-interface to set (defaults to minimum, 0)
* @param subTo No user-interface to set
*/
- public void addBreak(short main, short subFrom, short subTo)
- {
- if(breaks == null)
- {
- breaks = new ArrayList(getNumBreaks() + 10);
- BreakMap = new HashMap();
- }
+ public void addBreak(int main, int subFrom, int subTo) {
+
Integer key = new Integer(main);
- Break region = (Break)BreakMap.get(key);
- if(region != null)
- {
+ Break region = (Break)_breakMap.get(key);
+ if(region == null) {
+ region = new Break(main, subFrom, subTo);
+ _breakMap.put(key, region);
+ _breaks.add(region);
+ } else {
region.main = main;
region.subFrom = subFrom;
region.subTo = subTo;
- } else
- {
- region = new Break(main, subFrom, subTo);
- breaks.add(region);
}
- BreakMap.put(key, region);
}
/**
* Removes the break indicated by the parameter
* @param main (zero-based)
*/
- public void removeBreak(short main)
- {
+ public final void removeBreak(int main) {
Integer rowKey = new Integer(main);
- Break region = (Break)BreakMap.get(rowKey);
- breaks.remove(region);
- BreakMap.remove(rowKey);
- }
-
- public int getRecordSize()
- {
- return 6 + getNumBreaks() * 6;
+ Break region = (Break)_breakMap.get(rowKey);
+ _breaks.remove(region);
+ _breakMap.remove(rowKey);
}
/**
* @param main FIXME: Document this!
* @return The Break or null if no break exists at the row/col specified.
*/
- public Break getBreak(short main)
- {
- if (BreakMap == null)
- return null;
+ public final Break getBreak(int main) {
Integer rowKey = new Integer(main);
- return (Break)BreakMap.get(rowKey);
+ return (Break)_breakMap.get(rowKey);
}
- /* Clones the page break record
- * @see java.lang.Object#clone()
- */
- public Object clone() {
- PageBreakRecord record = new PageBreakRecord(getSid());
- Iterator iterator = getBreaksIterator();
- while (iterator.hasNext()) {
- Break original = (Break)iterator.next();
- record.addBreak(original.main, original.subFrom, original.subTo);
- }
- return record;
- }
-
+ public final int[] getBreaks() {
+ int count = getNumBreaks();
+ if (count < 1) {
+ return EMPTY_INT_ARRAY;
+ }
+ int[] result = new int[count];
+ for (int i=0; i<count; i++) {
+ Break breakItem = (Break)_breaks.get(i);
+ result[i] = breakItem.main;
+ }
+ return result;
+ }
}
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.hssf.record;
* @author Jason Height (jheight at chariot dot net dot au)
* @version 2.0-pre
*/
-
-public abstract class Record
-{
+public abstract class Record extends RecordBase {
/**
* instantiates a blank record strictly for ID matching
*/
- public Record()
+ protected Record()
{
}
*
* @param in the RecordInputstream to read the record from
*/
- public Record(RecordInputStream in)
+ protected Record(RecordInputStream in)
{
validateSid(in.getSid());
fillFields(in);
return retval;
}
- /**
- * 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 abstract int serialize(int offset, byte [] data);
/**
* gives the current serialized size of the record. Should include the sid and reclength (4 bytes).
--- /dev/null
+/* ====================================================================
+ 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;
+
+/**
+ * Common base class of {@link Record} and {@link RecordAggregate}
+ *
+ * @author Josh Micich
+ */
+public abstract class RecordBase {
+ /**
+ * 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 abstract int serialize(int offset, byte[] data);
+
+ /**
+ * gives the current serialized size of the record. Should include the sid
+ * and reclength (4 bytes).
+ */
+ public abstract int getRecordSize();
+}
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
+
package org.apache.poi.hssf.record;
+import java.util.Iterator;
+
/**
- * VerticalPageBreak record that stores page breaks at columns
- * <p>
- * This class is just used so that SID compares work properly in the RecordFactory
+ * VerticalPageBreak (0x001A) record that stores page breaks at columns<p/>
+ *
* @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;
+ }
}
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
* @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 */
* @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");
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);
}
-/*
-* 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;
+/* ====================================================================
+ 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.
+==================================================================== */
-import org.apache.poi.hssf.record.ColumnInfoRecord;
-import org.apache.poi.hssf.record.Record;
-import org.apache.poi.hssf.record.RecordInputStream;
+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.ColumnInfoRecord;
+import org.apache.poi.hssf.record.Record;
+
/**
* @author Glen Stampoultzis
* @version $Id$
*/
-public class ColumnInfoRecordsAggregate
- extends Record
-{
-// int size = 0;
- List records = null;
-
- 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();
- }
+public final class ColumnInfoRecordsAggregate extends RecordAggregate {
+ private final List records;
+
+ /**
+ * 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
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<nItems; i++) {
+ rv.visitRecord((Record)records.get(i));
+ }
}
public int findStartOfColumnOutlineGroup(int idx)
return idx;
}
- public ColumnInfoRecord getColInfo(int idx)
- {
+ private ColumnInfoRecord getColInfo(int idx) {
return (ColumnInfoRecord) records.get( idx );
}
columnInfo.setHidden( hidden );
if (idx + 1 < records.size())
{
- ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) records.get( idx + 1 );
+ ColumnInfoRecord nextColumnInfo = getColInfo(idx + 1);
if (columnInfo.getLastColumn() + 1 == nextColumnInfo.getFirstColumn())
{
if (nextColumnInfo.getOutlineLevel() < level)
return;
// Find the start of the group.
- ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get( findStartOfColumnOutlineGroup( idx ) );
+ ColumnInfoRecord columnInfo = getColInfo( findStartOfColumnOutlineGroup( idx ) );
// Hide all the columns until the end of the group
columnInfo = writeHidden( columnInfo, idx, true );
* @see org.apache.poi.hssf.record.ColumnInfoRecord
* @return record containing a ColumnInfoRecord
*/
- public static Record createColInfo()
+ public static ColumnInfoRecord createColInfo()
{
ColumnInfoRecord retval = new ColumnInfoRecord();
ci.setCollapsed( collapsed.booleanValue() );
}
- public int findColumnIdx(int column, int fromIdx)
+ private int findColumnIdx(int column, int fromIdx)
{
if (column < 0)
throw new IllegalArgumentException( "column parameter out of range: " + column );
ColumnInfoRecord ci;
for (int k = fromIdx; k < records.size(); k++)
{
- ci = ( ColumnInfoRecord ) records.get(k);
+ ci = getColInfo(k);
if ((ci.getFirstColumn() <= column)
&& (column <= ci.getLastColumn()))
{
{
if (columnIdx == 0)
return;
- ColumnInfoRecord previousCol = (ColumnInfoRecord) records.get( columnIdx - 1);
- ColumnInfoRecord currentCol = (ColumnInfoRecord) records.get( columnIdx );
+ ColumnInfoRecord previousCol = getColInfo( columnIdx - 1);
+ ColumnInfoRecord currentCol = getColInfo( columnIdx );
boolean adjacentColumns = previousCol.getLastColumn() == currentCol.getFirstColumn() - 1;
if (!adjacentColumns)
return;
int columnIdx = findColumnIdx( i, Math.max(0,fromIdx) );
if (columnIdx != -1)
{
- level = ((ColumnInfoRecord)records.get( columnIdx )).getOutlineLevel();
+ level = getColInfo(columnIdx).getOutlineLevel();
if (indent) level++; else level--;
level = Math.max(0, level);
level = Math.min(7, level);
}
}
+ /**
+ * Finds the <tt>ColumnInfoRecord</tt> which contains the specified columnIndex
+ * @param columnIndex index of the column (not the index of the ColumnInfoRecord)
+ * @return <code>null</code> if no column info found for the specified column
+ */
+ public ColumnInfoRecord findColumnInfo(int columnIndex) {
+ int nInfos = records.size();
+ for(int i=0; i< nInfos; i++) {
+ ColumnInfoRecord ci = getColInfo(i);
+ if (ci.getFirstColumn() <= columnIndex && columnIndex <= ci.getLastColumn()) {
+ return ci;
+ }
+ }
+ return null;
+ }
+ public int getMaxOutlineLevel() {
+ int result = 0;
+ int count=records.size();
+ for (int i=0; i<count; i++) {
+ ColumnInfoRecord columnInfoRecord = getColInfo(i);
+ result = Math.max(columnInfoRecord.getOutlineLevel(), result);
+ }
+ return result;
+ }
}
--- /dev/null
+/* ====================================================================
+ 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.CFHeaderRecord;
+import org.apache.poi.hssf.record.Record;
+
+/**
+ * Holds all the conditional formatting for a workbook sheet.<p/>
+ *
+ * See OOO exelfileformat.pdf sec 4.12 'Conditional Formatting Table'
+ *
+ * @author Josh Micich
+ */
+public final class ConditionalFormattingTable extends RecordAggregate {
+
+ private final List _cfHeaders;
+
+ /**
+ * Creates an empty ConditionalFormattingTable
+ */
+ public ConditionalFormattingTable() {
+ _cfHeaders = new ArrayList();
+ }
+
+ public ConditionalFormattingTable(RecordStream rs) {
+
+ List temp = new ArrayList();
+ while (rs.peekNextClass() == CFHeaderRecord.class) {
+ temp.add(CFRecordsAggregate.createCFAggregate(rs));
+ }
+ _cfHeaders = temp;
+ }
+
+ public void visitContainedRecords(RecordVisitor rv) {
+ for (int i = 0; i < _cfHeaders.size(); i++) {
+ rv.visitRecord((Record) _cfHeaders.get(i));
+ }
+ }
+
+ /**
+ * @return index of the newly added CF header aggregate
+ */
+ public int add(CFRecordsAggregate cfAggregate) {
+ _cfHeaders.add(cfAggregate);
+ return _cfHeaders.size() - 1;
+ }
+
+ public int size() {
+ return _cfHeaders.size();
+ }
+
+ public CFRecordsAggregate get(int index) {
+ checkIndex(index);
+ return (CFRecordsAggregate) _cfHeaders.get(index);
+ }
+
+ public void remove(int index) {
+ checkIndex(index);
+ _cfHeaders.remove(index);
+ }
+
+ private void checkIndex(int index) {
+ if (index < 0 || index >= _cfHeaders.size()) {
+ throw new IllegalArgumentException("Specified CF index " + index
+ + " is outside the allowable range (0.." + (_cfHeaders.size() - 1) + ")");
+ }
+ }
+}
import java.util.List;\r
\r
import org.apache.poi.hssf.model.RecordStream;\r
-import org.apache.poi.hssf.record.CFHeaderRecord;\r
-import org.apache.poi.hssf.record.CFRuleRecord;\r
import org.apache.poi.hssf.record.DVALRecord;\r
import org.apache.poi.hssf.record.DVRecord;\r
-import org.apache.poi.hssf.record.EOFRecord;\r
-import org.apache.poi.hssf.record.HyperlinkRecord;\r
-import org.apache.poi.hssf.record.MergeCellsRecord;\r
-import org.apache.poi.hssf.record.PaneRecord;\r
import org.apache.poi.hssf.record.Record;\r
-import org.apache.poi.hssf.record.SelectionRecord;\r
-import org.apache.poi.hssf.record.WindowTwoRecord;\r
\r
/**\r
* Manages the DVALRecord and DVRecords for a single sheet<br/>\r
*/\r
public final class DataValidityTable extends RecordAggregate {\r
\r
- private static final short sid = -0x01B2; // not a real record\r
private final DVALRecord _headerRec;\r
/**\r
* The list of data validations for the current sheet.\r
_validationList = temp;\r
}\r
\r
- private DataValidityTable() {\r
+ public DataValidityTable() {\r
_headerRec = new DVALRecord();\r
_validationList = new ArrayList();\r
}\r
\r
- public short getSid() {\r
- return sid;\r
- }\r
-\r
- public int serialize(int offset, byte[] data) {\r
- int result = _headerRec.serialize(offset, data);\r
- for (int i = 0; i < _validationList.size(); i++) {\r
- result += ((Record) _validationList.get(i)).serialize(offset + result, data);\r
+ public void visitContainedRecords(RecordVisitor rv) {\r
+ if (_validationList.isEmpty()) {\r
+ return;\r
}\r
- return result;\r
- }\r
-\r
- public int getRecordSize() {\r
- int result = _headerRec.getRecordSize();\r
- for (int i = _validationList.size() - 1; i >= 0; i--) {\r
- result += ((Record) _validationList.get(i)).getRecordSize();\r
+ rv.visitRecord(_headerRec);\r
+ for (int i = 0; i < _validationList.size(); i++) {\r
+ rv.visitRecord((Record) _validationList.get(i));\r
}\r
- return result;\r
- }\r
-\r
- /**\r
- * Creates a new <tt>DataValidityTable</tt> and inserts it in the right\r
- * place in the sheetRecords list.\r
- */\r
- public static DataValidityTable createForSheet(List sheetRecords) {\r
- int index = findDVTableInsertPos(sheetRecords);\r
-\r
- DataValidityTable result = new DataValidityTable();\r
- sheetRecords.add(index, result);\r
- return result;\r
}\r
\r
- /**\r
- * Finds the index where the sheet validations header record should be inserted\r
- * @param records the records for this sheet\r
- * \r
- * + WINDOW2\r
- * o SCL\r
- * o PANE\r
- * oo SELECTION\r
- * o STANDARDWIDTH\r
- * oo MERGEDCELLS\r
- * o LABELRANGES\r
- * o PHONETICPR\r
- * o Conditional Formatting Table\r
- * o Hyperlink Table\r
- * o Data Validity Table\r
- * o SHEETLAYOUT\r
- * o SHEETPROTECTION\r
- * o RANGEPROTECTION\r
- * + EOF\r
- */\r
- private static int findDVTableInsertPos(List records) {\r
- int i = records.size() - 1;\r
- if (!(records.get(i) instanceof EOFRecord)) {\r
- throw new IllegalStateException("Last sheet record should be EOFRecord");\r
- }\r
- while (i > 0) {\r
- i--;\r
- Record rec = (Record) records.get(i);\r
- if (isPriorRecord(rec.getSid())) {\r
- Record nextRec = (Record) records.get(i + 1);\r
- if (!isSubsequentRecord(nextRec.getSid())) {\r
- throw new IllegalStateException("Unexpected (" + nextRec.getClass().getName()\r
- + ") found after (" + rec.getClass().getName() + ")");\r
- }\r
- return i;\r
- }\r
- if (!isSubsequentRecord(rec.getSid())) {\r
- throw new IllegalStateException("Unexpected (" + rec.getClass().getName()\r
- + ") while looking for DV Table insert pos");\r
- }\r
- }\r
- return 0;\r
- }\r
-\r
- // TODO - add UninterpretedRecord as base class for many of these\r
- // unimplemented sids\r
-\r
- private static boolean isPriorRecord(short sid) {\r
- switch(sid) {\r
- case WindowTwoRecord.sid:\r
- case 0x00A0: // SCL\r
- case PaneRecord.sid:\r
- case SelectionRecord.sid:\r
- case 0x0099: // STANDARDWIDTH\r
- case MergeCellsRecord.sid:\r
- case 0x015F: // LABELRANGES\r
- case 0x00EF: // PHONETICPR\r
- case CFHeaderRecord.sid:\r
- case CFRuleRecord.sid:\r
- case HyperlinkRecord.sid:\r
- case 0x0800: // QUICKTIP\r
- return true;\r
- }\r
- return false;\r
- }\r
-\r
- private static boolean isSubsequentRecord(short sid) {\r
- switch(sid) {\r
- case 0x0862: // SHEETLAYOUT\r
- case 0x0867: // SHEETPROTECTION\r
- case 0x0868: // RANGEPROTECTION\r
- case EOFRecord.sid:\r
- return true;\r
- }\r
- return false;\r
- }\r
-\r
public void addDataValidation(DVRecord dvRecord) {\r
_validationList.add(dvRecord);\r
_headerRec.setDVRecNo(_validationList.size());\r
--- /dev/null
+/* ====================================================================
+ 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.MergeCellsRecord;
+import org.apache.poi.hssf.util.CellRangeAddress;
+import org.apache.poi.hssf.util.CellRangeAddressList;
+
+/**
+ *
+ * @author Josh Micich
+ */
+public final class MergedCellsTable extends RecordAggregate {
+ private static int MAX_MERGED_REGIONS = 1027; // enforced by the 8224 byte limit
+
+ private final List _mergedRegions;
+
+ /**
+ * Creates an empty aggregate
+ */
+ public MergedCellsTable() {
+ _mergedRegions = new ArrayList();
+ }
+
+ public MergedCellsTable(RecordStream rs) {
+ List temp = new ArrayList();
+ while (rs.peekNextClass() == MergeCellsRecord.class) {
+ MergeCellsRecord mcr = (MergeCellsRecord) rs.getNext();
+ int nRegions = mcr.getNumAreas();
+ for (int i = 0; i < nRegions; i++) {
+ temp.add(mcr.getAreaAt(i));
+ }
+ }
+ _mergedRegions = temp;
+ }
+
+ public int getRecordSize() {
+ // a bit cheaper than the default impl
+ int nRegions = _mergedRegions.size();
+ if (nRegions < 1) {
+ // no need to write a single empty MergeCellsRecord
+ return 0;
+ }
+ int nMergedCellsRecords = nRegions / MAX_MERGED_REGIONS;
+ int nLeftoverMergedRegions = nRegions % MAX_MERGED_REGIONS;
+
+ int result = nMergedCellsRecords
+ * (4 + CellRangeAddressList.getEncodedSize(MAX_MERGED_REGIONS)) + 4
+ + CellRangeAddressList.getEncodedSize(nLeftoverMergedRegions);
+ return result;
+ }
+
+ public void visitContainedRecords(RecordVisitor rv) {
+ int nRegions = _mergedRegions.size();
+ if (nRegions < 1) {
+ // no need to write a single empty MergeCellsRecord
+ return;
+ }
+
+ int nFullMergedCellsRecords = nRegions / MAX_MERGED_REGIONS;
+ int nLeftoverMergedRegions = nRegions % MAX_MERGED_REGIONS;
+ CellRangeAddress[] cras = new CellRangeAddress[nRegions];
+ _mergedRegions.toArray(cras);
+
+ for (int i = 0; i < nFullMergedCellsRecords; i++) {
+ int startIx = i * MAX_MERGED_REGIONS;
+ rv.visitRecord(new MergeCellsRecord(cras, startIx, MAX_MERGED_REGIONS));
+ }
+ if (nLeftoverMergedRegions > 0) {
+ int startIx = nFullMergedCellsRecords * MAX_MERGED_REGIONS;
+ rv.visitRecord(new MergeCellsRecord(cras, startIx, nLeftoverMergedRegions));
+ }
+ }
+
+ public void add(MergeCellsRecord mcr) {
+ _mergedRegions.add(mcr);
+ }
+
+ public CellRangeAddress get(int index) {
+ checkIndex(index);
+ return (CellRangeAddress) _mergedRegions.get(index);
+ }
+
+ public void remove(int index) {
+ checkIndex(index);
+ _mergedRegions.remove(index);
+ }
+
+ private void checkIndex(int index) {
+ if (index < 0 || index >= _mergedRegions.size()) {
+ throw new IllegalArgumentException("Specified CF index " + index
+ + " is outside the allowable range (0.." + (_mergedRegions.size() - 1) + ")");
+ }
+ }
+
+ public void addArea(int rowFrom, int colFrom, int rowTo, int colTo) {
+ _mergedRegions.add(new CellRangeAddress(rowFrom, rowTo, colFrom, colTo));
+ }
+
+ public int getNumberOfMergedRegions() {
+ return _mergedRegions.size();
+ }
+}
package org.apache.poi.hssf.record.aggregates;\r
\r
import org.apache.poi.hssf.record.Record;\r
+import org.apache.poi.hssf.record.RecordBase;\r
import org.apache.poi.hssf.record.RecordInputStream;\r
\r
/**\r
* \r
* @author Josh Micich\r
*/\r
-public abstract class RecordAggregate extends Record {\r
- // TODO - convert existing aggregate classes to proper subclasses of this one\r
+public abstract class RecordAggregate extends RecordBase {\r
+ // TODO - delete these methods when all subclasses have been converted\r
protected final void validateSid(short id) {\r
- // TODO - break class hierarchy and make separate from Record\r
throw new RuntimeException("Should not be called");\r
}\r
protected final void fillFields(RecordInputStream in) {\r
throw new RuntimeException("Should not be called");\r
}\r
- // force subclassses to provide better implementation than default\r
- public abstract int getRecordSize();\r
+ public final short getSid() {\r
+ throw new RuntimeException("Should not be called");\r
+ }\r
+\r
+ public abstract void visitContainedRecords(RecordVisitor rv);\r
+ \r
+ public final int serialize(int offset, byte[] data) {\r
+ SerializingRecordVisitor srv = new SerializingRecordVisitor(data, offset);\r
+ visitContainedRecords(srv);\r
+ return srv.countBytesWritten();\r
+ }\r
+ public int getRecordSize() {\r
+ RecordSizingVisitor rsv = new RecordSizingVisitor();\r
+ visitContainedRecords(rsv);\r
+ return rsv.getTotalSize();\r
+ }\r
+ \r
+ public interface RecordVisitor {\r
+ void visitRecord(Record r);\r
+ }\r
+ \r
+ private static final class SerializingRecordVisitor implements RecordVisitor {\r
+\r
+ private final byte[] _data;\r
+ private final int _startOffset;\r
+ private int _countBytesWritten;\r
+\r
+ public SerializingRecordVisitor(byte[] data, int startOffset) {\r
+ _data = data;\r
+ _startOffset = startOffset;\r
+ _countBytesWritten = 0;\r
+ }\r
+ public int countBytesWritten() {\r
+ return _countBytesWritten;\r
+ }\r
+ public void visitRecord(Record r) {\r
+ int currentOffset = _startOffset + _countBytesWritten;\r
+ _countBytesWritten += r.serialize(currentOffset, _data);\r
+ }\r
+ }\r
+ private static final class RecordSizingVisitor implements RecordVisitor {\r
+\r
+ private int _totalSize;\r
+ \r
+ public RecordSizingVisitor() {\r
+ _totalSize = 0;\r
+ }\r
+ public int getTotalSize() {\r
+ return _totalSize;\r
+ }\r
+ public void visitRecord(Record r) {\r
+ _totalSize += r.getRecordSize();\r
+ }\r
+ }\r
}\r
import org.apache.poi.hssf.record.NumberRecord;
import org.apache.poi.hssf.record.ObjRecord;
import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.RecordBase;
import org.apache.poi.hssf.record.StringRecord;
import org.apache.poi.hssf.record.SubRecord;
import org.apache.poi.hssf.record.TextObjectRecord;
HSSFComment comment = null;
HashMap txshapes = new HashMap(); //map shapeId and TextObjectRecord
for (Iterator it = sheet.getRecords().iterator(); it.hasNext(); ) {
- Record rec = ( Record ) it.next();
+ RecordBase rec = (RecordBase) it.next();
if (rec instanceof NoteRecord){
NoteRecord note = (NoteRecord)rec;
if (note.getRow() == row && note.getColumn() == column){
*/
public HSSFHyperlink getHyperlink(){
for (Iterator it = sheet.getRecords().iterator(); it.hasNext(); ) {
- Record rec = ( Record ) it.next();
+ RecordBase rec = (RecordBase) it.next();
if (rec instanceof HyperlinkRecord){
HyperlinkRecord link = (HyperlinkRecord)rec;
if(link.getFirstColumn() == record.getColumn() && link.getFirstRow() == record.getRow()){
}
/**
- * Retrieves all the horizontal page breaks
- * @return all the horizontal page breaks, or null if there are no row page breaks
+ * @return row indexes of all the horizontal page breaks, never <code>null</code>
*/
public int[] getRowBreaks(){
//we can probably cache this information, but this should be a sparsely used function
- int count = sheet.getNumRowBreaks();
- if (count > 0) {
- int[] returnValue = new int[count];
- Iterator iterator = sheet.getRowBreaks();
- int i = 0;
- while (iterator.hasNext()) {
- PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
- returnValue[i++] = breakItem.main;
- }
- return returnValue;
- }
- return null;
+ return sheet.getRowBreaks();
}
/**
- * Retrieves all the vertical page breaks
- * @return all the vertical page breaks, or null if there are no column page breaks
+ * @return column indexes of all the vertical page breaks, never <code>null</code>
*/
- public short[] getColumnBreaks(){
+ public int[] getColumnBreaks(){
//we can probably cache this information, but this should be a sparsely used function
- int count = sheet.getNumColumnBreaks();
- if (count > 0) {
- short[] returnValue = new short[count];
- Iterator iterator = sheet.getColumnBreaks();
- int i = 0;
- while (iterator.hasNext()) {
- PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
- returnValue[i++] = breakItem.main;
- }
- return returnValue;
- }
- return null;
+ return sheet.getColumnBreaks();
}
}
public int getSize() {
- return 2 + CellRangeAddress.getEncodedSize(_list.size());
+ return getEncodedSize(_list.size());
+ }
+ /**
+ * @return the total size of for the specified number of ranges,
+ * including the initial 2 byte range count
+ */
+ public static int getEncodedSize(int numberOfRanges) {
+ return 2 + CellRangeAddress.getEncodedSize(numberOfRanges);
}
public CellRangeAddressList copy() {
CellRangeAddressList result = new CellRangeAddressList();
package org.apache.poi.hssf.model;
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.List;
+
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.eventmodel.ERFListener;
import org.apache.poi.hssf.eventmodel.EventRecordFactory;
-import org.apache.poi.hssf.record.*;
+import org.apache.poi.hssf.record.BOFRecord;
+import org.apache.poi.hssf.record.BlankRecord;
+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.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.StringRecord;
+import org.apache.poi.hssf.record.UncalcedRecord;
import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.RowRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.ValueRecordsAggregate;
-
-import java.io.ByteArrayInputStream;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
+import org.apache.poi.hssf.util.CellRangeAddress;
/**
* Unit test for the Sheet class.
assertTrue( sheet.records.get(pos++) instanceof EOFRecord );
}
+ private static final class MergedCellListener implements ERFListener {
+
+ private int _count;
+ public MergedCellListener() {
+ _count = 0;
+ }
+ public boolean processRecord(Record rec) {
+ _count++;
+ return true;
+ }
+ public int getCount() {
+ return _count;
+ }
+ }
+
public void testAddMergedRegion() {
Sheet sheet = Sheet.createSheet();
int regionsToAdd = 4096;
- int startRecords = sheet.getRecords().size();
//simple test that adds a load of regions
for (int n = 0; n < regionsToAdd; n++)
assertTrue(sheet.getNumMergedRegions() == regionsToAdd);
//test that the regions were spread out over the appropriate number of records
- int recordsAdded = sheet.getRecords().size() - startRecords;
+ byte[] sheetData = new byte[sheet.getSize()];
+ sheet.serialize(0, sheetData);
+ MergedCellListener mcListener = new MergedCellListener();
+ EventRecordFactory erf = new EventRecordFactory(mcListener, new short[] { MergeCellsRecord.sid, });
+// POIFSFileSystem poifs = new POIFSFileSystem(new ByteArrayInputStream(sheetData));
+ erf.processRecords(new ByteArrayInputStream(sheetData));
+ int recordsAdded = mcListener.getCount();
int recordsExpected = regionsToAdd/1027;
if ((regionsToAdd % 1027) != 0)
recordsExpected++;
- assertTrue("The " + regionsToAdd + " merged regions should have been spread out over " + recordsExpected + " records, not " + recordsAdded, recordsAdded == recordsExpected);
+ assertTrue("The " + regionsToAdd + " merged regions should have been spread out over "
+ + recordsExpected + " records, not " + recordsAdded, recordsAdded == recordsExpected);
// Check we can't add one with invalid date
try {
sheet.addMergedRegion(10, (short)10, 9, (short)12);
Sheet sheet = Sheet.createSheet();
int regionsToAdd = 4096;
- for (int n = 0; n < regionsToAdd; n++)
- sheet.addMergedRegion(0, (short) 0, 1, (short) 1);
+ for (int n = 0; n < regionsToAdd; n++) {
+ sheet.addMergedRegion(n, 0, n, 1);
+ }
- int records = sheet.getRecords().size();
+ int nSheetRecords = sheet.getRecords().size();
//remove a third from the beginning
for (int n = 0; n < regionsToAdd/3; n++)
{
sheet.removeMergedRegion(0);
//assert they have been deleted
- assertTrue("Num of regions should be " + (regionsToAdd - n - 1) + " not " + sheet.getNumMergedRegions(), sheet.getNumMergedRegions() == regionsToAdd - n - 1);
+ assertEquals("Num of regions", regionsToAdd - n - 1, sheet.getNumMergedRegions());
}
-
- //assert any record removing was done
- int recordsRemoved = (regionsToAdd/3)/1027; //doesn't work for particular values of regionsToAdd
- assertTrue("Expected " + recordsRemoved + " record to be removed from the starting " + records + ". Currently there are " + sheet.getRecords().size() + " records", records - sheet.getRecords().size() == recordsRemoved);
+
+ // merge records are removed from within the MergedCellsTable,
+ // so the sheet record count should not change
+ assertEquals("Sheet Records", nSheetRecords, sheet.getRecords().size());
}
/**
public void testMovingMergedRegion() {
List records = new ArrayList();
- MergeCellsRecord merged = new MergeCellsRecord();
- merged.addArea(0, (short)0, 1, (short)2);
+ CellRangeAddress[] cras = {
+ new CellRangeAddress(0, 1, 0, 2),
+ };
+ MergeCellsRecord merged = new MergeCellsRecord(cras, 0, cras.length);
+ records.add(new DimensionsRecord());
records.add(new RowRecord(0));
records.add(new RowRecord(1));
records.add(new RowRecord(2));
public void testRowAggregation() {
List records = new ArrayList();
+ records.add(new DimensionsRecord());
records.add(new RowRecord(0));
records.add(new RowRecord(1));
records.add(new StringRecord());
boolean is0 = false;
boolean is11 = false;
- Iterator iterator = sheet.getRowBreaks();
- while (iterator.hasNext()) {
- PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
- int main = breakItem.main;
+ int[] rowBreaks = sheet.getRowBreaks();
+ for (int i = 0; i < rowBreaks.length; i++) {
+ int main = rowBreaks[i];
if (main != 0 && main != 10 && main != 11) fail("Invalid page break");
if (main == 0) is0 = true;
if (main == 10) is10= true;
boolean is1 = false;
boolean is15 = false;
- Iterator iterator = sheet.getColumnBreaks();
- while (iterator.hasNext()) {
- PageBreakRecord.Break breakItem = (PageBreakRecord.Break)iterator.next();
- int main = breakItem.main;
+ int[] colBreaks = sheet.getColumnBreaks();
+ for (int i = 0; i < colBreaks.length; i++) {
+ int main = colBreaks[i];
if (main != 0 && main != 1 && main != 10 && main != 15) fail("Invalid page break");
if (main == 0) is0 = true;
if (main == 1) is1 = true;
xfindex = sheet.getXFIndexForColAt((short) 1);
assertEquals(DEFAULT_IDX, xfindex);
- // TODO change return type to ColumnInfoRecord
- ColumnInfoRecord nci = (ColumnInfoRecord)ColumnInfoRecordsAggregate.createColInfo();
- sheet.columns.insertColumn(nci);
+ ColumnInfoRecord nci = ColumnInfoRecordsAggregate.createColInfo();
+ sheet._columnInfos.insertColumn(nci);
// single column ColumnInfoRecord
nci.setFirstColumn((short) 2);
List records = new ArrayList();
records.add(new BOFRecord());
records.add(new UncalcedRecord());
+ records.add(new DimensionsRecord());
records.add(new EOFRecord());
Sheet sheet = Sheet.createSheet(records, 0, 0);
if (serializedSize != estimatedSize) {
throw new AssertionFailedError("Identified bug 45066 b");
}
- assertEquals(50, serializedSize);
+ assertEquals(68, serializedSize);
}
/**
int dbCellRecordPos = getDbCellRecordPos(sheet);
- if (dbCellRecordPos == 264) {
+ if (dbCellRecordPos == 252) {
// The overt symptom of the bug
// DBCELL record pos is calculated wrong if VRA comes before RRA
throw new AssertionFailedError("Identified bug 45145");
assertEquals(RowRecordsAggregate.class, recs.get(rraIx).getClass());
assertEquals(ValueRecordsAggregate.class, recs.get(rraIx+1).getClass());
- assertEquals(254, dbCellRecordPos);
+ assertEquals(242, dbCellRecordPos);
}
/**
public void testGetCellWidth() {
Sheet sheet = Sheet.createSheet();
- // TODO change return type to ColumnInfoRecord
- ColumnInfoRecord nci = (ColumnInfoRecord)ColumnInfoRecordsAggregate.createColInfo();
+ ColumnInfoRecord nci = ColumnInfoRecordsAggregate.createColInfo();
// Prepare test model
nci.setFirstColumn((short)5);
nci.setColumnWidth((short)100);
- sheet.columns.insertColumn(nci);
+ sheet._columnInfos.insertColumn(nci);
assertEquals((short)100,sheet.getColumnWidth((short)5));
assertEquals((short)100,sheet.getColumnWidth((short)6));
}
}
-
-
-
* @throws Exception
*/
public void testCloneReferences() throws Exception {
- MergeCellsRecord merge = new MergeCellsRecord();
- merge.addArea(0, (short)0, 1, (short)2);
+ CellRangeAddress[] cras = { new CellRangeAddress(0, 1, 0, 2), };
+ MergeCellsRecord merge = new MergeCellsRecord(cras, 0, cras.length);
MergeCellsRecord clone = (MergeCellsRecord)merge.clone();
assertNotSame("Merged and cloned objects are the same", merge, clone);
assertEquals("New Clone Col From doesnt match", mergeRegion.getFirstColumn(), cloneRegion.getFirstColumn());
assertEquals("New Clone Col To doesnt match", mergeRegion.getLastColumn(), cloneRegion.getLastColumn());
- merge.removeAreaAt(0);
- assertNotNull("Clone's item not removed", clone.getAreaAt(0));
+ assertFalse(merge.getAreaAt(0) == clone.getAreaAt(0));
}
}
import junit.framework.TestCase;
+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.RecordFactory;
recs.add(rule2);
recs.add(rule3);
CFRecordsAggregate record;
- record = CFRecordsAggregate.createCFAggregate(recs, 0);
+ record = CFRecordsAggregate.createCFAggregate(new RecordStream(recs, 0));
// Serialize
byte [] serializedRecord = record.serialize();
assertEquals(2, cellRanges.length);
assertEquals(3, header.getNumberOfConditionalFormats());
- record = CFRecordsAggregate.createCFAggregate(recs, 0);
+ record = CFRecordsAggregate.createCFAggregate(new RecordStream(recs, 0));
record = record.cloneCFAggregate();
import junit.framework.TestCase;
import org.apache.poi.hssf.record.ColumnInfoRecord;
+import org.apache.poi.hssf.record.RecordBase;
/**
* @author Glen Stampoultzis
*/
-public final class TestColumnInfoRecordsAggregate extends TestCase
-{
- ColumnInfoRecordsAggregate columnInfoRecordsAggregate;
-
- public void testGetRecordSize() throws Exception
- {
- columnInfoRecordsAggregate = new ColumnInfoRecordsAggregate();
- columnInfoRecordsAggregate.insertColumn( createColumn( (short)1, (short)3 ));
- columnInfoRecordsAggregate.insertColumn( createColumn( (short)4, (short)7 ));
- columnInfoRecordsAggregate.insertColumn( createColumn( (short)8, (short)8 ));
-// columnInfoRecordsAggregate.setColumn( (short)2, new Short( (short)200 ), new Integer( 1 ), new Boolean( true ), null);
- columnInfoRecordsAggregate.groupColumnRange( (short)2, (short)5, true );
- assertEquals(6, columnInfoRecordsAggregate.getNumColumns());
-
- assertEquals(columnInfoRecordsAggregate.getRecordSize(), columnInfoRecordsAggregate.serialize().length);
-
- columnInfoRecordsAggregate = new ColumnInfoRecordsAggregate();
- columnInfoRecordsAggregate.groupColumnRange( (short)3, (short)6, true );
-
- assertEquals(columnInfoRecordsAggregate.getRecordSize(), serializedSize());
- }
-
- private int serializedSize()
- {
- return columnInfoRecordsAggregate.serialize(0, new byte[columnInfoRecordsAggregate.getRecordSize()]);
- }
-
- private ColumnInfoRecord createColumn( short firstCol, short lastCol )
- {
- ColumnInfoRecord columnInfoRecord = new ColumnInfoRecord( );
- columnInfoRecord.setFirstColumn(firstCol);
- columnInfoRecord.setLastColumn(lastCol);
- return columnInfoRecord;
- }
+public final class TestColumnInfoRecordsAggregate extends TestCase {
+
+ public void testGetRecordSize() {
+ ColumnInfoRecordsAggregate agg = new ColumnInfoRecordsAggregate();
+ agg.insertColumn(createColumn(1, 3));
+ agg.insertColumn(createColumn(4, 7));
+ agg.insertColumn(createColumn(8, 8));
+ agg.groupColumnRange((short) 2, (short) 5, true);
+ assertEquals(6, agg.getNumColumns());
+
+ confirmSerializedSize(agg);
+
+ agg = new ColumnInfoRecordsAggregate();
+ agg.groupColumnRange((short) 3, (short) 6, true);
+ confirmSerializedSize(agg);
+ }
+
+ private static void confirmSerializedSize(RecordBase cirAgg) {
+ int estimatedSize = cirAgg.getRecordSize();
+ byte[] buf = new byte[estimatedSize];
+ int serializedSize = cirAgg.serialize(0, buf);
+ assertEquals(estimatedSize, serializedSize);
+ }
+
+ private static ColumnInfoRecord createColumn(int firstCol, int lastCol) {
+ ColumnInfoRecord columnInfoRecord = new ColumnInfoRecord();
+ columnInfoRecord.setFirstColumn((short) firstCol);
+ columnInfoRecord.setLastColumn((short) lastCol);
+ return columnInfoRecord;
+ }
}
\ No newline at end of file
HSSFSheet sheet = wb.getSheetAt( 0 );
int[] breaks = sheet.getRowBreaks();
- assertNull(breaks);
+ assertEquals(0, breaks.length);
//add 3 row breaks
for (int j = 1; j <= 3; j++) {