public void expandColumn(short columnNumber) {
    int idx = findColumnIdx(columnNumber, 0);
    if (idx == -1) return;

    // If it is already exapanded do nothing.
    if (!isColumnGroupCollapsed(idx)) return;

    // Find the start of the group.
    int startIdx = findStartOfColumnOutlineGroup(idx);
    ColumnInfoRecord columnInfo = getColInfo(startIdx);

    // Find the end of the group.
    int endIdx = findEndOfColumnOutlineGroup(idx);
    ColumnInfoRecord endColumnInfo = getColInfo(endIdx);

    // expand:
    // colapsed 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 uncollapse contained groups
    if (!isColumnGroupHiddenByParent(idx)) {
      for (int i = startIdx; i <= endIdx; i++) {
        if (columnInfo.getOutlineLevel() == getColInfo(i).getOutlineLevel())
          getColInfo(i).setHidden(false);
      }
    }

    // Write collapse field
    setColumn((short) (columnInfo.getLastColumn() + 1), null, null, null, null, Boolean.FALSE);
  }
 /** Performs a deep clone of the record */
 public Object clone() {
   ColumnInfoRecordsAggregate rec = new ColumnInfoRecordsAggregate();
   for (int k = 0; k < records.size(); k++) {
     ColumnInfoRecord ci = (ColumnInfoRecord) records.get(k);
     ci = (ColumnInfoRecord) ci.clone();
     rec.insertColumn(ci);
   }
   return rec;
 }
  /**
   * creates the ColumnInfo Record and sets it to a default column/width
   *
   * @see loci.poi.hssf.record.ColumnInfoRecord
   * @return record containing a ColumnInfoRecord
   */
  public static Record createColInfo() {
    ColumnInfoRecord retval = new ColumnInfoRecord();

    retval.setColumnWidth((short) 2275);
    // was:       retval.setOptions(( short ) 6);
    retval.setOptions((short) 2);
    retval.setXFIndex((short) 0x0f);
    return retval;
  }
 /** Sets all non null fields into the <code>ci</code> parameter. */
 private void setColumnInfoFields(
     ColumnInfoRecord ci,
     Short xfStyle,
     Short width,
     Integer level,
     Boolean hidden,
     Boolean collapsed) {
   if (xfStyle != null) ci.setXFIndex(xfStyle.shortValue());
   if (width != null) ci.setColumnWidth(width.shortValue());
   if (level != null) ci.setOutlineLevel(level.shortValue());
   if (hidden != null) ci.setHidden(hidden.booleanValue());
   if (collapsed != null) ci.setCollapsed(collapsed.booleanValue());
 }
  public void collapseColumn(short columnNumber) {
    int idx = findColumnIdx(columnNumber, 0);
    if (idx == -1) return;

    // Find the start of the group.
    ColumnInfoRecord columnInfo =
        (ColumnInfoRecord) records.get(findStartOfColumnOutlineGroup(idx));

    // Hide all the columns until the end of the group
    columnInfo = writeHidden(columnInfo, idx, true);

    // Write collapse field
    setColumn((short) (columnInfo.getLastColumn() + 1), null, null, null, null, Boolean.TRUE);
  }
  public int findColumnIdx(int column, int fromIdx) {
    if (column < 0) throw new IllegalArgumentException("column parameter out of range: " + column);
    if (fromIdx < 0)
      throw new IllegalArgumentException("fromIdx parameter out of range: " + fromIdx);

    ColumnInfoRecord ci;
    for (int k = fromIdx; k < records.size(); k++) {
      ci = (ColumnInfoRecord) records.get(k);
      if ((ci.getFirstColumn() <= column) && (column <= ci.getLastColumn())) {
        return k;
      }
      ci = null;
    }
    return -1;
  }
 public ColumnInfoRecord writeHidden(ColumnInfoRecord columnInfo, int idx, boolean hidden) {
   int level = columnInfo.getOutlineLevel();
   while (idx < records.size()) {
     columnInfo.setHidden(hidden);
     if (idx + 1 < records.size()) {
       ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) records.get(idx + 1);
       if (columnInfo.getLastColumn() + 1 == nextColumnInfo.getFirstColumn()) {
         if (nextColumnInfo.getOutlineLevel() < level) break;
         columnInfo = nextColumnInfo;
       } else {
         break;
       }
     }
     idx++;
   }
   return columnInfo;
 }
  public int findEndOfColumnOutlineGroup(int idx) {
    // Find the end of the group.
    ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get(idx);
    int level = columnInfo.getOutlineLevel();
    while (idx < records.size() - 1) {
      ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) records.get(idx + 1);
      if (columnInfo.getLastColumn() + 1 == nextColumnInfo.getFirstColumn()) {
        if (nextColumnInfo.getOutlineLevel() < level) {
          break;
        }
        idx++;
        columnInfo = nextColumnInfo;
      } else {
        break;
      }
    }

    return idx;
  }
  public int findStartOfColumnOutlineGroup(int idx) {
    // Find the start of the group.
    ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get(idx);
    int level = columnInfo.getOutlineLevel();
    while (idx != 0) {
      ColumnInfoRecord prevColumnInfo = (ColumnInfoRecord) records.get(idx - 1);
      if (columnInfo.getFirstColumn() - 1 == prevColumnInfo.getLastColumn()) {
        if (prevColumnInfo.getOutlineLevel() < level) {
          break;
        }
        idx--;
        columnInfo = prevColumnInfo;
      } else {
        break;
      }
    }

    return idx;
  }
  public void collapseColInfoRecords(int columnIdx) {
    if (columnIdx == 0) return;
    ColumnInfoRecord previousCol = (ColumnInfoRecord) records.get(columnIdx - 1);
    ColumnInfoRecord currentCol = (ColumnInfoRecord) records.get(columnIdx);
    boolean adjacentColumns = previousCol.getLastColumn() == currentCol.getFirstColumn() - 1;
    if (!adjacentColumns) return;

    boolean columnsMatch =
        previousCol.getXFIndex() == currentCol.getXFIndex()
            && previousCol.getOptions() == currentCol.getOptions()
            && previousCol.getColumnWidth() == currentCol.getColumnWidth();

    if (columnsMatch) {
      previousCol.setLastColumn(currentCol.getLastColumn());
      records.remove(columnIdx);
    }
  }
  public void setColumn(
      short column, Short xfIndex, Short width, Integer level, Boolean hidden, Boolean collapsed) {
    ColumnInfoRecord ci = null;
    int k = 0;

    for (k = 0; k < records.size(); k++) {
      ci = (ColumnInfoRecord) records.get(k);
      if ((ci.getFirstColumn() <= column) && (column <= ci.getLastColumn())) {
        break;
      }
      ci = null;
    }

    if (ci != null) {
      boolean styleChanged = xfIndex != null && ci.getXFIndex() != xfIndex.shortValue();
      boolean widthChanged = width != null && ci.getColumnWidth() != width.shortValue();
      boolean levelChanged = level != null && ci.getOutlineLevel() != level.intValue();
      boolean hiddenChanged = hidden != null && ci.getHidden() != hidden.booleanValue();
      boolean collapsedChanged = collapsed != null && ci.getCollapsed() != collapsed.booleanValue();
      boolean columnChanged =
          styleChanged || widthChanged || levelChanged || hiddenChanged || collapsedChanged;
      if (!columnChanged) {
        // do nothing...nothing changed.
      } else if ((ci.getFirstColumn() == column)
          && (ci.getLastColumn() == column)) { // if its only for this cell then
        setColumnInfoFields(ci, xfIndex, width, level, hidden, collapsed);
      } else if ((ci.getFirstColumn() == column) || (ci.getLastColumn() == column)) {
        // okay so the width is different but the first or last column == the column we'return
        // setting
        // we'll just divide the info and create a new one
        if (ci.getFirstColumn() == column) {
          ci.setFirstColumn((short) (column + 1));
        } else {
          ci.setLastColumn((short) (column - 1));
        }
        ColumnInfoRecord nci = (ColumnInfoRecord) createColInfo();

        nci.setFirstColumn(column);
        nci.setLastColumn(column);
        nci.setOptions(ci.getOptions());
        nci.setXFIndex(ci.getXFIndex());
        setColumnInfoFields(nci, xfIndex, width, level, hidden, collapsed);

        insertColumn(k, nci);
      } else {
        // split to 3 records
        short lastcolumn = ci.getLastColumn();
        ci.setLastColumn((short) (column - 1));

        ColumnInfoRecord nci = (ColumnInfoRecord) createColInfo();
        nci.setFirstColumn(column);
        nci.setLastColumn(column);
        nci.setOptions(ci.getOptions());
        nci.setXFIndex(ci.getXFIndex());
        setColumnInfoFields(nci, xfIndex, width, level, hidden, collapsed);
        insertColumn(++k, nci);

        nci = (ColumnInfoRecord) createColInfo();
        nci.setFirstColumn((short) (column + 1));
        nci.setLastColumn(lastcolumn);
        nci.setOptions(ci.getOptions());
        nci.setXFIndex(ci.getXFIndex());
        nci.setColumnWidth(ci.getColumnWidth());
        insertColumn(++k, nci);
      }
    } else {

      // okay so there ISN'T a column info record that cover's this column so lets create one!
      ColumnInfoRecord nci = (ColumnInfoRecord) createColInfo();

      nci.setFirstColumn(column);
      nci.setLastColumn(column);
      setColumnInfoFields(nci, xfIndex, width, level, hidden, collapsed);
      insertColumn(k, nci);
    }
  }