blob: ea1585d9e9e8d7943e1788a02a39fbddff001551 [file] [log] [blame]
package org.apache.poi.hssf.record.aggregates;
import org.apache.poi.hssf.record.ColumnInfoRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RecordInputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @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();
}
/**
* Performs a deep clone of the record
*/
public Object clone()
{
ColumnInfoRecordsAggregate rec = new ColumnInfoRecordsAggregate();
for ( Iterator colIter = getIterator(); colIter.hasNext(); )
{
//return the cloned Row Record & insert
ColumnInfoRecord col = (ColumnInfoRecord) ( (ColumnInfoRecord) colIter.next() ).clone();
rec.insertColumn( col );
}
return rec;
}
/**
* Inserts a column into the aggregate (at the end of the list).
*/
public void insertColumn( ColumnInfoRecord col )
{
records.add( col );
}
/**
* Inserts a column into the aggregate (at the position specified
* by <code>idx</code>.
*/
public void insertColumn( int idx, ColumnInfoRecord col )
{
records.add( idx, col );
}
public int getNumColumns( )
{
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 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 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 ColumnInfoRecord getColInfo(int idx)
{
return (ColumnInfoRecord) records.get( idx );
}
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 boolean isColumnGroupCollapsed( int idx )
{
int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx );
if (endOfOutlineGroupIdx >= records.size())
return false;
if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn())
return false;
else
return getColInfo(endOfOutlineGroupIdx+1).getCollapsed();
}
public boolean isColumnGroupHiddenByParent( int idx )
{
// Look out outline details of end
int endLevel;
boolean endHidden;
int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx );
if (endOfOutlineGroupIdx >= records.size())
{
endLevel = 0;
endHidden = false;
}
else if (getColInfo(endOfOutlineGroupIdx).getLastColumn() + 1 != getColInfo(endOfOutlineGroupIdx + 1).getFirstColumn())
{
endLevel = 0;
endHidden = false;
}
else
{
endLevel = getColInfo( endOfOutlineGroupIdx + 1).getOutlineLevel();
endHidden = getColInfo( endOfOutlineGroupIdx + 1).getHidden();
}
// Look out outline details of start
int startLevel;
boolean startHidden;
int startOfOutlineGroupIdx = findStartOfColumnOutlineGroup( idx );
if (startOfOutlineGroupIdx <= 0)
{
startLevel = 0;
startHidden = false;
}
else if (getColInfo(startOfOutlineGroupIdx).getFirstColumn() - 1 != getColInfo(startOfOutlineGroupIdx - 1).getLastColumn())
{
startLevel = 0;
startHidden = false;
}
else
{
startLevel = getColInfo( startOfOutlineGroupIdx - 1).getOutlineLevel();
startHidden = getColInfo( startOfOutlineGroupIdx - 1 ).getHidden();
}
if (endLevel > startLevel)
{
return endHidden;
}
else
{
return startHidden;
}
}
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, Boolean.TRUE);
}
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, Boolean.FALSE);
}
/**
* creates the ColumnInfo Record and sets it to a default column/width
* @see org.apache.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;
}
public void setColumn(short column, 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 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 = 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, 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, 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, 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, width, level, hidden, collapsed );
insertColumn(k, nci);
}
}
/**
* Sets all non null fields into the <code>ci</code> parameter.
*/
private void setColumnInfoFields( ColumnInfoRecord ci, Short width, Integer level, Boolean hidden, Boolean collapsed )
{
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 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 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 );
}
}
/**
* Creates an outline group for the specified columns.
* @param fromColumn group from this column (inclusive)
* @param toColumn group to this column (inclusive)
* @param indent if true the group will be indented by one level,
* if false indenting will be removed by one level.
*/
public void groupColumnRange(short fromColumn, short toColumn, boolean indent)
{
// Set the level for each column
int fromIdx = 0;
for (int i = fromColumn; i <= toColumn; i++)
{
int level = 1;
int columnIdx = findColumnIdx( i, Math.max(0,fromIdx) );
if (columnIdx != -1)
{
level = ((ColumnInfoRecord)records.get( columnIdx )).getOutlineLevel();
if (indent) level++; else level--;
level = Math.max(0, level);
level = Math.min(7, level);
fromIdx = columnIdx - 1; // subtract 1 just in case this column is collapsed later.
}
setColumn((short)i, null, new Integer(level), null, null);
columnIdx = findColumnIdx( i, Math.max(0, fromIdx ) );
collapseColInfoRecords( columnIdx );
}
}
}