poi。HSSFCell#setAsActiveCellの問題。その2。

先日の記事の修正。


先日の記事の修正ではウィンドウが分割されている場合にアクティブセルが反映されない。
SelectionRecordクラスのfield_1_paneメンバは、そのレコードがどのペイン(分割されたウィンドウ)に属するのか示すらしい。
よって常に3では意味がない。(3というのは分割されている時の左上のペインを示す)


ウィンドウを分割していてもアクティブにできるペインは1つだけであり、アクティブでないペインのSelectionRecordは何を意味してるのかいまいちよくわからない。
分割位置を示していたりアクティブセルを示していたり?


InternalSheetクラスのオリジナルのコードではSelectionRecordが複数あると一番最後のものを_selectionメンバとして参照するようになっているが、この順番はペインの位置で決まっていて必ずしもアクティブなペインに対応するレコードが最後になるとは限らないので、アクティブなペインのレコードを_selectionメンバに持つようにする。
そのためPaneRecordもメンバとして保持する。
また、今の選択範囲にないセルをアクティブにした時に新しく作るSelectionRecordのfield_1_paneメンバを古いものから引き継ぐ。


InternalSheetクラスに下記のメンバを追加。

private PaneRecord _pane = null;

下記のコンストラクタを修正。

private InternalSheet(RecordStream rs) {
  _mergedCellsTable = new MergedCellsTable();
  RowRecordsAggregate rra = null;

  List<RecordBase> records = new ArrayList<RecordBase>(128);
  _records = records; // needed here due to calls to findFirstRecordLocBySid before we're done
  int dimsloc = -1;

  if (rs.peekNextSid() != BOFRecord.sid) {
    throw new RuntimeException("BOF record expected");
  }
  BOFRecord bof = (BOFRecord) rs.getNext();
  if (bof.getType() != BOFRecord.TYPE_WORKSHEET) {
    // TODO - fix junit tests throw new RuntimeException("Bad BOF record type");
  }
  records.add(bof);
  while (rs.hasNext()) {
    int recSid = rs.peekNextSid();

    if ( recSid == CFHeaderRecord.sid ) {
      condFormatting = new ConditionalFormattingTable(rs);
      records.add(condFormatting);
      continue;
    }

    if (recSid == ColumnInfoRecord.sid) {
      _columnInfos = new ColumnInfoRecordsAggregate(rs);
      records.add(_columnInfos);
      continue;
    }
    if ( recSid == DVALRecord.sid) {
      _dataValidityTable = new DataValidityTable(rs);
      records.add(_dataValidityTable);
      continue;
    }

    if (RecordOrderer.isRowBlockRecord(recSid)) {
      //only add the aggregate once
      if (rra != null) {
        throw new RuntimeException("row/cell records found in the wrong place");
      }
      RowBlocksReader rbr = new RowBlocksReader(rs);
      _mergedCellsTable.addRecords(rbr.getLooseMergedCells());
      rra = new RowRecordsAggregate(rbr.getPlainRecordStream(), rbr.getSharedFormulaManager());
      records.add(rra); //only add the aggregate once
      continue;
    }

    if (CustomViewSettingsRecordAggregate.isBeginRecord(recSid)) {
      // This happens three times in test sample file "29982.xls"
      // Also several times in bugzilla samples 46840-23373 and 46840-23374
      records.add(new CustomViewSettingsRecordAggregate(rs));
      continue;
    }

    if (PageSettingsBlock.isComponentRecord(recSid)) {
      if (_psBlock == null) {
        // first PSB record encountered - read all of them:
        _psBlock = new PageSettingsBlock(rs);
        records.add(_psBlock);
      } else {
        // one or more PSB records found after some intervening non-PSB records
        _psBlock.addLateRecords(rs);
      }
      // YK: in some cases records can be moved to the preceding
      // CustomViewSettingsRecordAggregate blocks
      _psBlock.positionRecords(records);
      continue;
    }

    if (WorksheetProtectionBlock.isComponentRecord(recSid)) {
      _protectionBlock.addRecords(rs);
      continue;
    }

    if (recSid == MergeCellsRecord.sid) {
      // when the MergedCellsTable is found in the right place, we expect those records to be contiguous
      _mergedCellsTable.read(rs);
      continue;
    }

    if (recSid == BOFRecord.sid) {
      ChartSubstreamRecordAggregate chartAgg = new ChartSubstreamRecordAggregate(rs);
      if (false) {
        // TODO - would like to keep the chart aggregate packed, but one unit test needs attention
        records.add(chartAgg);
      } else {
        spillAggregate(chartAgg, records);
      }
      continue;
    }

    Record rec = rs.getNext();
    if ( recSid == IndexRecord.sid ) {
      // ignore INDEX record because it is only needed by Excel,
      // and POI always re-calculates its contents
      continue;
    }


    if (recSid == UncalcedRecord.sid) {
      // don't add UncalcedRecord to the list
      _isUncalced = true; // this flag is enough
      continue;
    }

    if (recSid == FeatRecord.sid ||
      recSid == FeatHdrRecord.sid) {
      records.add(rec);
      continue;
    }

    if (recSid == EOFRecord.sid) {
      records.add(rec);
      break;
    }

    if (recSid == DimensionsRecord.sid) {
      // Make a columns aggregate if one hasn't ready been created.
      if (_columnInfos == null) {
        _columnInfos = new ColumnInfoRecordsAggregate();
        records.add(_columnInfos);
      }

      _dimensions    = ( DimensionsRecord ) rec;
      dimsloc = records.size();
    }
    else if (recSid == DefaultColWidthRecord.sid)
    {
      defaultcolwidth = ( DefaultColWidthRecord ) rec;
    }
    else if (recSid == DefaultRowHeightRecord.sid)
    {
      defaultrowheight = ( DefaultRowHeightRecord ) rec;
    }
    else if ( recSid == PrintGridlinesRecord.sid )
    {
      printGridlines = (PrintGridlinesRecord) rec;
    }
    else if ( recSid == GridsetRecord.sid )
    {
      gridset = (GridsetRecord) rec;
    }
    else if ( recSid == SelectionRecord.sid )
    {
      if (_selection == null) {
        _selection = (SelectionRecord) rec;
      } else if (_pane != null && _pane.getActivePane() == ((SelectionRecord) rec).getPane()) {
        _selection = (SelectionRecord) rec;
      }
    }
    else if ( recSid == WindowTwoRecord.sid )
    {
      windowTwo = (WindowTwoRecord) rec;
    }
    else if ( recSid == GutsRecord.sid )
    {
      _gutsRecord = (GutsRecord) rec;
    }
    else if ( recSid == PaneRecord.sid )
    {
      _pane = (PaneRecord) rec;
    }

    records.add(rec);
  }
  if (windowTwo == null) {
    throw new RuntimeException("WINDOW2 was not found");
  }
  if (_dimensions == null) {
    // Excel seems to always write the DIMENSION record, but tolerates when it is not present
    // in all cases Excel (2007) adds the missing DIMENSION record
    if (rra == null) {
      // bug 46206 alludes to files which skip the DIMENSION record
      // when there are no row/cell records.
      // Not clear which application wrote these files.
      rra = new RowRecordsAggregate();
    } else {
      log.log(POILogger.WARN, "DIMENSION record not found even though row/cells present");
      // Not sure if any tools write files like this, but Excel reads them OK
    }
    dimsloc = findFirstRecordLocBySid(WindowTwoRecord.sid);
    _dimensions = rra.createDimensions();
    records.add(dimsloc, _dimensions);
  }
  if (rra == null) {
    rra = new RowRecordsAggregate();
    records.add(dimsloc + 1, rra);
  }
  _rowsAggregate = rra;
  // put merged cells table in the right place (regardless of where the first MergedCellsRecord was found */
  RecordOrderer.addNewSheetRecord(records, _mergedCellsTable);
  RecordOrderer.addNewSheetRecord(records, _protectionBlock);
  if (log.check( POILogger.DEBUG ))
    log.log(POILogger.DEBUG, "sheet createSheet (existing file) exited");
}

