Inspired by bug #44958 - Record level support for Data Tables. (No formula parser support though)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@676310 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2008-07-13 12:37:29 +00:00
parent bae464ec10
commit 9c3f4b14c6
13 changed files with 487 additions and 1 deletions

View File

@ -37,6 +37,7 @@
<!-- Don't forget to update status.xml too! --> <!-- Don't forget to update status.xml too! -->
<release version="3.1.1-alpha1" date="2008-??-??"> <release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="add">44958 - Record level support for Data Tables. (No formula parser support though)</action>
<action dev="POI-DEVELOPERS" type="add">35583 - Include a version class, org.apache.poi.Version, to allow easy introspection of the POI version</action> <action dev="POI-DEVELOPERS" type="add">35583 - Include a version class, org.apache.poi.Version, to allow easy introspection of the POI version</action>
<action dev="POI-DEVELOPERS" type="add">Allow the cloning of one HSSFCellStyle onto another, including cloning styles from one HSSFWorkbook onto another</action> <action dev="POI-DEVELOPERS" type="add">Allow the cloning of one HSSFCellStyle onto another, including cloning styles from one HSSFWorkbook onto another</action>
<action dev="POI-DEVELOPERS" type="fix">45289 - finished support for special comparison operators in COUNTIF</action> <action dev="POI-DEVELOPERS" type="fix">45289 - finished support for special comparison operators in COUNTIF</action>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! --> <!-- Don't forget to update changes.xml too! -->
<changes> <changes>
<release version="3.1.1-alpha1" date="2008-??-??"> <release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="add">44958 - Record level support for Data Tables. (No formula parser support though)</action>
<action dev="POI-DEVELOPERS" type="add">35583 - Include a version class, org.apache.poi.Version, to allow easy introspection of the POI version</action> <action dev="POI-DEVELOPERS" type="add">35583 - Include a version class, org.apache.poi.Version, to allow easy introspection of the POI version</action>
<action dev="POI-DEVELOPERS" type="add">Allow the cloning of one HSSFCellStyle onto another, including cloning styles from one HSSFWorkbook onto another</action> <action dev="POI-DEVELOPERS" type="add">Allow the cloning of one HSSFCellStyle onto another, including cloning styles from one HSSFWorkbook onto another</action>
<action dev="POI-DEVELOPERS" type="fix">45289 - finished support for special comparison operators in COUNTIF</action> <action dev="POI-DEVELOPERS" type="fix">45289 - finished support for special comparison operators in COUNTIF</action>

View File

@ -365,6 +365,8 @@ public final class BiffViewer {
return new FileSharingRecord( in ); return new FileSharingRecord( in );
case HyperlinkRecord.sid: case HyperlinkRecord.sid:
return new HyperlinkRecord( in ); return new HyperlinkRecord( in );
case TableRecord.sid:
return new TableRecord( in );
} }
return new UnknownRecord( in ); return new UnknownRecord( in );
} }

View File

@ -96,6 +96,7 @@ import org.apache.poi.hssf.record.SharedFormulaRecord;
import org.apache.poi.hssf.record.StringRecord; import org.apache.poi.hssf.record.StringRecord;
import org.apache.poi.hssf.record.StyleRecord; import org.apache.poi.hssf.record.StyleRecord;
import org.apache.poi.hssf.record.TabIdRecord; import org.apache.poi.hssf.record.TabIdRecord;
import org.apache.poi.hssf.record.TableRecord;
import org.apache.poi.hssf.record.TopMarginRecord; import org.apache.poi.hssf.record.TopMarginRecord;
import org.apache.poi.hssf.record.UnknownRecord; import org.apache.poi.hssf.record.UnknownRecord;
import org.apache.poi.hssf.record.UseSelFSRecord; import org.apache.poi.hssf.record.UseSelFSRecord;
@ -160,7 +161,7 @@ public class EventRecordFactory
TopMarginRecord.class, BottomMarginRecord.class, TopMarginRecord.class, BottomMarginRecord.class,
PaletteRecord.class, StringRecord.class, SharedFormulaRecord.class, PaletteRecord.class, StringRecord.class, SharedFormulaRecord.class,
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class, WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
NoteRecord.class NoteRecord.class, TableRecord.class
}; };
} }

