213 lines
7.4 KiB
Java
213 lines
7.4 KiB
Java
/* ====================================================================
|
|
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.eventusermodel;
|
|
|
|
import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
|
|
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
|
|
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord;
|
|
import org.apache.poi.hssf.record.BOFRecord;
|
|
import org.apache.poi.hssf.record.CellValueRecordInterface;
|
|
import org.apache.poi.hssf.record.MulBlankRecord;
|
|
import org.apache.poi.hssf.record.MulRKRecord;
|
|
import org.apache.poi.hssf.record.NoteRecord;
|
|
import org.apache.poi.hssf.record.Record;
|
|
import org.apache.poi.hssf.record.RecordFactory;
|
|
import org.apache.poi.hssf.record.RowRecord;
|
|
import org.apache.poi.hssf.record.SharedFormulaRecord;
|
|
import org.apache.poi.hssf.record.StringRecord;
|
|
|
|
/**
|
|
* <p>A HSSFListener which tracks rows and columns, and will
|
|
* trigger your HSSFListener for all rows and cells,
|
|
* even the ones that aren't actually stored in the file.</p>
|
|
* <p>This allows your code to have a more "Excel" like
|
|
* view of the data in the file, and not have to worry
|
|
* (as much) about if a particular row/cell is in the
|
|
* file, or was skipped from being written as it was
|
|
* blank.
|
|
*/
|
|
public final class MissingRecordAwareHSSFListener implements HSSFListener {
|
|
private HSSFListener childListener;
|
|
|
|
// Need to have different counters for cell rows and
|
|
// row rows, as you sometimes get a RowRecord in the
|
|
// middle of some cells, and that'd break everything
|
|
private int lastRowRow;
|
|
|
|
private int lastCellRow;
|
|
private int lastCellColumn;
|
|
|
|
/**
|
|
* Constructs a new MissingRecordAwareHSSFListener, which
|
|
* will fire processRecord on the supplied child
|
|
* HSSFListener for all Records, and missing records.
|
|
* @param listener The HSSFListener to pass records on to
|
|
*/
|
|
public MissingRecordAwareHSSFListener(HSSFListener listener) {
|
|
resetCounts();
|
|
childListener = listener;
|
|
}
|
|
|
|
public void processRecord(Record record) {
|
|
int thisRow;
|
|
int thisColumn;
|
|
CellValueRecordInterface[] expandedRecords = null;
|
|
|
|
if (record instanceof CellValueRecordInterface) {
|
|
CellValueRecordInterface valueRec = (CellValueRecordInterface) record;
|
|
thisRow = valueRec.getRow();
|
|
thisColumn = valueRec.getColumn();
|
|
} else {
|
|
if (record instanceof StringRecord){
|
|
//it contains only cashed result of the previous FormulaRecord evaluation
|
|
childListener.processRecord(record);
|
|
return;
|
|
}
|
|
thisRow = -1;
|
|
thisColumn = -1;
|
|
|
|
switch (record.getSid()) {
|
|
// the BOFRecord can represent either the beginning of a sheet or
|
|
// the workbook
|
|
case BOFRecord.sid:
|
|
BOFRecord bof = (BOFRecord) record;
|
|
if (bof.getType() == BOFRecord.TYPE_WORKBOOK ||
|
|
bof.getType() == BOFRecord.TYPE_WORKSHEET) {
|
|
// Reset the row and column counts - new workbook / worksheet
|
|
resetCounts();
|
|
}
|
|
break;
|
|
case RowRecord.sid:
|
|
RowRecord rowrec = (RowRecord) record;
|
|
//System.out.println("Row " + rowrec.getRowNumber() + " found, first column at "
|
|
// + rowrec.getFirstCol() + " last column at " + rowrec.getLastCol());
|
|
|
|
// If there's a jump in rows, fire off missing row records
|
|
if (lastRowRow + 1 < rowrec.getRowNumber()) {
|
|
for (int i = (lastRowRow + 1); i < rowrec.getRowNumber(); i++) {
|
|
MissingRowDummyRecord dr = new MissingRowDummyRecord(i);
|
|
childListener.processRecord(dr);
|
|
}
|
|
}
|
|
|
|
// Record this as the last row we saw
|
|
lastRowRow = rowrec.getRowNumber();
|
|
lastCellColumn = -1;
|
|
break;
|
|
|
|
case SharedFormulaRecord.sid:
|
|
// SharedFormulaRecord occurs after the first FormulaRecord of the cell range.
|
|
// There are probably (but not always) more cell records after this
|
|
// - so don't fire off the LastCellOfRowDummyRecord yet
|
|
childListener.processRecord(record);
|
|
return;
|
|
case MulBlankRecord.sid:
|
|
// These appear in the middle of the cell records, to
|
|
// specify that the next bunch are empty but styled
|
|
// Expand this out into multiple blank cells
|
|
MulBlankRecord mbr = (MulBlankRecord)record;
|
|
expandedRecords = RecordFactory.convertBlankRecords(mbr);
|
|
break;
|
|
case MulRKRecord.sid:
|
|
// This is multiple consecutive number cells in one record
|
|
// Exand this out into multiple regular number cells
|
|
MulRKRecord mrk = (MulRKRecord)record;
|
|
expandedRecords = RecordFactory.convertRKRecords(mrk);
|
|
break;
|
|
case NoteRecord.sid:
|
|
NoteRecord nrec = (NoteRecord) record;
|
|
thisRow = nrec.getRow();
|
|
thisColumn = nrec.getColumn();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// First part of expanded record handling
|
|
if(expandedRecords != null && expandedRecords.length > 0) {
|
|
thisRow = expandedRecords[0].getRow();
|
|
thisColumn = expandedRecords[0].getColumn();
|
|
}
|
|
|
|
// If we're on cells, and this cell isn't in the same
|
|
// row as the last one, then fire the
|
|
// dummy end-of-row records
|
|
if(thisRow != lastCellRow && thisRow > 0) {
|
|
if (lastCellRow == -1) lastCellRow = 0;
|
|
for(int i=lastCellRow; i<thisRow; i++) {
|
|
int cols = -1;
|
|
if(i == lastCellRow) {
|
|
cols = lastCellColumn;
|
|
}
|
|
childListener.processRecord(new LastCellOfRowDummyRecord(i, cols));
|
|
}
|
|
}
|
|
|
|
// If we've just finished with the cells, then fire the
|
|
// final dummy end-of-row record
|
|
if(lastCellRow != -1 && lastCellColumn != -1 && thisRow == -1) {
|
|
childListener.processRecord(new LastCellOfRowDummyRecord(lastCellRow, lastCellColumn));
|
|
|
|
lastCellRow = -1;
|
|
lastCellColumn = -1;
|
|
}
|
|
|
|
// If we've moved onto a new row, the ensure we re-set
|
|
// the column counter
|
|
if(thisRow != lastCellRow) {
|
|
lastCellColumn = -1;
|
|
}
|
|
|
|
// If there's a gap in the cells, then fire
|
|
// the dummy cell records
|
|
if(lastCellColumn != thisColumn-1) {
|
|
for(int i=lastCellColumn+1; i<thisColumn; i++) {
|
|
childListener.processRecord(new MissingCellDummyRecord(thisRow, i));
|
|
}
|
|
}
|
|
|
|
// Next part of expanded record handling
|
|
if(expandedRecords != null && expandedRecords.length > 0) {
|
|
thisColumn = expandedRecords[expandedRecords.length-1].getColumn();
|
|
}
|
|
|
|
|
|
// Update cell and row counts as needed
|
|
if(thisColumn != -1) {
|
|
lastCellColumn = thisColumn;
|
|
lastCellRow = thisRow;
|
|
}
|
|
|
|
// Pass along the record(s)
|
|
if(expandedRecords != null && expandedRecords.length > 0) {
|
|
for(CellValueRecordInterface r : expandedRecords) {
|
|
childListener.processRecord((Record)r);
|
|
}
|
|
} else {
|
|
childListener.processRecord(record);
|
|
}
|
|
}
|
|
|
|
private void resetCounts() {
|
|
lastRowRow = -1;
|
|
lastCellRow = -1;
|
|
lastCellColumn = -1;
|
|
}
|
|
}
|