下記のメソッドを修正。

public void setActiveCellRow(int row) {
  //shouldn't have a sheet w/o a SelectionRecord, but best to guard anyway
  if (_selection != null) {
    if (_selection.isInRange(row, getActiveCellCol())) {
      _selection.setActiveCellRow(row);
      return;
    }
    SelectionRecord newSelection = new SelectionRecord(row, getActiveCellCol());
    newSelection.setPane(_selection.getPane());
    int index = _records.indexOf(_selection);
    _records.set(index, newSelection);
    _selection = newSelection;
  }
}

public void setActiveCellCol(short col) {
  //shouldn't have a sheet w/o a SelectionRecord, but best to guard anyway
  if (_selection != null) {
    if (_selection.isInRange(getActiveCellRow(), col)) {
      _selection.setActiveCellCol(col);
      return;
    }
    SelectionRecord newSelection = new SelectionRecord(getActiveCellRow(), col);
    newSelection.setPane(_selection.getPane());
    int index = _records.indexOf(_selection);
    _records.set(index, newSelection);
    _selection = newSelection;
  }
}

public void setActiveCell(int row, int col) {
  //shouldn't have a sheet w/o a SelectionRecord, but best to guard anyway
  if (_selection != null) {
    if (_selection.isInRange(row, col)) {
      _selection.setActiveCell(row, col);
      return;
    }
    SelectionRecord newSelection = new SelectionRecord(row, col);
    newSelection.setPane(_selection.getPane());
    int index = _records.indexOf(_selection);
    _records.set(index, newSelection);
    _selection = newSelection;
  }
}