View File

@ -248,6 +248,8 @@ public class HyperlinkRecord extends Record {
*/ */
public String getLabel() public String getLabel()
{ {
if(label == null) return null;
int idx = label.indexOf('\u0000'); int idx = label.indexOf('\u0000');
return idx == -1 ? label : label.substring(0, idx); return idx == -1 ? label : label.substring(0, idx);
} }
@ -269,6 +271,8 @@ public class HyperlinkRecord extends Record {
*/ */
public String getAddress() public String getAddress()
{ {
if(address == null) return null;
int idx = address.indexOf('\u0000'); int idx = address.indexOf('\u0000');
return idx == -1 ? address : address.substring(0, idx); return idx == -1 ? address : address.substring(0, idx);
} }

View File

@ -86,6 +86,7 @@ public class RecordFactory
CRNRecord.class, CRNRecord.class,
CFHeaderRecord.class, CFHeaderRecord.class,
CFRuleRecord.class, CFRuleRecord.class,
TableRecord.class
}; };
} }
private static Map recordsMap = recordsToMap(records); private static Map recordsMap = recordsToMap(records);

View File

@ -0,0 +1,248 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/**
* TableRecord - The record specifies a data table.
* This record is preceded by a single Formula record that
* defines the first cell in the data table, which should
* only contain a single Ptg, {@link TblPtg}.
*
* See p536 of the June 08 binary docs
*/
package org.apache.poi.hssf.record;
import org.apache.poi.hssf.record.formula.TblPtg;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.LittleEndian;
public class TableRecord extends Record {
public static final short sid = 566;
private short field_1_ref_rowFirst;
private short field_2_ref_rowLast;
private short field_3_ref_colFirst;
private short field_4_ref_colLast;
private byte field_5_flags;
private byte field_6_res;
private short field_7_rowInputRow;
private short field_8_colInputRow;
private short field_9_rowInputCol;
private short field_10_colInputCol;
private BitField alwaysCalc = BitFieldFactory.getInstance(0x0001);
private BitField reserved1 = BitFieldFactory.getInstance(0x0002);
private BitField rowOrColInpCell = BitFieldFactory.getInstance(0x0004);
private BitField oneOrTwoVar = BitFieldFactory.getInstance(0x0008);
private BitField rowDeleted = BitFieldFactory.getInstance(0x0010);
private BitField colDeleted = BitFieldFactory.getInstance(0x0020);
private BitField reserved2 = BitFieldFactory.getInstance(0x0040);
private BitField reserved3 = BitFieldFactory.getInstance(0x0080);
protected void fillFields(RecordInputStream in) {
field_1_ref_rowFirst = in.readShort();
field_2_ref_rowLast = in.readShort();
field_3_ref_colFirst = in.readUByte();
field_4_ref_colLast = in.readUByte();
field_5_flags = in.readByte();
field_6_res = in.readByte();
field_7_rowInputRow = in.readShort();
field_8_colInputRow = in.readShort();
field_9_rowInputCol = in.readShort();
field_10_colInputCol = in.readShort();
}
public TableRecord(RecordInputStream in) {
super(in);
}
public TableRecord() {
super();
}
public short getRowFirst() {
return field_1_ref_rowFirst;
}
public void setRowFirst(short field_1_ref_rowFirst) {
this.field_1_ref_rowFirst = field_1_ref_rowFirst;
}
public short getRowLast() {
return field_2_ref_rowLast;
}
public void setRowLast(short field_2_ref_rowLast) {
this.field_2_ref_rowLast = field_2_ref_rowLast;
}
public short getColFirst() {
return field_3_ref_colFirst;
}
public void setColFirst(short field_3_ref_colFirst) {
this.field_3_ref_colFirst = field_3_ref_colFirst;
}
public short getColLast() {
return field_4_ref_colLast;
}
public void setColLast(short field_4_ref_colLast) {
this.field_4_ref_colLast = field_4_ref_colLast;
}
public byte getFlags() {
return field_5_flags;
}
public void setFlags(byte field_5_flags) {
this.field_5_flags = field_5_flags;
}
public byte getReserved() {
return field_6_res;
}
public void setReserved(byte field_6_res) {
this.field_6_res = field_6_res;
}
public short getRowInputRow() {
return field_7_rowInputRow;
}
public void setRowInputRow(short field_7_rowInputRow) {
this.field_7_rowInputRow = field_7_rowInputRow;
}
public short getColInputRow() {
return field_8_colInputRow;
}
public void setColInputRow(short field_8_colInputRow) {
this.field_8_colInputRow = field_8_colInputRow;
}
public short getRowInputCol() {
return field_9_rowInputCol;
}
public void setRowInputCol(short field_9_rowInputCol) {
this.field_9_rowInputCol = field_9_rowInputCol;
}
public short getColInputCol() {
return field_10_colInputCol;
}
public void setColInputCol(short field_10_colInputCol) {
this.field_10_colInputCol = field_10_colInputCol;
}
public boolean isAlwaysCalc() {
return alwaysCalc.isSet(field_5_flags);
}
public void setAlwaysCalc(boolean flag) {
field_5_flags = alwaysCalc.setByteBoolean(field_5_flags, flag);
}
public boolean isRowOrColInpCell() {
return rowOrColInpCell.isSet(field_5_flags);
}
public void setRowOrColInpCell(boolean flag) {
field_5_flags = rowOrColInpCell.setByteBoolean(field_5_flags, flag);
}
public boolean isOneNotTwoVar() {
return oneOrTwoVar.isSet(field_5_flags);
}
public void setOneNotTwoVar(boolean flag) {
field_5_flags = oneOrTwoVar.setByteBoolean(field_5_flags, flag);
}
public boolean isColDeleted() {
return colDeleted.isSet(field_5_flags);
}
public void setColDeleted(boolean flag) {
field_5_flags = colDeleted.setByteBoolean(field_5_flags, flag);
}
public boolean isRowDeleted() {
return rowDeleted.isSet(field_5_flags);
}
public void setRowDeleted(boolean flag) {
field_5_flags = rowDeleted.setByteBoolean(field_5_flags, flag);
}
public short getSid() {
return sid;
}
public int serialize(int offset, byte[] data) {
LittleEndian.putShort(data, 0 + offset, sid);
LittleEndian.putShort(data, 2 + offset, ( short ) (16));
LittleEndian.putShort(data, 4 + offset, field_1_ref_rowFirst);
LittleEndian.putShort(data, 6 + offset, field_2_ref_rowLast);
LittleEndian.putByte(data, 8 + offset, field_3_ref_colFirst);
LittleEndian.putByte(data, 9 + offset, field_4_ref_colLast);
LittleEndian.putByte(data, 10 + offset, field_5_flags);
LittleEndian.putByte(data, 11 + offset, field_6_res);
LittleEndian.putShort(data, 12 + offset, field_7_rowInputRow);
LittleEndian.putShort(data, 14 + offset, field_8_colInputRow);
LittleEndian.putShort(data, 16 + offset, field_9_rowInputCol);
LittleEndian.putShort(data, 18 + offset, field_10_colInputCol);
return getRecordSize();
}
public int getRecordSize() {
return 4+16;
}
protected void validateSid(short id) {
if (id != sid)
{
throw new RecordFormatException("NOT A TABLE RECORD");
}
}
public String toString()
{
StringBuffer buffer = new StringBuffer();
buffer.append("[TABLE]\n");
buffer.append(" .row from = ")
.append(Integer.toHexString(field_1_ref_rowFirst)).append("\n");
buffer.append(" .row to = ")
.append(Integer.toHexString(field_2_ref_rowLast)).append("\n");
buffer.append(" .column from = ")
.append(Integer.toHexString(field_3_ref_colFirst)).append("\n");
buffer.append(" .column to = ")
.append(Integer.toHexString(field_4_ref_colLast)).append("\n");
buffer.append(" .flags = ")
.append(Integer.toHexString(field_5_flags)).append("\n");
buffer.append(" .always calc =")
.append(isAlwaysCalc()).append("\n");
buffer.append(" .reserved = ")
.append(Integer.toHexString(field_6_res)).append("\n");
buffer.append(" .row input row = ")
.append(Integer.toHexString(field_7_rowInputRow)).append("\n");
buffer.append(" .col input row = ")
.append(Integer.toHexString(field_8_colInputRow)).append("\n");
buffer.append(" .row input col = ")
.append(Integer.toHexString(field_9_rowInputCol)).append("\n");
buffer.append(" .col input col = ")
.append(Integer.toHexString(field_10_colInputCol)).append("\n");
buffer.append("[/TABLE]\n");
return buffer.toString();
}
}

