public void expandRow(int rowNumber) {
    int idx = rowNumber;
    if (idx == -1) return;

    // If it is already expanded do nothing.
    if (!isRowGroupCollapsed(idx)) {
      return;
    }

    // Find the start of the group.
    int startIdx = findStartOfRowOutlineGroup(idx);
    RowRecord row = getRow(startIdx);

    // Find the end of the group.
    int endIdx = findEndOfRowOutlineGroup(idx);

    // expand:
    // collapsed bit must be unset
    // hidden bit gets unset _if_ surrounding groups are expanded you can determine
    //   this by looking at the hidden bit of the enclosing group.  You will have
    //   to look at the start and the end of the current group to determine which
    //   is the enclosing group
    // hidden bit only is altered for this outline level.  ie.  don't un-collapse contained groups
    if (!isRowGroupHiddenByParent(idx)) {
      for (int i = startIdx; i <= endIdx; i++) {
        RowRecord otherRow = getRow(i);
        if (row.getOutlineLevel() == otherRow.getOutlineLevel() || !isRowGroupCollapsed(i)) {
          otherRow.setZeroHeight(false);
        }
      }
    }

    // Write collapse field
    getRow(endIdx + 1).setColapsed(false);
  }
 public void insertRow(RowRecord row) {
   // Integer integer = Integer.valueOf(row.getRowNumber());
   _rowRecords.put(Integer.valueOf(row.getRowNumber()), row);
   if ((row.getRowNumber() < _firstrow) || (_firstrow == -1)) {
     _firstrow = row.getRowNumber();
   }
   if ((row.getRowNumber() > _lastrow) || (_lastrow == -1)) {
     _lastrow = row.getRowNumber();
   }
 }
 /**
  * Hide all rows at or below the current outline level
  *
  * @return index of the <em>next<em> row after the last row that gets hidden
  */
 private int writeHidden(RowRecord pRowRecord, int row) {
   int rowIx = row;
   RowRecord rowRecord = pRowRecord;
   int level = rowRecord.getOutlineLevel();
   while (rowRecord != null && getRow(rowIx).getOutlineLevel() >= level) {
     rowRecord.setZeroHeight(true);
     rowIx++;
     rowRecord = getRow(rowIx);
   }
   return rowIx;
 }
  /** Returns the physical row number of the end row in a block */
  private int getEndRowNumberForBlock(int block) {
    int endIndex = ((block + 1) * DBCellRecord.BLOCK_SIZE) - 1;
    if (endIndex >= _rowRecords.size()) endIndex = _rowRecords.size() - 1;

    Iterator<RowRecord> rowIter = _rowRecords.values().iterator();
    RowRecord row = null;
    for (int i = 0; i <= endIndex; i++) {
      row = rowIter.next();
    }
    if (row == null) {
      throw new RuntimeException("Did not find start row for block " + block);
    }
    return row.getRowNumber();
  }
  public int findStartOfRowOutlineGroup(int row) {
    // Find the start of the group.
    RowRecord rowRecord = this.getRow(row);
    int level = rowRecord.getOutlineLevel();
    int currentRow = row;
    while (this.getRow(currentRow) != null) {
      rowRecord = this.getRow(currentRow);
      if (rowRecord.getOutlineLevel() < level) {
        return currentRow + 1;
      }
      currentRow--;
    }

    return currentRow + 1;
  }
  public void collapseRow(int rowNumber) {

    // Find the start of the group.
    int startRow = findStartOfRowOutlineGroup(rowNumber);
    RowRecord rowRecord = getRow(startRow);

    // Hide all the columns until the end of the group
    int nextRowIx = writeHidden(rowRecord, startRow);

    RowRecord row = getRow(nextRowIx);
    if (row == null) {
      row = createRow(nextRowIx);
      insertRow(row);
    }
    // Write collapse field
    row.setColapsed(true);
  }
  /** Returns the physical row number of the first row in a block */
  private int getStartRowNumberForBlock(int block) {
    // Given that we basically iterate through the rows in order,
    // TODO - For a performance improvement, it would be better to return an instance of
    // an iterator and use that instance throughout, rather than recreating one and
    // having to move it to the right position.
    int startIndex = block * DBCellRecord.BLOCK_SIZE;
    Iterator<RowRecord> rowIter = _rowRecords.values().iterator();
    RowRecord row = null;
    // Position the iterator at the start of the block
    for (int i = 0; i <= startIndex; i++) {
      row = rowIter.next();
    }
    if (row == null) {
      throw new RuntimeException("Did not find start row for block " + block);
    }

    return row.getRowNumber();
  }
 public void removeRow(RowRecord row) {
   int rowIndex = row.getRowNumber();
   _valuesAgg.removeAllCellsValuesForRow(rowIndex);
   Integer key = Integer.valueOf(rowIndex);
   RowRecord rr = _rowRecords.remove(key);
   if (rr == null) {
     throw new RuntimeException("Invalid row index (" + key.intValue() + ")");
   }
   if (row != rr) {
     _rowRecords.put(key, rr);
     throw new RuntimeException("Attempt to remove row that does not belong to this sheet");
   }
 }
  @Override
  public void processRecord(Record record) {
    switch (record.getSid()) {
      case BoundSheetRecord.sid:
        BoundSheetRecord boundSheetRecord = (BoundSheetRecord) record;
        parsedDataMap.put(boundSheetRecord.getSheetname(), new ArrayList<List<Object>>());
        boundSheetRecords.add(boundSheetRecord);

        missingRows.put(boundSheetRecord.getSheetname(), new ArrayList<Integer>());
        missingCells.put(boundSheetRecord.getSheetname(), new ArrayList<int[]>());
        break;
      case BOFRecord.sid:
        BOFRecord bofRecord = (BOFRecord) record;
        bofRecordType = bofRecord.getType();
        switch (bofRecordType) {
          case BOFRecord.TYPE_WORKBOOK:
            LOG.trace("loading excel data information.");
            break;
          case BOFRecord.TYPE_WORKSHEET:
            sheetIndex += 1;
            sheetName = boundSheetRecords.get(sheetIndex).getSheetname();
            currentSheet = parsedDataMap.get(sheetName);
            originalRowIndex = 0;
            maxColumnlength = -1;

            currentRow = new ArrayList<Object>();
            break;
        }
        break;
      case RowRecord.sid:
        RowRecord rowRecord = (RowRecord) record;
        int firstColumn = rowRecord.getFirstCol();
        int lastColumn = rowRecord.getLastCol();
        int length = lastColumn - firstColumn;
        if (length > maxColumnlength) {
          maxColumnlength = length;
        }
        break;
      case BlankRecord.sid:
        BlankRecord blankRecord = (BlankRecord) record;
        missingCells.get(sheetName).add(new int[] {blankRecord.getColumn(), blankRecord.getRow()});
        currentRow.add(null);
        break;
      case BoolErrRecord.sid:
        BoolErrRecord boolErrRecord = (BoolErrRecord) record;
        if (boolErrRecord.isBoolean()) {
          currentRow.add(boolErrRecord.getErrorValue() == BOOLEAN_CELL_TRUE_FLAG);
        } else if (boolErrRecord.isError()) {
          LOG.warn(boolErrRecord);
        }
        break;
      case FormulaRecord.sid:
        FormulaRecord formulaRecord = (FormulaRecord) record;
        LOG.trace(
            "formulaRecord:[" + formulaRecord.getColumn() + "," + formulaRecord.getRow() + "]");
        break;
      case StringRecord.sid:
        StringRecord stringRecord = (StringRecord) record;
        currentRow.add(stringRecord.getString());
        break;
      case LabelRecord.sid:
        LabelRecord labelRecord = (LabelRecord) record;
        currentRow.add(labelRecord.getValue());
        break;
      case LabelSSTRecord.sid:
        LabelSSTRecord labelSSTRecord = (LabelSSTRecord) record;
        currentRow.add(
            sheetRecordCollectingListener
                .getSSTRecord()
                .getString(labelSSTRecord.getSSTIndex())
                .getString());
        break;
      case NoteRecord.sid:
        NoteRecord notegRecord = (NoteRecord) record;
        LOG.trace("formulaRecord:[" + notegRecord.getColumn() + "," + notegRecord.getRow() + "]");
        break;
      case NumberRecord.sid:
        NumberRecord numberRecord = (NumberRecord) record;
        double numberValue = numberRecord.getValue();
        int formatIndex = formatTrackingHSSFListener.getFormatIndex(numberRecord);
        if (HSSFDateUtil.isInternalDateFormat(formatIndex)) {
          currentRow.add(HSSFDateUtil.getJavaDate(numberValue));
        } else {
          currentRow.add(numberValue);
        }
        break;
      case RKRecord.sid:
        RKRecord pkRecord = (RKRecord) record;
        currentRow.add(pkRecord.getRKNumber());
        break;
      case EOFRecord.sid:
        switch (bofRecordType) {
          case BOFRecord.TYPE_WORKBOOK:
            LOG.trace("loading excel data information complete.");
            break;
          case BOFRecord.TYPE_VB_MODULE:
            break;
          case BOFRecord.TYPE_WORKSHEET:
            int size = currentSheet.size();
            if (size > 0) {
              if (currentSheet.get(size - 1).isEmpty()) {
                currentSheet.remove(size - 1);
              }
            }

            break;
          case BOFRecord.TYPE_CHART:
            break;
          case BOFRecord.TYPE_EXCEL_4_MACRO:
            break;
          case BOFRecord.TYPE_WORKSPACE_FILE:
            break;
        }
        break;
      default:
        if (record instanceof LastCellOfRowDummyRecord) {
          // remove empty row.
          if (!currentRow.isEmpty()) {
            int nullSize = 0;
            for (Object obj : currentRow) {
              if (obj == null) {
                nullSize += 1;
              }
            }
            // remove the row of all member is null
            if (nullSize == currentRow.size()) {
              currentRow.clear();
              missingRows.get(sheetName).add(originalRowIndex);
            } else {
              LastCellOfRowDummyRecord lastCellOfRowDummyRecord = (LastCellOfRowDummyRecord) record;
              if (lastCellOfRowDummyRecord.getLastColumnNumber() > -1) {
                for (int i = currentRow.size(); i < maxColumnlength; i += 1) {
                  missingCells.get(sheetName).add(new int[] {i, lastCellOfRowDummyRecord.getRow()});
                  currentRow.add(null);
                }
              }

              currentSheet.add(currentRow);
              currentRow = new ArrayList<Object>();
            }
          }
          originalRowIndex += 1;
        } else if (record instanceof MissingCellDummyRecord) {
          MissingCellDummyRecord missingCellDummyRecord = (MissingCellDummyRecord) record;
          currentRow.add(null);
          missingCells
              .get(sheetName)
              .add(new int[] {missingCellDummyRecord.getColumn(), missingCellDummyRecord.getRow()});
        } else if (record instanceof MissingRowDummyRecord) {
          MissingRowDummyRecord missingRowDummyRecord = (MissingRowDummyRecord) record;
          missingRows.get(sheetName).add(missingRowDummyRecord.getRowNumber());
        }
        break;
    }
  }