Fix for bug 45639 - cleaned up index logic inside ColumnInfoRecordsAggregate
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@694534 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
2e6ca61fe7
commit
9ec9c9251f
@ -37,6 +37,7 @@
|
||||
|
||||
<!-- Don't forget to update status.xml too! -->
|
||||
<release version="3.1.1-alpha1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">45639 - Fixed AIOOBE due to bad index logic in ColumnInfoRecordsAggregate</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">Fixed special cases of INDEX function (single column/single row, errors)</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45761 - Support for Very Hidden excel sheets in HSSF</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45738 - Initial HWPF support for Office Art Shapes</action>
|
||||
|
@ -34,6 +34,7 @@
|
||||
<!-- Don't forget to update changes.xml too! -->
|
||||
<changes>
|
||||
<release version="3.1.1-alpha1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">45639 - Fixed AIOOBE due to bad index logic in ColumnInfoRecordsAggregate</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">Fixed special cases of INDEX function (single column/single row, errors)</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45761 - Support for Very Hidden excel sheets in HSSF</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45738 - Initial HWPF support for Office Art Shapes</action>
|
||||
|
@ -1055,7 +1055,7 @@ public final class Sheet implements Model {
|
||||
|
||||
ColumnInfoRecord ci = _columnInfos.findColumnInfo(columnIndex);
|
||||
if (ci != null) {
|
||||
return ci.getColumnWidth();
|
||||
return (short)ci.getColumnWidth();
|
||||
}
|
||||
//default column width is measured in characters
|
||||
//multiply
|
||||
@ -1079,8 +1079,8 @@ public final class Sheet implements Model {
|
||||
public short getXFIndexForColAt(short columnIndex) {
|
||||
ColumnInfoRecord ci = _columnInfos.findColumnInfo(columnIndex);
|
||||
if (ci != null) {
|
||||
return ci.getXFIndex();
|
||||
}
|
||||
return (short)ci.getXFIndex();
|
||||
}
|
||||
return 0xF;
|
||||
}
|
||||
|
||||
@ -1138,8 +1138,7 @@ public final class Sheet implements Model {
|
||||
* @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)
|
||||
{
|
||||
public void groupColumnRange(int fromColumn, int toColumn, boolean indent) {
|
||||
|
||||
// Set the level for each column
|
||||
_columnInfos.groupColumnRange( fromColumn, toColumn, indent);
|
||||
@ -1709,17 +1708,13 @@ public final class Sheet implements Model {
|
||||
}
|
||||
|
||||
|
||||
public void setColumnGroupCollapsed( short columnNumber, boolean collapsed )
|
||||
{
|
||||
if (collapsed)
|
||||
{
|
||||
_columnInfos.collapseColumn( columnNumber );
|
||||
}
|
||||
else
|
||||
{
|
||||
_columnInfos.expandColumn( columnNumber );
|
||||
}
|
||||
}
|
||||
public void setColumnGroupCollapsed(int columnNumber, boolean collapsed) {
|
||||
if (collapsed) {
|
||||
_columnInfos.collapseColumn(columnNumber);
|
||||
} else {
|
||||
_columnInfos.expandColumn(columnNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* protect a spreadsheet with a password (not encypted, just sets protect
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
import org.apache.poi.util.HexDump;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.BitField;
|
||||
import org.apache.poi.util.BitFieldFactory;
|
||||
@ -30,19 +31,24 @@ import org.apache.poi.util.BitFieldFactory;
|
||||
*/
|
||||
public final class ColumnInfoRecord extends Record {
|
||||
public static final short sid = 0x7d;
|
||||
private short field_1_first_col;
|
||||
private short field_2_last_col;
|
||||
private short field_3_col_width;
|
||||
private short field_4_xf_index;
|
||||
private short field_5_options;
|
||||
private int field_1_first_col;
|
||||
private int field_2_last_col;
|
||||
private int field_3_col_width;
|
||||
private int field_4_xf_index;
|
||||
private int field_5_options;
|
||||
private static final BitField hidden = BitFieldFactory.getInstance(0x01);
|
||||
private static final BitField outlevel = BitFieldFactory.getInstance(0x0700);
|
||||
private static final BitField collapsed = BitFieldFactory.getInstance(0x1000);
|
||||
// Excel seems write values 2, 10, and 260, even though spec says "must be zero"
|
||||
private short field_6_reserved;
|
||||
|
||||
public ColumnInfoRecord()
|
||||
{
|
||||
/**
|
||||
* Creates a column info record with default width and format
|
||||
*/
|
||||
public ColumnInfoRecord() {
|
||||
setColumnWidth(2275);
|
||||
field_5_options = 2;
|
||||
field_4_xf_index = 0x0f;
|
||||
field_6_reserved = 2; // seems to be the most common value
|
||||
}
|
||||
|
||||
@ -90,7 +96,7 @@ public final class ColumnInfoRecord extends Record {
|
||||
* @param fc - the first column index (0-based)
|
||||
*/
|
||||
|
||||
public void setFirstColumn(short fc)
|
||||
public void setFirstColumn(int fc)
|
||||
{
|
||||
field_1_first_col = fc;
|
||||
}
|
||||
@ -100,7 +106,7 @@ public final class ColumnInfoRecord extends Record {
|
||||
* @param lc - the last column index (0-based)
|
||||
*/
|
||||
|
||||
public void setLastColumn(short lc)
|
||||
public void setLastColumn(int lc)
|
||||
{
|
||||
field_2_last_col = lc;
|
||||
}
|
||||
@ -110,7 +116,7 @@ public final class ColumnInfoRecord extends Record {
|
||||
* @param cw - column width
|
||||
*/
|
||||
|
||||
public void setColumnWidth(short cw)
|
||||
public void setColumnWidth(int cw)
|
||||
{
|
||||
field_3_col_width = cw;
|
||||
}
|
||||
@ -121,20 +127,11 @@ public final class ColumnInfoRecord extends Record {
|
||||
* @see org.apache.poi.hssf.record.ExtendedFormatRecord
|
||||
*/
|
||||
|
||||
public void setXFIndex(short xfi)
|
||||
public void setXFIndex(int xfi)
|
||||
{
|
||||
field_4_xf_index = xfi;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the options bitfield - use the bitsetters instead
|
||||
* @param options - the bitfield raw value
|
||||
*/
|
||||
|
||||
public void setOptions(short options)
|
||||
{
|
||||
field_5_options = options;
|
||||
}
|
||||
|
||||
// start options bitfield
|
||||
|
||||
@ -146,7 +143,7 @@ public final class ColumnInfoRecord extends Record {
|
||||
|
||||
public void setHidden(boolean ishidden)
|
||||
{
|
||||
field_5_options = hidden.setShortBoolean(field_5_options, ishidden);
|
||||
field_5_options = hidden.setBoolean(field_5_options, ishidden);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,9 +152,9 @@ public final class ColumnInfoRecord extends Record {
|
||||
* @param olevel -outline level for the cells
|
||||
*/
|
||||
|
||||
public void setOutlineLevel(short olevel)
|
||||
public void setOutlineLevel(int olevel)
|
||||
{
|
||||
field_5_options = outlevel.setShortValue(field_5_options, olevel);
|
||||
field_5_options = outlevel.setValue(field_5_options, olevel);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,7 +165,7 @@ public final class ColumnInfoRecord extends Record {
|
||||
|
||||
public void setCollapsed(boolean iscollapsed)
|
||||
{
|
||||
field_5_options = collapsed.setShortBoolean(field_5_options,
|
||||
field_5_options = collapsed.setBoolean(field_5_options,
|
||||
iscollapsed);
|
||||
}
|
||||
|
||||
@ -179,7 +176,7 @@ public final class ColumnInfoRecord extends Record {
|
||||
* @return the first column index (0-based)
|
||||
*/
|
||||
|
||||
public short getFirstColumn()
|
||||
public int getFirstColumn()
|
||||
{
|
||||
return field_1_first_col;
|
||||
}
|
||||
@ -189,7 +186,7 @@ public final class ColumnInfoRecord extends Record {
|
||||
* @return the last column index (0-based)
|
||||
*/
|
||||
|
||||
public short getLastColumn()
|
||||
public int getLastColumn()
|
||||
{
|
||||
return field_2_last_col;
|
||||
}
|
||||
@ -199,7 +196,7 @@ public final class ColumnInfoRecord extends Record {
|
||||
* @return column width
|
||||
*/
|
||||
|
||||
public short getColumnWidth()
|
||||
public int getColumnWidth()
|
||||
{
|
||||
return field_3_col_width;
|
||||
}
|
||||
@ -210,21 +207,18 @@ public final class ColumnInfoRecord extends Record {
|
||||
* @see org.apache.poi.hssf.record.ExtendedFormatRecord
|
||||
*/
|
||||
|
||||
public short getXFIndex()
|
||||
public int getXFIndex()
|
||||
{
|
||||
return field_4_xf_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the options bitfield - use the bitsetters instead
|
||||
* @return the bitfield raw value
|
||||
*/
|
||||
|
||||
public short getOptions()
|
||||
{
|
||||
public int getOptions() {
|
||||
return field_5_options;
|
||||
}
|
||||
|
||||
public void setOptions(int field_5_options) {
|
||||
this.field_5_options = field_5_options;
|
||||
}
|
||||
|
||||
// start options bitfield
|
||||
|
||||
/**
|
||||
@ -244,9 +238,9 @@ public final class ColumnInfoRecord extends Record {
|
||||
* @return outline level for the cells
|
||||
*/
|
||||
|
||||
public short getOutlineLevel()
|
||||
public int getOutlineLevel()
|
||||
{
|
||||
return outlevel.getShortValue(field_5_options);
|
||||
return outlevel.getValue(field_5_options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -261,6 +255,31 @@ public final class ColumnInfoRecord extends Record {
|
||||
}
|
||||
|
||||
// end options bitfield
|
||||
|
||||
public boolean containsColumn(int columnIndex) {
|
||||
return field_1_first_col <= columnIndex && columnIndex <= field_2_last_col;
|
||||
}
|
||||
public boolean isAdjacentBefore(ColumnInfoRecord other) {
|
||||
return field_2_last_col == other.field_1_first_col - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the format, options and column width match
|
||||
*/
|
||||
public boolean formatMatches(ColumnInfoRecord other) {
|
||||
if (field_4_xf_index != other.field_4_xf_index) {
|
||||
return false;
|
||||
}
|
||||
if (field_5_options != other.field_5_options) {
|
||||
return false;
|
||||
}
|
||||
if (field_3_col_width != other.field_3_col_width) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public short getSid()
|
||||
{
|
||||
return sid;
|
||||
@ -269,13 +288,13 @@ public final class ColumnInfoRecord extends Record {
|
||||
public int serialize(int offset, byte [] data)
|
||||
{
|
||||
LittleEndian.putShort(data, 0 + offset, sid);
|
||||
LittleEndian.putShort(data, 2 + offset, ( short ) 12);
|
||||
LittleEndian.putShort(data, 4 + offset, getFirstColumn());
|
||||
LittleEndian.putShort(data, 6 + offset, getLastColumn());
|
||||
LittleEndian.putShort(data, 8 + offset, getColumnWidth());
|
||||
LittleEndian.putShort(data, 10 + offset, getXFIndex());
|
||||
LittleEndian.putShort(data, 12 + offset, getOptions());
|
||||
LittleEndian.putShort(data, 14 + offset, field_6_reserved);
|
||||
LittleEndian.putUShort(data, 2 + offset, 12);
|
||||
LittleEndian.putUShort(data, 4 + offset, getFirstColumn());
|
||||
LittleEndian.putUShort(data, 6 + offset, getLastColumn());
|
||||
LittleEndian.putUShort(data, 8 + offset, getColumnWidth());
|
||||
LittleEndian.putUShort(data, 10 + offset, getXFIndex());
|
||||
LittleEndian.putUShort(data, 12 + offset, field_5_options);
|
||||
LittleEndian.putUShort(data, 14 + offset, field_6_reserved);
|
||||
return getRecordSize();
|
||||
}
|
||||
|
||||
@ -286,24 +305,19 @@ public final class ColumnInfoRecord extends Record {
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
buffer.append("[COLINFO]\n");
|
||||
buffer.append("colfirst = ").append(getFirstColumn())
|
||||
.append("\n");
|
||||
buffer.append("collast = ").append(getLastColumn())
|
||||
.append("\n");
|
||||
buffer.append("colwidth = ").append(getColumnWidth())
|
||||
.append("\n");
|
||||
buffer.append("xfindex = ").append(getXFIndex()).append("\n");
|
||||
buffer.append("options = ").append(getOptions()).append("\n");
|
||||
buffer.append(" hidden = ").append(getHidden()).append("\n");
|
||||
buffer.append(" olevel = ").append(getOutlineLevel())
|
||||
.append("\n");
|
||||
buffer.append(" collapsed = ").append(getCollapsed())
|
||||
.append("\n");
|
||||
buffer.append("[/COLINFO]\n");
|
||||
return buffer.toString();
|
||||
sb.append("[COLINFO]\n");
|
||||
sb.append(" colfirst = ").append(getFirstColumn()).append("\n");
|
||||
sb.append(" collast = ").append(getLastColumn()).append("\n");
|
||||
sb.append(" colwidth = ").append(getColumnWidth()).append("\n");
|
||||
sb.append(" xfindex = ").append(getXFIndex()).append("\n");
|
||||
sb.append(" options = ").append(HexDump.shortToHex(field_5_options)).append("\n");
|
||||
sb.append(" hidden = ").append(getHidden()).append("\n");
|
||||
sb.append(" olevel = ").append(getOutlineLevel()).append("\n");
|
||||
sb.append(" collapsed= ").append(getCollapsed()).append("\n");
|
||||
sb.append("[/COLINFO]\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public Object clone() {
|
||||
|
@ -18,18 +18,35 @@
|
||||
package org.apache.poi.hssf.record.aggregates;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.model.RecordStream;
|
||||
import org.apache.poi.hssf.record.ColumnInfoRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
|
||||
/**
|
||||
* @author Glen Stampoultzis
|
||||
* @version $Id$
|
||||
*/
|
||||
public final class ColumnInfoRecordsAggregate extends RecordAggregate {
|
||||
/**
|
||||
* List of {@link ColumnInfoRecord}s assumed to be in order
|
||||
*/
|
||||
private final List records;
|
||||
|
||||
|
||||
private static final class CIRComparator implements Comparator {
|
||||
public static final Comparator instance = new CIRComparator();
|
||||
private CIRComparator() {
|
||||
// enforce singleton
|
||||
}
|
||||
public int compare(Object a, Object b) {
|
||||
return compareColInfos((ColumnInfoRecord)a, (ColumnInfoRecord)b);
|
||||
}
|
||||
public static int compareColInfos(ColumnInfoRecord a, ColumnInfoRecord b) {
|
||||
return a.getFirstColumn()-b.getFirstColumn();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty aggregate
|
||||
@ -37,486 +54,470 @@ public final class ColumnInfoRecordsAggregate extends RecordAggregate {
|
||||
public ColumnInfoRecordsAggregate() {
|
||||
records = new ArrayList();
|
||||
}
|
||||
public ColumnInfoRecordsAggregate(RecordStream rs) {
|
||||
this();
|
||||
|
||||
while(rs.peekNextClass() == ColumnInfoRecord.class) {
|
||||
records.add(rs.getNext());
|
||||
}
|
||||
if (records.size() < 1) {
|
||||
throw new RuntimeException("No column info records found");
|
||||
}
|
||||
}
|
||||
public ColumnInfoRecordsAggregate(RecordStream rs) {
|
||||
this();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
boolean isInOrder = true;
|
||||
ColumnInfoRecord cirPrev = null;
|
||||
while(rs.peekNextClass() == ColumnInfoRecord.class) {
|
||||
ColumnInfoRecord cir = (ColumnInfoRecord) rs.getNext();
|
||||
records.add(cir);
|
||||
if (cirPrev != null && CIRComparator.compareColInfos(cirPrev, cir) > 0) {
|
||||
isInOrder = false;
|
||||
}
|
||||
cirPrev = cir;
|
||||
}
|
||||
if (records.size() < 1) {
|
||||
throw new RuntimeException("No column info records found");
|
||||
}
|
||||
if (!isInOrder) {
|
||||
Collections.sort(records, CIRComparator.instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a column into the aggregate (at the end of the list).
|
||||
*/
|
||||
public void insertColumn( ColumnInfoRecord col )
|
||||
{
|
||||
records.add( col );
|
||||
}
|
||||
/**
|
||||
* 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);
|
||||
rec.records.add(ci.clone());
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
/**
|
||||
* Inserts a column into the aggregate (at the end of the list).
|
||||
*/
|
||||
public void insertColumn(ColumnInfoRecord col) {
|
||||
records.add(col);
|
||||
Collections.sort(records, CIRComparator.instance);
|
||||
}
|
||||
|
||||
public int getNumColumns( )
|
||||
{
|
||||
return records.size();
|
||||
}
|
||||
/**
|
||||
* Inserts a column into the aggregate (at the position specified by
|
||||
* <code>idx</code>.
|
||||
*/
|
||||
private void insertColumn(int idx, ColumnInfoRecord col) {
|
||||
records.add(idx, col);
|
||||
}
|
||||
|
||||
public void visitContainedRecords(RecordVisitor rv) {
|
||||
int nItems = records.size();
|
||||
if (nItems < 1) {
|
||||
return;
|
||||
}
|
||||
for(int i=0; i<nItems; i++) {
|
||||
rv.visitRecord((Record)records.get(i));
|
||||
}
|
||||
}
|
||||
/* package */ int getNumColumns() {
|
||||
return records.size();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
public void visitContainedRecords(RecordVisitor rv) {
|
||||
int nItems = records.size();
|
||||
if (nItems < 1) {
|
||||
return;
|
||||
}
|
||||
ColumnInfoRecord cirPrev = null;
|
||||
for(int i=0; i<nItems; i++) {
|
||||
ColumnInfoRecord cir = (ColumnInfoRecord)records.get(i);
|
||||
rv.visitRecord(cir);
|
||||
if (cirPrev != null && CIRComparator.compareColInfos(cirPrev, cir) > 0) {
|
||||
// Excel probably wouldn't mind, but there is much logic in this class
|
||||
// that assumes the column info records are kept in order
|
||||
throw new RuntimeException("Column info records are out of order");
|
||||
}
|
||||
cirPrev = cir;
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
private int findStartOfColumnOutlineGroup(int pIdx) {
|
||||
// Find the start of the group.
|
||||
ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get(pIdx);
|
||||
int level = columnInfo.getOutlineLevel();
|
||||
int idx = pIdx;
|
||||
while (idx != 0) {
|
||||
ColumnInfoRecord prevColumnInfo = (ColumnInfoRecord) records.get(idx - 1);
|
||||
if (!prevColumnInfo.isAdjacentBefore(columnInfo)) {
|
||||
break;
|
||||
}
|
||||
if (prevColumnInfo.getOutlineLevel() < level) {
|
||||
break;
|
||||
}
|
||||
idx--;
|
||||
columnInfo = prevColumnInfo;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
private int findEndOfColumnOutlineGroup(int colInfoIndex) {
|
||||
// Find the end of the group.
|
||||
ColumnInfoRecord columnInfo = (ColumnInfoRecord) records.get(colInfoIndex);
|
||||
int level = columnInfo.getOutlineLevel();
|
||||
int idx = colInfoIndex;
|
||||
while (idx < records.size() - 1) {
|
||||
ColumnInfoRecord nextColumnInfo = (ColumnInfoRecord) records.get(idx + 1);
|
||||
if (!columnInfo.isAdjacentBefore(nextColumnInfo)) {
|
||||
break;
|
||||
}
|
||||
if (nextColumnInfo.getOutlineLevel() < level) {
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
columnInfo = nextColumnInfo;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
private ColumnInfoRecord getColInfo(int idx) {
|
||||
return (ColumnInfoRecord) records.get( idx );
|
||||
}
|
||||
private 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 = getColInfo(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();
|
||||
}
|
||||
/**
|
||||
* 'Collapsed' state is stored in a single column col info record immediately after the outline group
|
||||
* @param idx
|
||||
* @return
|
||||
*/
|
||||
private boolean isColumnGroupCollapsed(int idx) {
|
||||
int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup(idx);
|
||||
int nextColInfoIx = endOfOutlineGroupIdx+1;
|
||||
if (nextColInfoIx >= records.size()) {
|
||||
return false;
|
||||
}
|
||||
ColumnInfoRecord nextColInfo = getColInfo(nextColInfoIx);
|
||||
if (!getColInfo(endOfOutlineGroupIdx).isAdjacentBefore(nextColInfo)) {
|
||||
return false;
|
||||
}
|
||||
return nextColInfo.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();
|
||||
}
|
||||
private boolean isColumnGroupHiddenByParent(int idx) {
|
||||
// Look out outline details of end
|
||||
int endLevel = 0;
|
||||
boolean endHidden = false;
|
||||
int endOfOutlineGroupIdx = findEndOfColumnOutlineGroup( idx );
|
||||
if (endOfOutlineGroupIdx < records.size()) {
|
||||
ColumnInfoRecord nextInfo = getColInfo(endOfOutlineGroupIdx + 1);
|
||||
if (getColInfo(endOfOutlineGroupIdx).isAdjacentBefore(nextInfo)) {
|
||||
endLevel = nextInfo.getOutlineLevel();
|
||||
endHidden = nextInfo.getHidden();
|
||||
}
|
||||
}
|
||||
// Look out outline details of start
|
||||
int startLevel = 0;
|
||||
boolean startHidden = false;
|
||||
int startOfOutlineGroupIdx = findStartOfColumnOutlineGroup( idx );
|
||||
if (startOfOutlineGroupIdx > 0) {
|
||||
ColumnInfoRecord prevInfo = getColInfo(startOfOutlineGroupIdx - 1);
|
||||
if (prevInfo.isAdjacentBefore(getColInfo(startOfOutlineGroupIdx))) {
|
||||
startLevel = prevInfo.getOutlineLevel();
|
||||
startHidden = prevInfo.getHidden();
|
||||
}
|
||||
}
|
||||
if (endLevel > startLevel) {
|
||||
return endHidden;
|
||||
}
|
||||
return startHidden;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
public void collapseColumn(int columnIndex) {
|
||||
int colInfoIx = findColInfoIdx(columnIndex, 0);
|
||||
if (colInfoIx == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (endLevel > startLevel)
|
||||
{
|
||||
return endHidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
return startHidden;
|
||||
}
|
||||
}
|
||||
// Find the start of the group.
|
||||
int groupStartColInfoIx = findStartOfColumnOutlineGroup(colInfoIx);
|
||||
ColumnInfoRecord columnInfo = getColInfo(groupStartColInfoIx);
|
||||
|
||||
public void collapseColumn( short columnNumber )
|
||||
{
|
||||
int idx = findColumnIdx( columnNumber, 0 );
|
||||
if (idx == -1)
|
||||
return;
|
||||
// Hide all the columns until the end of the group
|
||||
int lastColIx = setGroupHidden(groupStartColInfoIx, columnInfo.getOutlineLevel(), true);
|
||||
|
||||
// Find the start of the group.
|
||||
ColumnInfoRecord columnInfo = getColInfo( 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 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ColumnInfoRecord createColInfo()
|
||||
{
|
||||
ColumnInfoRecord retval = new ColumnInfoRecord();
|
||||
|
||||
retval.setColumnWidth(( short ) 2275);
|
||||
// was: retval.setOptions(( short ) 6);
|
||||
retval.setOptions(( short ) 2);
|
||||
retval.setXFIndex(( short ) 0x0f);
|
||||
return retval;
|
||||
}
|
||||
// Write collapse field
|
||||
setColumn(lastColIx + 1, null, null, null, null, Boolean.TRUE);
|
||||
}
|
||||
/**
|
||||
* Sets all adjacent columns of the same outline level to the specified hidden status.
|
||||
* @param pIdx the col info index of the start of the outline group
|
||||
* @return the column index of the last column in the outline group
|
||||
*/
|
||||
private int setGroupHidden(int pIdx, int level, boolean hidden) {
|
||||
int idx = pIdx;
|
||||
ColumnInfoRecord columnInfo = getColInfo(idx);
|
||||
while (idx < records.size()) {
|
||||
columnInfo.setHidden(hidden);
|
||||
if (idx + 1 < records.size()) {
|
||||
ColumnInfoRecord nextColumnInfo = getColInfo(idx + 1);
|
||||
if (!columnInfo.isAdjacentBefore(nextColumnInfo)) {
|
||||
break;
|
||||
}
|
||||
if (nextColumnInfo.getOutlineLevel() < level) {
|
||||
break;
|
||||
}
|
||||
columnInfo = nextColumnInfo;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return columnInfo.getLastColumn();
|
||||
}
|
||||
|
||||
|
||||
public void setColumn(short column, Short xfIndex, Short width, Integer level, Boolean hidden, Boolean collapsed)
|
||||
{
|
||||
ColumnInfoRecord ci = null;
|
||||
int k = 0;
|
||||
public void expandColumn(int columnIndex) {
|
||||
int idx = findColInfoIdx(columnIndex, 0);
|
||||
if (idx == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (k = 0; k < records.size(); k++)
|
||||
{
|
||||
ci = ( ColumnInfoRecord ) records.get(k);
|
||||
if ((ci.getFirstColumn() <= column)
|
||||
&& (column <= ci.getLastColumn()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
ci = null;
|
||||
}
|
||||
// If it is already expanded do nothing.
|
||||
if (!isColumnGroupCollapsed(idx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
// Find the start/end of the group.
|
||||
int startIdx = findStartOfColumnOutlineGroup(idx);
|
||||
int endIdx = findEndOfColumnOutlineGroup(idx);
|
||||
|
||||
nci.setFirstColumn(column);
|
||||
nci.setLastColumn(column);
|
||||
nci.setOptions(ci.getOptions());
|
||||
nci.setXFIndex(ci.getXFIndex());
|
||||
setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed );
|
||||
// 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
|
||||
ColumnInfoRecord columnInfo = getColInfo(endIdx);
|
||||
if (!isColumnGroupHiddenByParent(idx)) {
|
||||
int outlineLevel = columnInfo.getOutlineLevel();
|
||||
for (int i = startIdx; i <= endIdx; i++) {
|
||||
ColumnInfoRecord ci = getColInfo(i);
|
||||
if (outlineLevel == ci.getOutlineLevel())
|
||||
ci.setHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
insertColumn(k, nci);
|
||||
}
|
||||
else
|
||||
{
|
||||
//split to 3 records
|
||||
short lastcolumn = ci.getLastColumn();
|
||||
ci.setLastColumn(( short ) (column - 1));
|
||||
// Write collapse flag (stored in a single col info record after this outline group)
|
||||
setColumn(columnInfo.getLastColumn() + 1, null, null, null, null, Boolean.FALSE);
|
||||
}
|
||||
|
||||
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);
|
||||
private static ColumnInfoRecord copyColInfo(ColumnInfoRecord ci) {
|
||||
return (ColumnInfoRecord) ci.clone();
|
||||
}
|
||||
|
||||
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();
|
||||
public void setColumn(int targetColumnIx, Short xfIndex, Short width,
|
||||
Integer level, Boolean hidden, Boolean collapsed) {
|
||||
ColumnInfoRecord ci = null;
|
||||
int k = 0;
|
||||
|
||||
nci.setFirstColumn(column);
|
||||
nci.setLastColumn(column);
|
||||
setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed );
|
||||
insertColumn(k, nci);
|
||||
}
|
||||
}
|
||||
for (k = 0; k < records.size(); k++) {
|
||||
ColumnInfoRecord tci = (ColumnInfoRecord) records.get(k);
|
||||
if (tci.containsColumn(targetColumnIx)) {
|
||||
ci = tci;
|
||||
break;
|
||||
}
|
||||
if (tci.getFirstColumn() > targetColumnIx) {
|
||||
// call column infos after k are for later columns
|
||||
break; // exit now so k will be the correct insert pos
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() );
|
||||
}
|
||||
if (ci == null) {
|
||||
// okay so there ISN'T a column info record that covers this column so lets create one!
|
||||
ColumnInfoRecord nci = new ColumnInfoRecord();
|
||||
|
||||
private 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 );
|
||||
nci.setFirstColumn(targetColumnIx);
|
||||
nci.setLastColumn(targetColumnIx);
|
||||
setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed );
|
||||
insertColumn(k, nci);
|
||||
attemptMergeColInfoRecords(k);
|
||||
return;
|
||||
}
|
||||
|
||||
ColumnInfoRecord ci;
|
||||
for (int k = fromIdx; k < records.size(); k++)
|
||||
{
|
||||
ci = getColInfo(k);
|
||||
if ((ci.getFirstColumn() <= column)
|
||||
&& (column <= ci.getLastColumn()))
|
||||
{
|
||||
return k;
|
||||
}
|
||||
ci = null;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
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();
|
||||
|
||||
public void collapseColInfoRecords( int columnIdx )
|
||||
{
|
||||
if (columnIdx == 0)
|
||||
return;
|
||||
ColumnInfoRecord previousCol = getColInfo( columnIdx - 1);
|
||||
ColumnInfoRecord currentCol = getColInfo( columnIdx );
|
||||
boolean adjacentColumns = previousCol.getLastColumn() == currentCol.getFirstColumn() - 1;
|
||||
if (!adjacentColumns)
|
||||
return;
|
||||
boolean columnChanged = styleChanged || widthChanged || levelChanged || hiddenChanged || collapsedChanged;
|
||||
if (!columnChanged) {
|
||||
// do nothing...nothing changed.
|
||||
return;
|
||||
}
|
||||
|
||||
boolean columnsMatch =
|
||||
previousCol.getXFIndex() == currentCol.getXFIndex() &&
|
||||
previousCol.getOptions() == currentCol.getOptions() &&
|
||||
previousCol.getColumnWidth() == currentCol.getColumnWidth();
|
||||
if (ci.getFirstColumn() == targetColumnIx && ci.getLastColumn() == targetColumnIx) {
|
||||
// ColumnInfo ci for a single column, the target column
|
||||
setColumnInfoFields(ci, xfIndex, width, level, hidden, collapsed);
|
||||
attemptMergeColInfoRecords(k);
|
||||
return;
|
||||
}
|
||||
|
||||
if (columnsMatch)
|
||||
{
|
||||
previousCol.setLastColumn( currentCol.getLastColumn() );
|
||||
records.remove( columnIdx );
|
||||
}
|
||||
}
|
||||
if (ci.getFirstColumn() == targetColumnIx || ci.getLastColumn() == targetColumnIx) {
|
||||
// The target column is at either end of the multi-column ColumnInfo ci
|
||||
// we'll just divide the info and create a new one
|
||||
if (ci.getFirstColumn() == targetColumnIx) {
|
||||
ci.setFirstColumn(targetColumnIx + 1);
|
||||
} else {
|
||||
ci.setLastColumn(targetColumnIx - 1);
|
||||
k++; // adjust insert pos to insert after
|
||||
}
|
||||
ColumnInfoRecord nci = copyColInfo(ci);
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
nci.setFirstColumn(targetColumnIx);
|
||||
nci.setLastColumn(targetColumnIx);
|
||||
setColumnInfoFields( nci, xfIndex, width, level, hidden, collapsed );
|
||||
|
||||
// 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 = getColInfo(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, null, new Integer(level), null, null);
|
||||
columnIdx = findColumnIdx( i, Math.max(0, fromIdx ) );
|
||||
collapseColInfoRecords( columnIdx );
|
||||
}
|
||||
insertColumn(k, nci);
|
||||
attemptMergeColInfoRecords(k);
|
||||
} else {
|
||||
//split to 3 records
|
||||
ColumnInfoRecord ciStart = ci;
|
||||
ColumnInfoRecord ciMid = copyColInfo(ci);
|
||||
ColumnInfoRecord ciEnd = copyColInfo(ci);
|
||||
int lastcolumn = ci.getLastColumn();
|
||||
|
||||
ciStart.setLastColumn(targetColumnIx - 1);
|
||||
|
||||
}
|
||||
/**
|
||||
* Finds the <tt>ColumnInfoRecord</tt> which contains the specified columnIndex
|
||||
* @param columnIndex index of the column (not the index of the ColumnInfoRecord)
|
||||
* @return <code>null</code> if no column info found for the specified column
|
||||
*/
|
||||
ciMid.setFirstColumn(targetColumnIx);
|
||||
ciMid.setLastColumn(targetColumnIx);
|
||||
setColumnInfoFields(ciMid, xfIndex, width, level, hidden, collapsed);
|
||||
insertColumn(++k, ciMid);
|
||||
|
||||
ciEnd.setFirstColumn(targetColumnIx+1);
|
||||
ciEnd.setLastColumn(lastcolumn);
|
||||
insertColumn(++k, ciEnd);
|
||||
// no need to attemptMergeColInfoRecords because we
|
||||
// know both on each side are different
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all non null fields into the <code>ci</code> parameter.
|
||||
*/
|
||||
private static 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() );
|
||||
}
|
||||
}
|
||||
|
||||
private int findColInfoIdx(int columnIx, int fromColInfoIdx) {
|
||||
if (columnIx < 0) {
|
||||
throw new IllegalArgumentException( "column parameter out of range: " + columnIx );
|
||||
}
|
||||
if (fromColInfoIdx < 0) {
|
||||
throw new IllegalArgumentException( "fromIdx parameter out of range: " + fromColInfoIdx );
|
||||
}
|
||||
|
||||
for (int k = fromColInfoIdx; k < records.size(); k++) {
|
||||
ColumnInfoRecord ci = getColInfo(k);
|
||||
if (ci.containsColumn(columnIx)) {
|
||||
return k;
|
||||
}
|
||||
if (ci.getFirstColumn() > columnIx) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to merge the col info record at the specified index
|
||||
* with either or both of its neighbours
|
||||
*/
|
||||
private void attemptMergeColInfoRecords(int colInfoIx) {
|
||||
int nRecords = records.size();
|
||||
if (colInfoIx < 0 || colInfoIx >= nRecords) {
|
||||
throw new IllegalArgumentException("colInfoIx " + colInfoIx
|
||||
+ " is out of range (0.." + (nRecords-1) + ")");
|
||||
}
|
||||
ColumnInfoRecord currentCol = getColInfo(colInfoIx);
|
||||
int nextIx = colInfoIx+1;
|
||||
if (nextIx < nRecords) {
|
||||
if (mergeColInfoRecords(currentCol, getColInfo(nextIx))) {
|
||||
records.remove(nextIx);
|
||||
}
|
||||
}
|
||||
if (colInfoIx > 0) {
|
||||
if (mergeColInfoRecords(getColInfo(colInfoIx - 1), currentCol)) {
|
||||
records.remove(colInfoIx);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* merges two column info records (if they are adjacent and have the same formatting, etc)
|
||||
* @return <code>false</code> if the two column records could not be merged
|
||||
*/
|
||||
private static boolean mergeColInfoRecords(ColumnInfoRecord ciA, ColumnInfoRecord ciB) {
|
||||
if (ciA.isAdjacentBefore(ciB) && ciA.formatMatches(ciB)) {
|
||||
ciA.setLastColumn(ciB.getLastColumn());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Creates an outline group for the specified columns, by setting the level
|
||||
* field for each col info record in the range. {@link ColumnInfoRecord}s
|
||||
* may be created, split or merged as a result of this operation.
|
||||
*
|
||||
* @param fromColumnIx
|
||||
* group from this column (inclusive)
|
||||
* @param toColumnIx
|
||||
* group to this column (inclusive)
|
||||
* @param indent
|
||||
* if <code>true</code> the group will be indented by one
|
||||
* level, if <code>false</code> indenting will be decreased by
|
||||
* one level.
|
||||
*/
|
||||
public void groupColumnRange(int fromColumnIx, int toColumnIx, boolean indent) {
|
||||
|
||||
int colInfoSearchStartIdx = 0; // optimization to speed up the search for col infos
|
||||
for (int i = fromColumnIx; i <= toColumnIx; i++) {
|
||||
int level = 1;
|
||||
int colInfoIdx = findColInfoIdx(i, colInfoSearchStartIdx);
|
||||
if (colInfoIdx != -1) {
|
||||
level = getColInfo(colInfoIdx).getOutlineLevel();
|
||||
if (indent) {
|
||||
level++;
|
||||
} else {
|
||||
level--;
|
||||
}
|
||||
level = Math.max(0, level);
|
||||
level = Math.min(7, level);
|
||||
colInfoSearchStartIdx = Math.max(0, colInfoIdx - 1); // -1 just in case this column is collapsed later.
|
||||
}
|
||||
setColumn(i, null, null, new Integer(level), null, null);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Finds the <tt>ColumnInfoRecord</tt> which contains the specified columnIndex
|
||||
* @param columnIndex index of the column (not the index of the ColumnInfoRecord)
|
||||
* @return <code>null</code> if no column info found for the specified column
|
||||
*/
|
||||
public ColumnInfoRecord findColumnInfo(int columnIndex) {
|
||||
int nInfos = records.size();
|
||||
for(int i=0; i< nInfos; i++) {
|
||||
ColumnInfoRecord ci = getColInfo(i);
|
||||
if (ci.getFirstColumn() <= columnIndex && columnIndex <= ci.getLastColumn()) {
|
||||
if (ci.containsColumn(columnIndex)) {
|
||||
return ci;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public int getMaxOutlineLevel() {
|
||||
int result = 0;
|
||||
int count=records.size();
|
||||
for (int i=0; i<count; i++) {
|
||||
ColumnInfoRecord columnInfoRecord = getColInfo(i);
|
||||
result = Math.max(columnInfoRecord.getOutlineLevel(), result);
|
||||
}
|
||||
return result;
|
||||
int result = 0;
|
||||
int count=records.size();
|
||||
for (int i=0; i<count; i++) {
|
||||
ColumnInfoRecord columnInfoRecord = getColInfo(i);
|
||||
result = Math.max(columnInfoRecord.getOutlineLevel(), result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -384,7 +384,7 @@ public final class HSSFSheet {
|
||||
|
||||
for(int index=0; index<records.size(); index++) {
|
||||
if(records.get(index) instanceof DVRecord) {
|
||||
dvRecords.add(records.get(index));
|
||||
dvRecords.add(records.get(index));
|
||||
}
|
||||
}
|
||||
return dvRecords;
|
||||
@ -1595,15 +1595,33 @@ public final class HSSFSheet {
|
||||
return patriarch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated (Sep 2008) use {@link #setColumnGroupCollapsed(int, boolean)}
|
||||
*/
|
||||
public void setColumnGroupCollapsed(short columnNumber, boolean collapsed) {
|
||||
setColumnGroupCollapsed(columnNumber & 0xFFFF, collapsed);
|
||||
}
|
||||
/**
|
||||
* @deprecated (Sep 2008) use {@link #groupColumn(int, int)}
|
||||
*/
|
||||
public void groupColumn(short fromColumn, short toColumn) {
|
||||
groupColumn(fromColumn & 0xFFFF, toColumn & 0xFFFF);
|
||||
}
|
||||
/**
|
||||
* @deprecated (Sep 2008) use {@link #ungroupColumn(int, int)}
|
||||
*/
|
||||
public void ungroupColumn(short fromColumn, short toColumn) {
|
||||
ungroupColumn(fromColumn & 0xFFFF, toColumn & 0xFFFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands or collapses a column group.
|
||||
*
|
||||
* @param columnNumber One of the columns in the group.
|
||||
* @param collapsed true = collapse group, false = expand group.
|
||||
*/
|
||||
public void setColumnGroupCollapsed( short columnNumber, boolean collapsed )
|
||||
{
|
||||
sheet.setColumnGroupCollapsed( columnNumber, collapsed );
|
||||
public void setColumnGroupCollapsed(int columnNumber, boolean collapsed) {
|
||||
sheet.setColumnGroupCollapsed(columnNumber, collapsed);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1612,14 +1630,12 @@ public final class HSSFSheet {
|
||||
* @param fromColumn beginning of the column range.
|
||||
* @param toColumn end of the column range.
|
||||
*/
|
||||
public void groupColumn(short fromColumn, short toColumn)
|
||||
{
|
||||
sheet.groupColumnRange( fromColumn, toColumn, true );
|
||||
public void groupColumn(int fromColumn, int toColumn) {
|
||||
sheet.groupColumnRange(fromColumn, toColumn, true);
|
||||
}
|
||||
|
||||
public void ungroupColumn( short fromColumn, short toColumn )
|
||||
{
|
||||
sheet.groupColumnRange( fromColumn, toColumn, false );
|
||||
public void ungroupColumn(int fromColumn, int toColumn) {
|
||||
sheet.groupColumnRange(fromColumn, toColumn, false);
|
||||
}
|
||||
|
||||
public void groupRow(int fromRow, int toRow)
|
||||
|
@ -357,7 +357,7 @@ public final class TestSheet extends TestCase {
|
||||
xfindex = sheet.getXFIndexForColAt((short) 1);
|
||||
assertEquals(DEFAULT_IDX, xfindex);
|
||||
|
||||
ColumnInfoRecord nci = ColumnInfoRecordsAggregate.createColInfo();
|
||||
ColumnInfoRecord nci = new ColumnInfoRecord();
|
||||
sheet._columnInfos.insertColumn(nci);
|
||||
|
||||
// single column ColumnInfoRecord
|
||||
@ -567,7 +567,7 @@ public final class TestSheet extends TestCase {
|
||||
|
||||
sheet.setMargin(HSSFSheet.LeftMargin, 0.3);
|
||||
try {
|
||||
row.createCell((short) 0);
|
||||
row.createCell(0);
|
||||
} catch (IllegalStateException e) {
|
||||
if (e.getMessage().equals("Cannot create value records before row records exist")) {
|
||||
throw new AssertionFailedError("Identified bug 45717");
|
||||
@ -576,4 +576,3 @@ public final class TestSheet extends TestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@ package org.apache.poi.hssf.model;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.record.ColumnInfoRecord;
|
||||
import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
|
||||
|
||||
/**
|
||||
* @author Tony Poppleton
|
||||
@ -29,7 +28,7 @@ public final class TestSheetAdditional extends TestCase {
|
||||
|
||||
public void testGetCellWidth() {
|
||||
Sheet sheet = Sheet.createSheet();
|
||||
ColumnInfoRecord nci = ColumnInfoRecordsAggregate.createColInfo();
|
||||
ColumnInfoRecord nci = new ColumnInfoRecord();
|
||||
|
||||
// Prepare test model
|
||||
nci.setFirstColumn((short)5);
|
||||
@ -55,5 +54,4 @@ public final class TestSheetAdditional extends TestCase {
|
||||
assertEquals((short)100,sheet.getColumnWidth((short)9));
|
||||
assertEquals((short)100,sheet.getColumnWidth((short)10));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,9 +17,16 @@
|
||||
|
||||
package org.apache.poi.hssf.record.aggregates;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.record.ColumnInfoRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.RecordBase;
|
||||
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
|
||||
|
||||
/**
|
||||
* @author Glen Stampoultzis
|
||||
@ -28,11 +35,11 @@ public final class TestColumnInfoRecordsAggregate extends TestCase {
|
||||
|
||||
public void testGetRecordSize() {
|
||||
ColumnInfoRecordsAggregate agg = new ColumnInfoRecordsAggregate();
|
||||
agg.insertColumn(createColumn(1, 3));
|
||||
agg.insertColumn(createColumn(4, 7));
|
||||
agg.insertColumn(createColumn(8, 8));
|
||||
agg.insertColumn(createColInfo(1, 3));
|
||||
agg.insertColumn(createColInfo(4, 7));
|
||||
agg.insertColumn(createColInfo(8, 8));
|
||||
agg.groupColumnRange((short) 2, (short) 5, true);
|
||||
assertEquals(6, agg.getNumColumns());
|
||||
assertEquals(4, agg.getNumColumns());
|
||||
|
||||
confirmSerializedSize(agg);
|
||||
|
||||
@ -48,10 +55,91 @@ public final class TestColumnInfoRecordsAggregate extends TestCase {
|
||||
assertEquals(estimatedSize, serializedSize);
|
||||
}
|
||||
|
||||
private static ColumnInfoRecord createColumn(int firstCol, int lastCol) {
|
||||
private static ColumnInfoRecord createColInfo(int firstCol, int lastCol) {
|
||||
ColumnInfoRecord columnInfoRecord = new ColumnInfoRecord();
|
||||
columnInfoRecord.setFirstColumn((short) firstCol);
|
||||
columnInfoRecord.setLastColumn((short) lastCol);
|
||||
return columnInfoRecord;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CIRCollector implements RecordVisitor {
|
||||
|
||||
private List _list;
|
||||
public CIRCollector() {
|
||||
_list = new ArrayList();
|
||||
}
|
||||
public void visitRecord(Record r) {
|
||||
_list.add(r);
|
||||
}
|
||||
public static ColumnInfoRecord[] getRecords(ColumnInfoRecordsAggregate agg) {
|
||||
CIRCollector circ = new CIRCollector();
|
||||
agg.visitContainedRecords(circ);
|
||||
List list = circ._list;
|
||||
ColumnInfoRecord[] result = new ColumnInfoRecord[list.size()];
|
||||
list.toArray(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void testGroupColumns_bug45639() {
|
||||
ColumnInfoRecordsAggregate agg = new ColumnInfoRecordsAggregate();
|
||||
agg.groupColumnRange( 7, 9, true);
|
||||
agg.groupColumnRange( 4, 12, true);
|
||||
try {
|
||||
agg.groupColumnRange( 1, 15, true);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new AssertionFailedError("Identified bug 45639");
|
||||
}
|
||||
ColumnInfoRecord[] cirs = CIRCollector.getRecords(agg);
|
||||
assertEquals(5, cirs.length);
|
||||
confirmCIR(cirs, 0, 1, 3, 1, false, false);
|
||||
confirmCIR(cirs, 1, 4, 6, 2, false, false);
|
||||
confirmCIR(cirs, 2, 7, 9, 3, false, false);
|
||||
confirmCIR(cirs, 3, 10, 12, 2, false, false);
|
||||
confirmCIR(cirs, 4, 13, 15, 1, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that an inner group remains hidden
|
||||
*/
|
||||
public void testHiddenAfterExpanding() {
|
||||
ColumnInfoRecordsAggregate agg = new ColumnInfoRecordsAggregate();
|
||||
agg.groupColumnRange(1, 15, true);
|
||||
agg.groupColumnRange(4, 12, true);
|
||||
|
||||
ColumnInfoRecord[] cirs;
|
||||
|
||||
// collapse both inner and outer groups
|
||||
agg.collapseColumn(6);
|
||||
agg.collapseColumn(3);
|
||||
|
||||
cirs = CIRCollector.getRecords(agg);
|
||||
assertEquals(5, cirs.length);
|
||||
confirmCIR(cirs, 0, 1, 3, 1, true, false);
|
||||
confirmCIR(cirs, 1, 4, 12, 2, true, false);
|
||||
confirmCIR(cirs, 2, 13, 13, 1, true, true);
|
||||
confirmCIR(cirs, 3, 14, 15, 1, true, false);
|
||||
confirmCIR(cirs, 4, 16, 16, 0, false, true);
|
||||
|
||||
// just expand the inner group
|
||||
agg.expandColumn(6);
|
||||
|
||||
cirs = CIRCollector.getRecords(agg);
|
||||
assertEquals(4, cirs.length);
|
||||
if (!cirs[1].getHidden()) {
|
||||
throw new AssertionFailedError("Inner group should still be hidden");
|
||||
}
|
||||
confirmCIR(cirs, 0, 1, 3, 1, true, false);
|
||||
confirmCIR(cirs, 1, 4, 12, 2, true, false);
|
||||
confirmCIR(cirs, 2, 13, 15, 1, true, false);
|
||||
confirmCIR(cirs, 3, 16, 16, 0, false, true);
|
||||
}
|
||||
private static void confirmCIR(ColumnInfoRecord[] cirs, int ix, int startColIx, int endColIx, int level, boolean isHidden, boolean isCollapsed) {
|
||||
ColumnInfoRecord cir = cirs[ix];
|
||||
assertEquals("startColIx", startColIx, cir.getFirstColumn());
|
||||
assertEquals("endColIx", endColIx, cir.getLastColumn());
|
||||
assertEquals("level", level, cir.getOutlineLevel());
|
||||
assertEquals("hidden", isHidden, cir.getHidden());
|
||||
assertEquals("collapsed", isCollapsed, cir.getCollapsed());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user