View File

@ -188,6 +188,7 @@ public abstract class Ptg implements Cloneable {
switch(id) { switch(id) {
case 0x00: return new UnknownPtg(); // TODO - not a real Ptg case 0x00: return new UnknownPtg(); // TODO - not a real Ptg
case ExpPtg.sid: return new ExpPtg(in); // 0x01 case ExpPtg.sid: return new ExpPtg(in); // 0x01
case TblPtg.sid: return new TblPtg(in); // 0x02
case AddPtg.sid: return AddPtg.instance; // 0x03 case AddPtg.sid: return AddPtg.instance; // 0x03
case SubtractPtg.sid: return SubtractPtg.instance; // 0x04 case SubtractPtg.sid: return SubtractPtg.instance; // 0x04
case MultiplyPtg.sid: return MultiplyPtg.instance; // 0x05 case MultiplyPtg.sid: return MultiplyPtg.instance; // 0x05

View File

@ -0,0 +1,87 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndian;
/**
* This ptg indicates a data table.
* It only occurs in a FORMULA record, never in an
* ARRAY or NAME record. When ptgTbl occurs in a
* formula, it is the only token in the formula.
* (TODO - check this when processing)
* This indicates that the cell containing the
* formula is an interior cell in a data table;
* the table description is found in a TABLE
* record. Rows and columns which contain input
* values to be substituted in the table do
* not contain ptgTbl.
* See page 811 of the june 08 binary docs.
*/
public final class TblPtg extends ControlPtg {
private final static int SIZE = 4;
public final static short sid = 0x2;
/** The row number of the upper left corner */
private final short field_1_first_row;
/** The column number of the upper left corner */
private final short field_2_first_col;
public TblPtg(RecordInputStream in)
{
field_1_first_row = in.readShort();
field_2_first_col = in.readUByte();
}
public void writeBytes(byte [] array, int offset)
{
array[offset+0]= (byte) (sid);
LittleEndian.putShort(array,offset+1,field_1_first_row);
LittleEndian.putByte(array,offset+3,field_2_first_col);
}
public int getSize()
{
return SIZE;
}
public short getRow() {
return field_1_first_row;
}
public short getColumn() {
return field_2_first_col;
}
public String toFormulaString(HSSFWorkbook book)
{
// table(....)[][]
throw new RecordFormatException("Table and Arrays are not yet supported");
}
public String toString()
{
StringBuffer buffer = new StringBuffer("[Data Table - Parent cell is an interior cell in a data table]\n");
buffer.append("top left row = ").append(getRow()).append("\n");
buffer.append("top left col = ").append(getColumn()).append("\n");
return buffer.toString();
}
}

Binary file not shown.

View File

@ -101,6 +101,7 @@ public final class AllRecordTests {
result.addTestSuite(TestStringRecord.class); result.addTestSuite(TestStringRecord.class);
result.addTestSuite(TestSubRecord.class); result.addTestSuite(TestSubRecord.class);
result.addTestSuite(TestSupBookRecord.class); result.addTestSuite(TestSupBookRecord.class);
result.addTestSuite(TestTableRecord.class);
result.addTestSuite(TestTextObjectBaseRecord.class); result.addTestSuite(TestTextObjectBaseRecord.class);
result.addTestSuite(TestTextObjectRecord.class); result.addTestSuite(TestTextObjectRecord.class);
result.addTestSuite(TestTextRecord.class); result.addTestSuite(TestTextRecord.class);

View File

@ -0,0 +1,107 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record;
import junit.framework.TestCase;
/**
* Tests the serialization and deserialization of the TableRecord
* class works correctly. Test data taken directly from a real
* Excel file.
*/
public class TestTableRecord
extends TestCase
{
byte[] header = new byte[] {
0x36, 02, 0x10, 00, // sid=x236, 16 bytes long
};
byte[] data = new byte[] {
03, 00, // from row 3
8, 00, // to row 8
04, // from col 4
06, // to col 6
00, 00, // no flags set
04, 00, // row inp row 4
01, 00, // col inp row 1
0x76, 0x40, // row inp col 0x4076 (!)
00, 00 // col inp col 0
};
public TestTableRecord(String name)
{
super(name);
}
public void testLoad()
throws Exception
{
TableRecord record = new TableRecord(new TestcaseRecordInputStream((short)0x236, (short)data.length, data));
assertEquals(3, record.getRowFirst());
assertEquals(8, record.getRowLast());
assertEquals(4, record.getColFirst());
assertEquals(6, record.getColLast());
assertEquals(0, record.getFlags());
assertEquals(4, record.getRowInputRow());
assertEquals(1, record.getColInputRow());
assertEquals(0x4076, record.getRowInputCol());
assertEquals(0, record.getColInputCol());
assertEquals( 16 + 4, record.getRecordSize() );
record.validateSid((short)0x236);
}
public void testStore()
{
// Offset 0x3bd9 (15321)
// recordid = 0x236, size = 16
// [TABLE]
// .row from = 3
// .row to = 8
// .column from = 4
// .column to = 6
// .flags = 0
// .always calc =false
// .reserved = 0
// .row input row = 4
// .col input row = 1
// .row input col = 4076
// .col input col = 0
// [/TABLE]
TableRecord record = new TableRecord();
record.setRowFirst((short)3);
record.setRowLast((short)8);
record.setColFirst((short)4);
record.setColLast((short)6);
record.setFlags((byte)0);
record.setReserved((byte)0);
record.setRowInputRow((short)4);
record.setColInputRow((short)1);
record.setRowInputCol((short)0x4076);
record.setColInputCol((short)0);
byte [] recordBytes = record.serialize();
assertEquals(recordBytes.length - 4, data.length);
for (int i = 0; i < data.length; i++)
assertEquals("At offset " + i, data[i], recordBytes[i+4]);
}
}

View File

@ -31,6 +31,7 @@ import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.model.Workbook; import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.CellValueRecordInterface; import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord; import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord;
import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.NameRecord; import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.DeletedArea3DPtg; import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
@ -1321,4 +1322,35 @@ public final class TestBugs extends TestCase {
assertEquals(5, r.getLastCellNum()); // last cell # + 1 assertEquals(5, r.getLastCellNum()); // last cell # + 1
assertEquals(3, r.getPhysicalNumberOfCells()); assertEquals(3, r.getPhysicalNumberOfCells());
} }
/**
* Data Tables - ptg 0x2
*/
public void test44958() throws Exception {
HSSFWorkbook wb = openSample("44958.xls");
HSSFSheet s;
HSSFRow r;
HSSFCell c;
// Check the contents of the formulas
// E4 to G9 of sheet 4 make up the table
s = wb.getSheet("OneVariable Table Completed");
r = s.getRow(3);
c = r.getCell(4);
assertEquals(HSSFCell.CELL_TYPE_FORMULA, c.getCellType());
// TODO - check the formula once tables and
// arrays are properly supported
// E4 to H9 of sheet 5 make up the table
s = wb.getSheet("TwoVariable Table Example");
r = s.getRow(3);
c = r.getCell(4);
assertEquals(HSSFCell.CELL_TYPE_FORMULA, c.getCellType());
// TODO - check the formula once tables and
// arrays are properly supported
}
} }