<!-- Don't forget to update status.xml too! -->
<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">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>
<!-- Don't forget to update changes.xml too! -->
<changes>
<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">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>
return new FileSharingRecord( in );
case HyperlinkRecord.sid:
return new HyperlinkRecord( in );
+ case TableRecord.sid:
+ return new TableRecord( in );
}
return new UnknownRecord( in );
}
import org.apache.poi.hssf.record.StringRecord;
import org.apache.poi.hssf.record.StyleRecord;
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.UnknownRecord;
import org.apache.poi.hssf.record.UseSelFSRecord;
TopMarginRecord.class, BottomMarginRecord.class,
PaletteRecord.class, StringRecord.class, SharedFormulaRecord.class,
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
- NoteRecord.class
+ NoteRecord.class, TableRecord.class
};
}
*/
public String getLabel()
{
+ if(label == null) return null;
+
int idx = label.indexOf('\u0000');
return idx == -1 ? label : label.substring(0, idx);
}
*/
public String getAddress()
{
+ if(address == null) return null;
+
int idx = address.indexOf('\u0000');
return idx == -1 ? address : address.substring(0, idx);
}
CRNRecord.class,
CFHeaderRecord.class,
CFRuleRecord.class,
+ TableRecord.class
};
}
private static Map recordsMap = recordsToMap(records);
--- /dev/null
+/* ====================================================================
+ 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();
+ }
+}
switch(id) {
case 0x00: return new UnknownPtg(); // TODO - not a real Ptg
case ExpPtg.sid: return new ExpPtg(in); // 0x01
+ case TblPtg.sid: return new TblPtg(in); // 0x02
case AddPtg.sid: return AddPtg.instance; // 0x03
case SubtractPtg.sid: return SubtractPtg.instance; // 0x04
case MultiplyPtg.sid: return MultiplyPtg.instance; // 0x05
--- /dev/null
+/* ====================================================================
+ 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();
+ }
+}
result.addTestSuite(TestStringRecord.class);
result.addTestSuite(TestSubRecord.class);
result.addTestSuite(TestSupBookRecord.class);
+ result.addTestSuite(TestTableRecord.class);
result.addTestSuite(TestTextObjectBaseRecord.class);
result.addTestSuite(TestTextObjectRecord.class);
result.addTestSuite(TestTextRecord.class);
--- /dev/null
+
+/* ====================================================================
+ 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]);
+ }
+}
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.CellValueRecordInterface;
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.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
assertEquals(5, r.getLastCellNum()); // last cell # + 1
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
+ }
}