diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index ab7565ea3..7e1ccbfba 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -50,6 +50,7 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + 44958 - Record level support for Data Tables. (No formula parser support though) 35583 - Include a version class, org.apache.poi.Version, to allow easy introspection of the POI version Allow the cloning of one HSSFCellStyle onto another, including cloning styles from one HSSFWorkbook onto another 45289 - finished support for special comparison operators in COUNTIF diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 0c55a1e96..febf5bd1d 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -47,6 +47,7 @@ Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx + 44958 - Record level support for Data Tables. (No formula parser support though) 35583 - Include a version class, org.apache.poi.Version, to allow easy introspection of the POI version Allow the cloning of one HSSFCellStyle onto another, including cloning styles from one HSSFWorkbook onto another 45289 - finished support for special comparison operators in COUNTIF diff --git a/src/java/org/apache/poi/hssf/dev/BiffViewer.java b/src/java/org/apache/poi/hssf/dev/BiffViewer.java index 9396e679d..85b57f089 100644 --- a/src/java/org/apache/poi/hssf/dev/BiffViewer.java +++ b/src/java/org/apache/poi/hssf/dev/BiffViewer.java @@ -365,6 +365,8 @@ public final class BiffViewer { return new FileSharingRecord( in ); case HyperlinkRecord.sid: return new HyperlinkRecord( in ); + case TableRecord.sid: + return new TableRecord( in ); } return new UnknownRecord( in ); } diff --git a/src/java/org/apache/poi/hssf/eventmodel/EventRecordFactory.java b/src/java/org/apache/poi/hssf/eventmodel/EventRecordFactory.java index 04660c568..8c4abb76a 100644 --- a/src/java/org/apache/poi/hssf/eventmodel/EventRecordFactory.java +++ b/src/java/org/apache/poi/hssf/eventmodel/EventRecordFactory.java @@ -96,6 +96,7 @@ import org.apache.poi.hssf.record.SharedFormulaRecord; 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; @@ -160,7 +161,7 @@ public class EventRecordFactory TopMarginRecord.class, BottomMarginRecord.class, PaletteRecord.class, StringRecord.class, SharedFormulaRecord.class, WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class, - NoteRecord.class + NoteRecord.class, TableRecord.class }; } diff --git a/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java b/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java index d74d54ab1..6447546ec 100644 --- a/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java +++ b/src/java/org/apache/poi/hssf/record/CFHeaderRecord.java @@ -42,7 +42,7 @@ public final class CFHeaderRecord extends Record { field_4_cell_ranges = EMPTY_CELL_RANGE_ARRAY; } - public CFHeaderRecord(Region[] regions) + public CFHeaderRecord(org.apache.poi.ss.util.Region[] regions) { CellRange[] unmergedRanges = CellRange.convertRegionsToCellRanges(regions); CellRange[] mergeCellRanges = CellRange.mergeCellRanges(unmergedRanges); diff --git a/src/java/org/apache/poi/hssf/record/HyperlinkRecord.java b/src/java/org/apache/poi/hssf/record/HyperlinkRecord.java index 798d4e1ff..e28cf5bf8 100644 --- a/src/java/org/apache/poi/hssf/record/HyperlinkRecord.java +++ b/src/java/org/apache/poi/hssf/record/HyperlinkRecord.java @@ -248,6 +248,8 @@ public class HyperlinkRecord extends Record { */ public String getLabel() { + if(label == null) return null; + int idx = label.indexOf('\u0000'); return idx == -1 ? label : label.substring(0, idx); } @@ -269,6 +271,8 @@ public class HyperlinkRecord extends Record { */ public String getAddress() { + if(address == null) return null; + int idx = address.indexOf('\u0000'); return idx == -1 ? address : address.substring(0, idx); } diff --git a/src/java/org/apache/poi/hssf/record/RecordFactory.java b/src/java/org/apache/poi/hssf/record/RecordFactory.java index 5a627988f..aaee94991 100644 --- a/src/java/org/apache/poi/hssf/record/RecordFactory.java +++ b/src/java/org/apache/poi/hssf/record/RecordFactory.java @@ -86,6 +86,7 @@ public class RecordFactory CRNRecord.class, CFHeaderRecord.class, CFRuleRecord.class, + TableRecord.class }; } private static Map recordsMap = recordsToMap(records); diff --git a/src/java/org/apache/poi/hssf/record/TableRecord.java b/src/java/org/apache/poi/hssf/record/TableRecord.java new file mode 100644 index 000000000..7a48e5e0e --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/TableRecord.java @@ -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(); + } +} diff --git a/src/java/org/apache/poi/hssf/record/cf/CellRange.java b/src/java/org/apache/poi/hssf/record/cf/CellRange.java index f45b79b00..8331fd98f 100644 --- a/src/java/org/apache/poi/hssf/record/cf/CellRange.java +++ b/src/java/org/apache/poi/hssf/record/cf/CellRange.java @@ -78,7 +78,7 @@ public final class CellRange return _firstColumn == 0 && _lastColumn == LAST_COLUMN_INDEX; } - private static CellRange createFromRegion(Region r) { + private static CellRange createFromRegion(org.apache.poi.ss.util.Region r) { return new CellRange(r.getRowFrom(), r.getRowTo(), r.getColumnFrom(), r.getColumnTo()); } @@ -353,7 +353,7 @@ public final class CellRange * @param regions * @return List of CellRange objects */ - public static CellRange[] convertRegionsToCellRanges(Region[] regions) + public static CellRange[] convertRegionsToCellRanges(org.apache.poi.ss.util.Region[] regions) { CellRange[] result = new CellRange[regions.length]; for( int i=0; i