mirror of https://github.com/apache/poi.git
Merged revisions 700479,700493,700916,701302,701569,701598,701747 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk ........ r700479 | yegor | 2008-09-30 07:32:37 -0700 (Tue, 30 Sep 2008) | 1 line reverted the change made in r693085 , see bug #45859 ........ r700493 | yegor | 2008-09-30 08:11:26 -0700 (Tue, 30 Sep 2008) | 1 line initial support for creating hyperlinks in HSLF, units test are still to do ........ r700916 | josh | 2008-10-01 13:56:21 -0700 (Wed, 01 Oct 2008) | 1 line Fixed bug in CellCacheEntry (support for caching blank evaluation results) ........ r701302 | yegor | 2008-10-02 22:27:06 -0700 (Thu, 02 Oct 2008) | 1 line fixed bug #45889:rrayIndexOutOfBoundsException when constructing HSLF Table with a single row ........ r701569 | josh | 2008-10-03 16:50:22 -0700 (Fri, 03 Oct 2008) | 1 line Fix for bug 45912 - ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord ........ r701598 | josh | 2008-10-03 21:59:26 -0700 (Fri, 03 Oct 2008) | 1 line changed workbook reference to index in CellLocation ........ r701747 | josh | 2008-10-04 21:43:48 -0700 (Sat, 04 Oct 2008) | 1 line Better bounds checking in RecordInputStream. Removed rarely used methods readShortArray and putShortArray ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@703069 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
a9f18bad9d
commit
f55bd82eef
|
@ -67,6 +67,9 @@
|
|||
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
|
||||
</release>
|
||||
<release version="3.2-alpha1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Initial support for creating hyperlinks in HSLF</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45876 - fixed BoundSheetRecord to allow sheet names longer than 31 chars</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45890 - fixed HSSFSheet.shiftRows to also update conditional formats</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
|
||||
|
|
|
@ -64,6 +64,9 @@
|
|||
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
|
||||
</release>
|
||||
<release version="3.2-alpha1" date="2008-??-??">
|
||||
<action dev="POI-DEVELOPERS" type="fix">45912 - fixed ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45889 - fixed ArrayIndexOutOfBoundsException when constructing HSLF Table with a single row </action>
|
||||
<action dev="POI-DEVELOPERS" type="add">Initial support for creating hyperlinks in HSLF</action>
|
||||
<action dev="POI-DEVELOPERS" type="fix">45876 - fixed BoundSheetRecord to allow sheet names longer than 31 chars</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45890 - fixed HSSFSheet.shiftRows to also update conditional formats</action>
|
||||
<action dev="POI-DEVELOPERS" type="add">45865 modified Formula Parser/Evaluator to handle cross-worksheet formulas</action>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -16,166 +15,312 @@
|
|||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
|
||||
import org.apache.poi.util.*;
|
||||
import org.apache.poi.hssf.record.formula.Area3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.RefPtg;
|
||||
import org.apache.poi.util.HexDump;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
import org.apache.poi.util.StringUtil;
|
||||
|
||||
/**
|
||||
* ftPictFmla (0x0009)<br/>
|
||||
* A sub-record within the OBJ record which stores a reference to an object
|
||||
* stored in a separate entry within the OLE2 compound file.
|
||||
*
|
||||
* @author Daniel Noll
|
||||
*/
|
||||
public class EmbeddedObjectRefSubRecord
|
||||
extends SubRecord
|
||||
{
|
||||
public static final short sid = 0x9;
|
||||
public final class EmbeddedObjectRefSubRecord extends SubRecord {
|
||||
public static final short sid = 0x0009;
|
||||
|
||||
public short field_1_stream_id_offset; // Offset to stream ID from the point after this value.
|
||||
public short[] field_2_unknown; // Unknown stuff at the front. TODO: Confirm that it's a short[]
|
||||
// TODO: Consider making a utility class for these. I've discovered the same field ordering
|
||||
// in FormatRecord and StringRecord, it may be elsewhere too.
|
||||
public short field_3_unicode_len; // Length of Unicode string.
|
||||
public boolean field_4_unicode_flag; // Flags whether the string is Unicode.
|
||||
public String field_5_ole_classname; // Classname of the embedded OLE document (e.g. Word.Document.8)
|
||||
public int field_6_stream_id; // ID of the OLE stream containing the actual data.
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = { };
|
||||
|
||||
private int field_5_ole_classname_padding; // developer laziness...
|
||||
public byte[] remainingBytes;
|
||||
private int field_1_unknown_int;
|
||||
/** either an area or a cell ref */
|
||||
private Ptg field_2_refPtg;
|
||||
private byte[] field_2_unknownFormulaData;
|
||||
// TODO: Consider making a utility class for these. I've discovered the same field ordering
|
||||
// in FormatRecord and StringRecord, it may be elsewhere too.
|
||||
private boolean field_3_unicode_flag; // Flags whether the string is Unicode.
|
||||
private String field_4_ole_classname; // Classname of the embedded OLE document (e.g. Word.Document.8)
|
||||
/** Formulas often have a single non-zero trailing byte.
|
||||
* This is in a similar position to he pre-streamId padding
|
||||
* It is unknown if the value is important (it seems to mirror a value a few bytes earlier)
|
||||
* */
|
||||
private Byte field_4_unknownByte;
|
||||
private Integer field_5_stream_id; // ID of the OLE stream containing the actual data.
|
||||
private byte[] field_6_unknown;
|
||||
|
||||
public EmbeddedObjectRefSubRecord()
|
||||
{
|
||||
field_2_unknown = new short[0];
|
||||
remainingBytes = new byte[0];
|
||||
field_1_stream_id_offset = 6;
|
||||
field_5_ole_classname = "";
|
||||
}
|
||||
|
||||
public short getSid()
|
||||
{
|
||||
return sid;
|
||||
}
|
||||
// currently for testing only - needs review
|
||||
EmbeddedObjectRefSubRecord() {
|
||||
field_2_unknownFormulaData = new byte[] { 0x02, 0x6C, 0x6A, 0x16, 0x01, }; // just some sample data. These values vary a lot
|
||||
field_6_unknown = EMPTY_BYTE_ARRAY;
|
||||
field_4_ole_classname = null;
|
||||
}
|
||||
|
||||
public EmbeddedObjectRefSubRecord(RecordInputStream in)
|
||||
{
|
||||
field_1_stream_id_offset = in.readShort();
|
||||
field_2_unknown = in.readShortArray();
|
||||
field_3_unicode_len = in.readShort();
|
||||
field_4_unicode_flag = ( in.readByte() & 0x01 ) != 0;
|
||||
public short getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
if ( field_4_unicode_flag )
|
||||
{
|
||||
field_5_ole_classname = in.readUnicodeLEString( field_3_unicode_len );
|
||||
}
|
||||
else
|
||||
{
|
||||
field_5_ole_classname = in.readCompressedUnicode( field_3_unicode_len );
|
||||
}
|
||||
public EmbeddedObjectRefSubRecord(RecordInputStream in) {
|
||||
// Much guess-work going on here due to lack of any documentation.
|
||||
// See similar source code in OOO:
|
||||
// http://lxr.go-oo.org/source/sc/sc/source/filter/excel/xiescher.cxx
|
||||
// 1223 void XclImpOleObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nRecSize )
|
||||
|
||||
// Padded with NUL bytes. The -2 is because field_1_stream_id_offset
|
||||
// is relative to after the offset field, whereas in.getRecordOffset()
|
||||
// is relative to the start of this record (minus the header.)
|
||||
field_5_ole_classname_padding = 0;
|
||||
while (in.getRecordOffset() - 2 < field_1_stream_id_offset)
|
||||
{
|
||||
field_5_ole_classname_padding++;
|
||||
in.readByte(); // discard
|
||||
}
|
||||
int streamIdOffset = in.readShort(); // OOO calls this 'nFmlaLen'
|
||||
|
||||
// Fetch the stream ID
|
||||
field_6_stream_id = in.readInt();
|
||||
|
||||
// Store what's left
|
||||
remainingBytes = in.readRemainder();
|
||||
}
|
||||
int dataLenAfterFormula = in.remaining() - streamIdOffset;
|
||||
int formulaSize = in.readUShort();
|
||||
field_1_unknown_int = in.readInt();
|
||||
byte[] formulaRawBytes = readRawData(in, formulaSize);
|
||||
field_2_refPtg = readRefPtg(formulaRawBytes);
|
||||
if (field_2_refPtg == null) {
|
||||
// common case
|
||||
// field_2_n16 seems to be 5 here
|
||||
// The formula almost looks like tTbl but the row/column values seem like garbage.
|
||||
field_2_unknownFormulaData = formulaRawBytes;
|
||||
} else {
|
||||
field_2_unknownFormulaData = null;
|
||||
}
|
||||
|
||||
public int serialize(int offset, byte[] data)
|
||||
{
|
||||
int pos = offset;
|
||||
int stringByteCount;
|
||||
if (in.remaining() >= dataLenAfterFormula + 3) {
|
||||
int tag = in.readByte();
|
||||
if (tag != 0x03) {
|
||||
throw new RecordFormatException("Expected byte 0x03 here");
|
||||
}
|
||||
int nChars = in.readUShort();
|
||||
if (nChars > 0) {
|
||||
// OOO: the 4th way Xcl stores a unicode string: not even a Grbit byte present if length 0
|
||||
field_3_unicode_flag = ( in.readByte() & 0x01 ) != 0;
|
||||
if (field_3_unicode_flag) {
|
||||
field_4_ole_classname = in.readUnicodeLEString(nChars);
|
||||
stringByteCount = nChars * 2;
|
||||
} else {
|
||||
field_4_ole_classname = in.readCompressedUnicode(nChars);
|
||||
stringByteCount = nChars;
|
||||
}
|
||||
} else {
|
||||
field_4_ole_classname = "";
|
||||
stringByteCount = 0;
|
||||
}
|
||||
} else {
|
||||
field_4_ole_classname = null;
|
||||
stringByteCount = 0;
|
||||
}
|
||||
// Pad to next 2-byte boundary
|
||||
if (((stringByteCount + formulaSize) % 2) != 0) {
|
||||
int b = in.readByte();
|
||||
if (field_2_refPtg != null && field_4_ole_classname == null) {
|
||||
field_4_unknownByte = new Byte((byte)b);
|
||||
}
|
||||
}
|
||||
int nUnexpectedPadding = in.remaining() - dataLenAfterFormula;
|
||||
|
||||
LittleEndian.putShort(data, pos, sid); pos += 2;
|
||||
LittleEndian.putShort(data, pos, (short)(getRecordSize() - 4)); pos += 2;
|
||||
if (nUnexpectedPadding > 0) {
|
||||
System.err.println("Discarding " + nUnexpectedPadding + " unexpected padding bytes ");
|
||||
readRawData(in, nUnexpectedPadding);
|
||||
}
|
||||
|
||||
LittleEndian.putShort(data, pos, field_1_stream_id_offset); pos += 2;
|
||||
LittleEndian.putShortArray(data, pos, field_2_unknown); pos += field_2_unknown.length * 2 + 2;
|
||||
LittleEndian.putShort(data, pos, field_3_unicode_len); pos += 2;
|
||||
data[pos] = field_4_unicode_flag ? (byte) 0x01 : (byte) 0x00; pos++;
|
||||
// Fetch the stream ID
|
||||
if (dataLenAfterFormula >= 4) {
|
||||
field_5_stream_id = new Integer(in.readInt());
|
||||
} else {
|
||||
field_5_stream_id = null;
|
||||
}
|
||||
|
||||
if ( field_4_unicode_flag )
|
||||
{
|
||||
StringUtil.putUnicodeLE( field_5_ole_classname, data, pos ); pos += field_5_ole_classname.length() * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
StringUtil.putCompressedUnicode( field_5_ole_classname, data, pos ); pos += field_5_ole_classname.length();
|
||||
}
|
||||
field_6_unknown = in.readRemainder();
|
||||
}
|
||||
|
||||
// Padded with the same number of NUL bytes as were originally skipped.
|
||||
// XXX: This is only accurate until we make the classname mutable.
|
||||
pos += field_5_ole_classname_padding;
|
||||
|
||||
LittleEndian.putInt(data, pos, field_6_stream_id); pos += 4;
|
||||
private static Ptg readRefPtg(byte[] formulaRawBytes) {
|
||||
byte[] data = new byte[formulaRawBytes.length + 4];
|
||||
LittleEndian.putUShort(data, 0, -5555);
|
||||
LittleEndian.putUShort(data, 2, formulaRawBytes.length);
|
||||
System.arraycopy(formulaRawBytes, 0, data, 4, formulaRawBytes.length);
|
||||
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data));
|
||||
in.nextRecord();
|
||||
byte ptgSid = in.readByte();
|
||||
switch(ptgSid) {
|
||||
case AreaPtg.sid: return new AreaPtg(in);
|
||||
case Area3DPtg.sid: return new Area3DPtg(in);
|
||||
case RefPtg.sid: return new RefPtg(in);
|
||||
case Ref3DPtg.sid: return new Ref3DPtg(in);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
System.arraycopy(remainingBytes, 0, data, pos, remainingBytes.length);
|
||||
private static byte[] readRawData(RecordInputStream in, int size) {
|
||||
if (size < 0) {
|
||||
throw new IllegalArgumentException("Negative size (" + size + ")");
|
||||
}
|
||||
if (size == 0) {
|
||||
return EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
byte[] result = new byte[size];
|
||||
for(int i=0; i< size; i++) {
|
||||
result[i] = in.readByte();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getStreamIDOffset(int formulaSize) {
|
||||
int result = 2 + 4; // formulaSize + f2unknown_int
|
||||
result += formulaSize;
|
||||
|
||||
int stringLen;
|
||||
if (field_4_ole_classname == null) {
|
||||
// don't write 0x03, stringLen, flag, text
|
||||
stringLen = 0;
|
||||
} else {
|
||||
result += 1 + 2 + 1; // 0x03, stringLen, flag
|
||||
stringLen = field_4_ole_classname.length();
|
||||
if (field_3_unicode_flag) {
|
||||
result += stringLen * 2;
|
||||
} else {
|
||||
result += stringLen;
|
||||
}
|
||||
}
|
||||
// pad to next 2 byte boundary
|
||||
if ((result % 2) != 0) {
|
||||
result ++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getDataSize(int idOffset) {
|
||||
|
||||
return getRecordSize();
|
||||
}
|
||||
int result = 2 + idOffset; // 2 for idOffset short field itself
|
||||
if (field_5_stream_id != null) {
|
||||
result += 4;
|
||||
}
|
||||
return result + field_6_unknown.length;
|
||||
}
|
||||
private int getDataSize() {
|
||||
int formulaSize = field_2_refPtg == null ? field_2_unknownFormulaData.length : field_2_refPtg.getSize();
|
||||
int idOffset = getStreamIDOffset(formulaSize);
|
||||
return getDataSize(idOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of record (exluding 4 byte header)
|
||||
*/
|
||||
public int getRecordSize()
|
||||
{
|
||||
// The stream id offset is relative to after the stream ID.
|
||||
// Add 2 bytes for the stream id offset and 4 bytes for the stream id itself and 4 byts for the record header.
|
||||
return remainingBytes.length + field_1_stream_id_offset + 2 + 4 + 4;
|
||||
}
|
||||
public int serialize(int base, byte[] data) {
|
||||
|
||||
/**
|
||||
* Gets the stream ID containing the actual data. The data itself
|
||||
* can be found under a top-level directory entry in the OLE2 filesystem
|
||||
* under the name "MBD<var>xxxxxxxx</var>" where <var>xxxxxxxx</var> is
|
||||
* this ID converted into hex (in big endian order, funnily enough.)
|
||||
*
|
||||
* @return the data stream ID.
|
||||
*/
|
||||
public int getStreamId()
|
||||
{
|
||||
return field_6_stream_id;
|
||||
}
|
||||
int formulaSize = field_2_refPtg == null ? field_2_unknownFormulaData.length : field_2_refPtg.getSize();
|
||||
int idOffset = getStreamIDOffset(formulaSize);
|
||||
int dataSize = getDataSize(idOffset);
|
||||
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append("[ftPictFmla]\n");
|
||||
buffer.append(" .streamIdOffset = ")
|
||||
.append("0x").append(HexDump.toHex( field_1_stream_id_offset ))
|
||||
.append(" (").append( field_1_stream_id_offset ).append(" )")
|
||||
.append(System.getProperty("line.separator"));
|
||||
buffer.append(" .unknown = ")
|
||||
.append("0x").append(HexDump.toHex( field_2_unknown ))
|
||||
.append(" (").append( field_2_unknown.length ).append(" )")
|
||||
.append(System.getProperty("line.separator"));
|
||||
buffer.append(" .unicodeLen = ")
|
||||
.append("0x").append(HexDump.toHex( field_3_unicode_len ))
|
||||
.append(" (").append( field_3_unicode_len ).append(" )")
|
||||
.append(System.getProperty("line.separator"));
|
||||
buffer.append(" .unicodeFlag = ")
|
||||
.append("0x").append( field_4_unicode_flag ? 0x01 : 0x00 )
|
||||
.append(" (").append( field_4_unicode_flag ).append(" )")
|
||||
.append(System.getProperty("line.separator"));
|
||||
buffer.append(" .oleClassname = ")
|
||||
.append(field_5_ole_classname)
|
||||
.append(System.getProperty("line.separator"));
|
||||
buffer.append(" .streamId = ")
|
||||
.append("0x").append(HexDump.toHex( field_6_stream_id ))
|
||||
.append(" (").append( field_6_stream_id ).append(" )")
|
||||
.append(System.getProperty("line.separator"));
|
||||
buffer.append("[/ftPictFmla]");
|
||||
return buffer.toString();
|
||||
}
|
||||
LittleEndian.putUShort(data, base + 0, sid);
|
||||
LittleEndian.putUShort(data, base + 2, dataSize);
|
||||
|
||||
LittleEndian.putUShort(data, base + 4, idOffset);
|
||||
LittleEndian.putUShort(data, base + 6, formulaSize);
|
||||
LittleEndian.putInt(data, base + 8, field_1_unknown_int);
|
||||
|
||||
int pos = base+12;
|
||||
|
||||
if (field_2_refPtg == null) {
|
||||
System.arraycopy(field_2_unknownFormulaData, 0, data, pos, field_2_unknownFormulaData.length);
|
||||
} else {
|
||||
field_2_refPtg.writeBytes(data, pos);
|
||||
}
|
||||
pos += formulaSize;
|
||||
|
||||
int stringLen;
|
||||
if (field_4_ole_classname == null) {
|
||||
// don't write 0x03, stringLen, flag, text
|
||||
stringLen = 0;
|
||||
} else {
|
||||
LittleEndian.putByte(data, pos, 0x03);
|
||||
pos += 1;
|
||||
stringLen = field_4_ole_classname.length();
|
||||
LittleEndian.putUShort(data, pos, stringLen);
|
||||
pos += 2;
|
||||
LittleEndian.putByte(data, pos, field_3_unicode_flag ? 0x01 : 0x00);
|
||||
pos += 1;
|
||||
|
||||
if (field_3_unicode_flag) {
|
||||
StringUtil.putUnicodeLE(field_4_ole_classname, data, pos);
|
||||
pos += stringLen * 2;
|
||||
} else {
|
||||
StringUtil.putCompressedUnicode(field_4_ole_classname, data, pos);
|
||||
pos += stringLen;
|
||||
}
|
||||
}
|
||||
|
||||
// pad to next 2-byte boundary (requires 0 or 1 bytes)
|
||||
switch(idOffset - (pos - 6 - base)) { // 6 for 3 shorts: sid, dataSize, idOffset
|
||||
case 1:
|
||||
LittleEndian.putByte(data, pos, field_4_unknownByte == null ? 0x00 : field_4_unknownByte.intValue());
|
||||
pos ++;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Bad padding calculation (" + idOffset + ", " + (pos-base) + ")");
|
||||
}
|
||||
|
||||
if (field_5_stream_id != null) {
|
||||
LittleEndian.putInt(data, pos, field_5_stream_id.intValue());
|
||||
pos += 4;
|
||||
}
|
||||
System.arraycopy(field_6_unknown, 0, data, pos, field_6_unknown.length);
|
||||
|
||||
return 4 + dataSize;
|
||||
}
|
||||
|
||||
public int getRecordSize() {
|
||||
return 4 + getDataSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stream ID containing the actual data. The data itself
|
||||
* can be found under a top-level directory entry in the OLE2 filesystem
|
||||
* under the name "MBD<var>xxxxxxxx</var>" where <var>xxxxxxxx</var> is
|
||||
* this ID converted into hex (in big endian order, funnily enough.)
|
||||
*
|
||||
* @return the data stream ID. Possibly <code>null</code>
|
||||
*/
|
||||
public Integer getStreamId() {
|
||||
return field_5_stream_id;
|
||||
}
|
||||
|
||||
public String getOLEClassName() {
|
||||
return field_4_ole_classname;
|
||||
}
|
||||
|
||||
public byte[] getObjectData() {
|
||||
return field_6_unknown;
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("[ftPictFmla]\n");
|
||||
sb.append(" .f2unknown = ").append(HexDump.intToHex(field_1_unknown_int)).append("\n");
|
||||
if (field_2_refPtg == null) {
|
||||
sb.append(" .f3unknown = ").append(HexDump.toHex(field_2_unknownFormulaData)).append("\n");
|
||||
} else {
|
||||
sb.append(" .formula = ").append(field_2_refPtg.toString()).append("\n");
|
||||
}
|
||||
if (field_4_ole_classname != null) {
|
||||
sb.append(" .unicodeFlag = ").append(field_3_unicode_flag).append("\n");
|
||||
sb.append(" .oleClassname = ").append(field_4_ole_classname).append("\n");
|
||||
}
|
||||
if (field_4_unknownByte != null) {
|
||||
sb.append(" .f4unknown = ").append(HexDump.byteToHex(field_4_unknownByte.intValue())).append("\n");
|
||||
}
|
||||
if (field_5_stream_id != null) {
|
||||
sb.append(" .streamId = ").append(HexDump.intToHex(field_5_stream_id.intValue())).append("\n");
|
||||
}
|
||||
if (field_6_unknown.length > 0) {
|
||||
sb.append(" .f7unknown = ").append(HexDump.toHex(field_6_unknown)).append("\n");
|
||||
}
|
||||
sb.append("[/ftPictFmla]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public class RecordInputStream extends InputStream {
|
|||
/** Maximum size of a single record (minus the 4 byte header) without a continue*/
|
||||
public final static short MAX_RECORD_DATA_SIZE = 8224;
|
||||
private static final int INVALID_SID_VALUE = -1;
|
||||
|
||||
|
||||
private InputStream in;
|
||||
protected short currentSid;
|
||||
protected short currentLength = -1;
|
||||
|
@ -42,34 +42,34 @@ public class RecordInputStream extends InputStream {
|
|||
protected byte[] data = new byte[MAX_RECORD_DATA_SIZE];
|
||||
protected short recordOffset;
|
||||
protected long pos;
|
||||
|
||||
|
||||
private boolean autoContinue = true;
|
||||
|
||||
public RecordInputStream(InputStream in) throws RecordFormatException {
|
||||
public RecordInputStream(InputStream in) throws RecordFormatException {
|
||||
this.in = in;
|
||||
try {
|
||||
nextSid = LittleEndian.readShort(in);
|
||||
//Dont increment the pos just yet (technically we are at the start of
|
||||
//the record stream until nextRecord is called).
|
||||
//Don't increment the pos just yet (technically we are at the start of
|
||||
//the record stream until nextRecord is called).
|
||||
} catch (IOException ex) {
|
||||
throw new RecordFormatException("Error reading bytes", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/** This method will read a byte from the current record*/
|
||||
public int read() {
|
||||
checkRecordPosition();
|
||||
|
||||
byte result = data[recordOffset];
|
||||
recordOffset += 1;
|
||||
pos += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** This method will read a byte from the current record*/
|
||||
public int read() {
|
||||
checkRecordPosition(LittleEndian.BYTE_SIZE);
|
||||
|
||||
byte result = data[recordOffset];
|
||||
recordOffset += LittleEndian.BYTE_SIZE;
|
||||
pos += LittleEndian.BYTE_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
public short getSid() {
|
||||
return currentSid;
|
||||
}
|
||||
|
||||
|
||||
public short getLength() {
|
||||
return currentLength;
|
||||
}
|
||||
|
@ -85,12 +85,11 @@ public class RecordInputStream extends InputStream {
|
|||
public boolean hasNextRecord() {
|
||||
return nextSid != INVALID_SID_VALUE;
|
||||
}
|
||||
|
||||
|
||||
/** Moves to the next record in the stream.
|
||||
*
|
||||
*
|
||||
* <i>Note: The auto continue flag is reset to true</i>
|
||||
*/
|
||||
|
||||
public void nextRecord() throws RecordFormatException {
|
||||
if ((currentLength != -1) && (currentLength != recordOffset)) {
|
||||
System.out.println("WARN. Unread "+remaining()+" bytes of record 0x"+Integer.toHexString(currentSid));
|
||||
|
@ -100,7 +99,7 @@ public class RecordInputStream extends InputStream {
|
|||
autoContinue = true;
|
||||
try {
|
||||
recordOffset = 0;
|
||||
currentLength = LittleEndian.readShort(in);
|
||||
currentLength = LittleEndian.readShort(in);
|
||||
if (currentLength > MAX_RECORD_DATA_SIZE)
|
||||
throw new RecordFormatException("The content of an excel record cannot exceed "+MAX_RECORD_DATA_SIZE+" bytes");
|
||||
pos += LittleEndian.SHORT_SIZE;
|
||||
|
@ -113,138 +112,124 @@ public class RecordInputStream extends InputStream {
|
|||
// ex45582-22397.xls has one extra byte after the last record
|
||||
// Excel reads that file OK
|
||||
}
|
||||
nextSid = INVALID_SID_VALUE;
|
||||
nextSid = INVALID_SID_VALUE;
|
||||
} else {
|
||||
nextSid = LittleEndian.readShort(in);
|
||||
if (nextSid == INVALID_SID_VALUE) {
|
||||
throw new RecordFormatException("Found sid " + nextSid + " after record with sid 0x"
|
||||
+ Integer.toHexString(currentSid).toUpperCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new RecordFormatException("Error reading bytes", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setAutoContinue(boolean enable) {
|
||||
this.autoContinue = enable;
|
||||
this.autoContinue = enable;
|
||||
}
|
||||
|
||||
|
||||
public boolean getAutoContinue() {
|
||||
return autoContinue;
|
||||
}
|
||||
|
||||
protected void checkRecordPosition() {
|
||||
if (remaining() <= 0) {
|
||||
if (isContinueNext() && autoContinue) {
|
||||
nextRecord();
|
||||
}
|
||||
else throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an 8 bit, signed value
|
||||
*/
|
||||
public byte readByte() {
|
||||
checkRecordPosition();
|
||||
|
||||
byte result = data[recordOffset];
|
||||
recordOffset += 1;
|
||||
pos += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16 bit, signed value
|
||||
*/
|
||||
public short readShort() {
|
||||
checkRecordPosition();
|
||||
|
||||
short result = LittleEndian.getShort(data, recordOffset);
|
||||
recordOffset += LittleEndian.SHORT_SIZE;
|
||||
pos += LittleEndian.SHORT_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int readInt() {
|
||||
checkRecordPosition();
|
||||
|
||||
int result = LittleEndian.getInt(data, recordOffset);
|
||||
recordOffset += LittleEndian.INT_SIZE;
|
||||
pos += LittleEndian.INT_SIZE;
|
||||
return result;
|
||||
}
|
||||
private void checkRecordPosition(int requiredByteCount) {
|
||||
|
||||
public long readLong() {
|
||||
checkRecordPosition();
|
||||
|
||||
long result = LittleEndian.getLong(data, recordOffset);
|
||||
recordOffset += LittleEndian.LONG_SIZE;
|
||||
pos += LittleEndian.LONG_SIZE;
|
||||
return result;
|
||||
}
|
||||
if (remaining() < requiredByteCount) {
|
||||
if (isContinueNext() && autoContinue) {
|
||||
nextRecord();
|
||||
} else {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an 8 bit, signed value
|
||||
*/
|
||||
public byte readByte() {
|
||||
checkRecordPosition(LittleEndian.BYTE_SIZE);
|
||||
|
||||
byte result = data[recordOffset];
|
||||
recordOffset += LittleEndian.BYTE_SIZE;
|
||||
pos += LittleEndian.BYTE_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16 bit, signed value
|
||||
*/
|
||||
public short readShort() {
|
||||
checkRecordPosition(LittleEndian.SHORT_SIZE);
|
||||
|
||||
short result = LittleEndian.getShort(data, recordOffset);
|
||||
recordOffset += LittleEndian.SHORT_SIZE;
|
||||
pos += LittleEndian.SHORT_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int readInt() {
|
||||
checkRecordPosition(LittleEndian.INT_SIZE);
|
||||
|
||||
int result = LittleEndian.getInt(data, recordOffset);
|
||||
recordOffset += LittleEndian.INT_SIZE;
|
||||
pos += LittleEndian.INT_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
public long readLong() {
|
||||
checkRecordPosition(LittleEndian.LONG_SIZE);
|
||||
|
||||
long result = LittleEndian.getLong(data, recordOffset);
|
||||
recordOffset += LittleEndian.LONG_SIZE;
|
||||
pos += LittleEndian.LONG_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an 8 bit, unsigned value
|
||||
*/
|
||||
public short readUByte() {
|
||||
return (short) (readByte() & 0x00FF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16 bit, unsigned value.
|
||||
* @return
|
||||
*/
|
||||
public int readUShort() {
|
||||
checkRecordPosition(LittleEndian.SHORT_SIZE);
|
||||
|
||||
int result = LittleEndian.getUShort(data, recordOffset);
|
||||
recordOffset += LittleEndian.SHORT_SIZE;
|
||||
pos += LittleEndian.SHORT_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
public double readDouble() {
|
||||
checkRecordPosition(LittleEndian.DOUBLE_SIZE);
|
||||
long valueLongBits = LittleEndian.getLong(data, recordOffset);
|
||||
double result = Double.longBitsToDouble(valueLongBits);
|
||||
if (Double.isNaN(result)) {
|
||||
throw new RuntimeException("Did not expect to read NaN"); // (Because Excel typically doesn't write NaN
|
||||
}
|
||||
recordOffset += LittleEndian.DOUBLE_SIZE;
|
||||
pos += LittleEndian.DOUBLE_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an 8 bit, unsigned value
|
||||
*/
|
||||
public short readUByte() {
|
||||
short s = readByte();
|
||||
if(s < 0) {
|
||||
s += 256;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16 bit,un- signed value.
|
||||
* @return
|
||||
*/
|
||||
public int readUShort() {
|
||||
checkRecordPosition();
|
||||
|
||||
int result = LittleEndian.getUShort(data, recordOffset);
|
||||
recordOffset += LittleEndian.SHORT_SIZE;
|
||||
pos += LittleEndian.SHORT_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
public double readDouble() {
|
||||
checkRecordPosition();
|
||||
long valueLongBits = LittleEndian.getLong(data, recordOffset);
|
||||
double result = Double.longBitsToDouble(valueLongBits);
|
||||
if (Double.isNaN(result)) {
|
||||
throw new RuntimeException("Did not expect to read NaN");
|
||||
}
|
||||
recordOffset += LittleEndian.DOUBLE_SIZE;
|
||||
pos += LittleEndian.DOUBLE_SIZE;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public short[] readShortArray() {
|
||||
checkRecordPosition();
|
||||
|
||||
short[] arr = LittleEndian.getShortArray(data, recordOffset);
|
||||
final int size = (2 * (arr.length +1));
|
||||
recordOffset += size;
|
||||
pos += size;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* given a byte array of 16-bit unicode characters, compress to 8-bit and
|
||||
* return a string
|
||||
*
|
||||
* { 0x16, 0x00 } -0x16
|
||||
*
|
||||
* given a byte array of 16-bit unicode characters, compress to 8-bit and
|
||||
* return a string
|
||||
*
|
||||
* { 0x16, 0x00 } -0x16
|
||||
*
|
||||
* @param length the length of the final string
|
||||
* @return the converted string
|
||||
* @exception IllegalArgumentException if len is too large (i.e.,
|
||||
* there is not enough data in string to create a String of that
|
||||
* length)
|
||||
*/
|
||||
* there is not enough data in string to create a String of that
|
||||
* length)
|
||||
*/
|
||||
public String readUnicodeLEString(int length) {
|
||||
if ((length < 0) || (((remaining() / 2) < length) && !isContinueNext())) {
|
||||
throw new IllegalArgumentException("Illegal length - asked for " + length + " but only " + (remaining()/2) + " left!");
|
||||
|
@ -258,11 +243,11 @@ public class RecordInputStream extends InputStream {
|
|||
if(compressByte != 1) throw new IllegalArgumentException("compressByte in continue records must be 1 while reading unicode LE string");
|
||||
}
|
||||
char ch = (char)readShort();
|
||||
buf.append(ch);
|
||||
buf.append(ch);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
public String readCompressedUnicode(int length) {
|
||||
if ((length < 0) || ((remaining() < length) && !isContinueNext())) {
|
||||
throw new IllegalArgumentException("Illegal length " + length);
|
||||
|
@ -277,23 +262,23 @@ public class RecordInputStream extends InputStream {
|
|||
}
|
||||
byte b = readByte();
|
||||
char ch = (char)(0x00FF & b); // avoid sex
|
||||
buf.append(ch);
|
||||
buf.append(ch);
|
||||
}
|
||||
return buf.toString();
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
/** Returns an excel style unicode string from the bytes reminaing in the record.
|
||||
* <i>Note:</i> Unicode strings differ from <b>normal</b> strings due to the addition of
|
||||
* formatting information.
|
||||
*
|
||||
*
|
||||
* @return The unicode string representation of the remaining bytes.
|
||||
*/
|
||||
public UnicodeString readUnicodeString() {
|
||||
return new UnicodeString(this);
|
||||
}
|
||||
|
||||
|
||||
/** Returns the remaining bytes for the current record.
|
||||
*
|
||||
*
|
||||
* @return The remaining bytes of the current record.
|
||||
*/
|
||||
public byte[] readRemainder() {
|
||||
|
@ -304,39 +289,39 @@ public class RecordInputStream extends InputStream {
|
|||
pos += size;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/** Reads all byte data for the current record, including any
|
||||
* that overlaps into any following continue records.
|
||||
*
|
||||
*
|
||||
* @deprecated Best to write a input stream that wraps this one where there is
|
||||
* special sub record that may overlap continue records.
|
||||
*/
|
||||
*/
|
||||
public byte[] readAllContinuedRemainder() {
|
||||
//Using a ByteArrayOutputStream is just an easy way to get a
|
||||
//growable array of the data.
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(2*MAX_RECORD_DATA_SIZE);
|
||||
|
||||
while (isContinueNext()) {
|
||||
byte[] b = readRemainder();
|
||||
byte[] b = readRemainder();
|
||||
out.write(b, 0, b.length);
|
||||
nextRecord();
|
||||
}
|
||||
byte[] b = readRemainder();
|
||||
out.write(b, 0, b.length);
|
||||
|
||||
byte[] b = readRemainder();
|
||||
out.write(b, 0, b.length);
|
||||
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
/** The remaining number of bytes in the <i>current</i> record.
|
||||
*
|
||||
*
|
||||
* @return The number of bytes remaining in the current record
|
||||
*/
|
||||
public int remaining() {
|
||||
return (currentLength - recordOffset);
|
||||
}
|
||||
|
||||
/** Returns true iif a Continue record is next in the excel stream
|
||||
*
|
||||
/** Returns true iif a Continue record is next in the excel stream
|
||||
*
|
||||
* @return True when a ContinueRecord is next.
|
||||
*/
|
||||
public boolean isContinueNext() {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -15,66 +14,66 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
|
||||
|
||||
import org.apache.poi.util.*;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
* The series list record defines the series displayed as an overlay to the main chart record.
|
||||
* NOTE: This source is automatically generated please do not modify this file. Either subclass or
|
||||
* remove the record in src/records/definitions.
|
||||
|
||||
*
|
||||
* The series list record defines the series displayed as an overlay to the main chart record.<br/>
|
||||
* TODO - does this record (0x1016) really exist. It doesn't seem to be referenced in either the OOO or MS doc
|
||||
*
|
||||
* @author Glen Stampoultzis (glens at apache.org)
|
||||
*/
|
||||
public class SeriesListRecord
|
||||
extends Record
|
||||
{
|
||||
public final static short sid = 0x1016;
|
||||
public final class SeriesListRecord extends Record {
|
||||
public final static short sid = 0x1016;
|
||||
private short[] field_1_seriesNumbers;
|
||||
|
||||
|
||||
public SeriesListRecord()
|
||||
{
|
||||
|
||||
public SeriesListRecord(short[] seriesNumbers) {
|
||||
field_1_seriesNumbers = seriesNumbers;
|
||||
}
|
||||
|
||||
public SeriesListRecord(RecordInputStream in)
|
||||
{
|
||||
field_1_seriesNumbers = in.readShortArray();
|
||||
public SeriesListRecord(RecordInputStream in) {
|
||||
int nItems = in.readUShort();
|
||||
short[] ss = new short[nItems];
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
ss[i] = in.readShort();
|
||||
|
||||
}
|
||||
field_1_seriesNumbers = ss;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
public String toString() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
||||
buffer.append("[SERIESLIST]\n");
|
||||
buffer.append(" .seriesNumbers = ")
|
||||
.append(" (").append( getSeriesNumbers() ).append(" )");
|
||||
buffer.append(System.getProperty("line.separator"));
|
||||
buffer.append(" .seriesNumbers= ").append(" (").append( getSeriesNumbers() ).append(" )");
|
||||
buffer.append("\n");
|
||||
|
||||
buffer.append("[/SERIESLIST]\n");
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public int serialize(int offset, byte[] data)
|
||||
{
|
||||
int pos = 0;
|
||||
public int serialize(int offset, byte[] data) {
|
||||
|
||||
LittleEndian.putShort(data, 0 + offset, sid);
|
||||
LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4));
|
||||
int nItems = field_1_seriesNumbers.length;
|
||||
int dataSize = 2 + 2 * nItems;
|
||||
|
||||
LittleEndian.putUShort(data, 0 + offset, sid);
|
||||
LittleEndian.putUShort(data, 2 + offset, dataSize);
|
||||
|
||||
LittleEndian.putShortArray(data, 4 + offset + pos, field_1_seriesNumbers);
|
||||
LittleEndian.putUShort(data, 4 + offset, nItems);
|
||||
|
||||
int pos = offset + 6;
|
||||
for (int i = 0; i < nItems; i++) {
|
||||
LittleEndian.putUShort(data, pos, field_1_seriesNumbers[i]);
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
return getRecordSize();
|
||||
return 4 + dataSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of record (exluding 4 byte header)
|
||||
*/
|
||||
public int getRecordSize()
|
||||
{
|
||||
return 4 + field_1_seriesNumbers.length * 2 + 2;
|
||||
|
@ -86,34 +85,23 @@ public class SeriesListRecord
|
|||
}
|
||||
|
||||
public Object clone() {
|
||||
SeriesListRecord rec = new SeriesListRecord();
|
||||
|
||||
rec.field_1_seriesNumbers = field_1_seriesNumbers;
|
||||
return rec;
|
||||
return new SeriesListRecord((short[]) field_1_seriesNumbers.clone());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the series numbers field for the SeriesList record.
|
||||
*/
|
||||
public short[] getSeriesNumbers()
|
||||
{
|
||||
public short[] getSeriesNumbers() {
|
||||
return field_1_seriesNumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the series numbers field for the SeriesList record.
|
||||
*/
|
||||
public void setSeriesNumbers(short[] field_1_seriesNumbers)
|
||||
{
|
||||
public void setSeriesNumbers(short[] field_1_seriesNumbers) {
|
||||
this.field_1_seriesNumbers = field_1_seriesNumbers;
|
||||
}
|
||||
|
||||
|
||||
} // END OF CLASS
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ import org.apache.poi.util.HexDump;
|
|||
*
|
||||
* @author Daniel Noll
|
||||
*/
|
||||
public class HSSFObjectData
|
||||
public final class HSSFObjectData
|
||||
{
|
||||
/**
|
||||
* Underlying object record ultimately containing a reference to the object.
|
||||
|
@ -60,8 +60,7 @@ public class HSSFObjectData
|
|||
* Returns the OLE2 Class Name of the object
|
||||
*/
|
||||
public String getOLE2ClassName() {
|
||||
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
|
||||
return subRecord.field_5_ole_classname;
|
||||
return findObjectRecord().getOLEClassName();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,9 +71,9 @@ public class HSSFObjectData
|
|||
* @throws IOException if there was an error reading the data.
|
||||
*/
|
||||
public DirectoryEntry getDirectory() throws IOException {
|
||||
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
|
||||
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
|
||||
|
||||
int streamId = ((EmbeddedObjectRefSubRecord) subRecord).getStreamId();
|
||||
int streamId = subRecord.getStreamId().intValue();
|
||||
String streamName = "MBD" + HexDump.toHex(streamId);
|
||||
|
||||
Entry entry = poifs.getRoot().getEntry(streamName);
|
||||
|
@ -91,8 +90,7 @@ public class HSSFObjectData
|
|||
* Entry
|
||||
*/
|
||||
public byte[] getObjectData() {
|
||||
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
|
||||
return subRecord.remainingBytes;
|
||||
return findObjectRecord().getObjectData();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,10 +99,11 @@ public class HSSFObjectData
|
|||
* (Not all do, those that don't have a data portion)
|
||||
*/
|
||||
public boolean hasDirectoryEntry() {
|
||||
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
|
||||
|
||||
// Field 6 tells you
|
||||
return (subRecord.field_6_stream_id != 0);
|
||||
EmbeddedObjectRefSubRecord subRecord = findObjectRecord();
|
||||
|
||||
// 'stream id' field tells you
|
||||
Integer streamId = subRecord.getStreamId();
|
||||
return streamId != null && streamId.intValue() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,7 +116,7 @@ public class HSSFObjectData
|
|||
while (subRecordIter.hasNext()) {
|
||||
Object subRecord = subRecordIter.next();
|
||||
if (subRecord instanceof EmbeddedObjectRefSubRecord) {
|
||||
return (EmbeddedObjectRefSubRecord)subRecord;
|
||||
return (EmbeddedObjectRefSubRecord)subRecord;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -724,8 +724,8 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
|
|||
HSSFName newName = new HSSFName(this, newNameRecord);
|
||||
names.add(newName);
|
||||
|
||||
workbook.cloneDrawings(clonedSheet.getSheet());
|
||||
}
|
||||
workbook.cloneDrawings(clonedSheet.getSheet());
|
||||
// TODO - maybe same logic required for other/all built-in name records
|
||||
|
||||
return clonedSheet;
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.poi.ss.formula;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
|
@ -60,6 +61,9 @@ final class CellCacheEntry {
|
|||
// value type is changing
|
||||
return false;
|
||||
}
|
||||
if (a == BlankEval.INSTANCE) {
|
||||
return b == a;
|
||||
}
|
||||
if (cls == NumberEval.class) {
|
||||
return ((NumberEval)a).getNumberValue() == ((NumberEval)b).getNumberValue();
|
||||
}
|
||||
|
|
|
@ -25,24 +25,24 @@ import org.apache.poi.hssf.util.CellReference;
|
|||
final class CellLocation {
|
||||
public static final CellLocation[] EMPTY_ARRAY = { };
|
||||
|
||||
private final EvaluationWorkbook _book;
|
||||
private final int _bookIx;
|
||||
private final int _sheetIndex;
|
||||
private final int _rowIndex;
|
||||
private final int _columnIndex;
|
||||
private final int _hashCode;
|
||||
|
||||
public CellLocation(EvaluationWorkbook book, int sheetIndex, int rowIndex, int columnIndex) {
|
||||
public CellLocation(int bookIx, int sheetIndex, int rowIndex, int columnIndex) {
|
||||
if (sheetIndex < 0) {
|
||||
throw new IllegalArgumentException("sheetIndex must not be negative");
|
||||
}
|
||||
_book = book;
|
||||
_bookIx = bookIx;
|
||||
_sheetIndex = sheetIndex;
|
||||
_rowIndex = rowIndex;
|
||||
_columnIndex = columnIndex;
|
||||
_hashCode = System.identityHashCode(book) + sheetIndex + 17 * (rowIndex + 17 * columnIndex);
|
||||
_hashCode = _bookIx + 17 * (sheetIndex + 17 * (rowIndex + 17 * columnIndex));
|
||||
}
|
||||
public Object getBook() {
|
||||
return _book;
|
||||
public int getBookIndex() {
|
||||
return _bookIx;
|
||||
}
|
||||
public int getSheetIndex() {
|
||||
return _sheetIndex;
|
||||
|
@ -65,7 +65,7 @@ final class CellLocation {
|
|||
if (getSheetIndex() != other.getSheetIndex()) {
|
||||
return false;
|
||||
}
|
||||
if (getBook() != other.getBook()) {
|
||||
if (getBookIndex() != other.getBookIndex()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -97,7 +97,7 @@ public final class CollaboratingWorkbooksEnvironment {
|
|||
EvaluationCache cache = new EvaluationCache(evalListener);
|
||||
|
||||
for(int i=0; i<nItems; i++) {
|
||||
evaluators[i].attachToEnvironment(env, cache);
|
||||
evaluators[i].attachToEnvironment(env, cache, i);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -203,7 +203,7 @@ final class EvaluationCache {
|
|||
CellLocation clB = (CellLocation) b;
|
||||
|
||||
int cmp;
|
||||
cmp = System.identityHashCode(clA.getBook()) - System.identityHashCode(clB.getBook());
|
||||
cmp = clA.getBookIndex() - clB.getBookIndex();
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ public final class WorkbookEvaluator {
|
|||
|
||||
private final EvaluationWorkbook _workbook;
|
||||
private EvaluationCache _cache;
|
||||
private int _workbookIx;
|
||||
|
||||
private final IEvaluationListener _evaluationListener;
|
||||
private final Map _sheetIndexesBySheet;
|
||||
|
@ -94,6 +95,7 @@ public final class WorkbookEvaluator {
|
|||
_cache = new EvaluationCache(evaluationListener);
|
||||
_sheetIndexesBySheet = new IdentityHashMap();
|
||||
_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
|
||||
_workbookIx = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,9 +113,10 @@ public final class WorkbookEvaluator {
|
|||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
/* package */ void attachToEnvironment(CollaboratingWorkbooksEnvironment collaboratingWorkbooksEnvironment, EvaluationCache cache) {
|
||||
/* package */ void attachToEnvironment(CollaboratingWorkbooksEnvironment collaboratingWorkbooksEnvironment, EvaluationCache cache, int workbookIx) {
|
||||
_collaboratingWorkbookEnvironment = collaboratingWorkbooksEnvironment;
|
||||
_cache = cache;
|
||||
_workbookIx = workbookIx;
|
||||
}
|
||||
/* package */ CollaboratingWorkbooksEnvironment getEnvironment() {
|
||||
return _collaboratingWorkbookEnvironment;
|
||||
|
@ -122,6 +125,7 @@ public final class WorkbookEvaluator {
|
|||
/* package */ void detachFromEnvironment() {
|
||||
_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
|
||||
_cache = new EvaluationCache(_evaluationListener);
|
||||
_workbookIx = 0;
|
||||
}
|
||||
/* package */ IEvaluationListener getEvaluationListener() {
|
||||
return _evaluationListener;
|
||||
|
@ -148,7 +152,7 @@ public final class WorkbookEvaluator {
|
|||
throw new IllegalArgumentException("value must not be null");
|
||||
}
|
||||
int sheetIndex = getSheetIndex(sheet);
|
||||
_cache.setValue(new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex), true, CellLocation.EMPTY_ARRAY, value);
|
||||
_cache.setValue(getCellLoc(sheetIndex, rowIndex, columnIndex), true, CellLocation.EMPTY_ARRAY, value);
|
||||
|
||||
}
|
||||
/**
|
||||
|
@ -157,7 +161,7 @@ public final class WorkbookEvaluator {
|
|||
*/
|
||||
public void notifySetFormula(Sheet sheet, int rowIndex, int columnIndex) {
|
||||
int sheetIndex = getSheetIndex(sheet);
|
||||
_cache.setValue(new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex), false, CellLocation.EMPTY_ARRAY, null);
|
||||
_cache.setValue(getCellLoc(sheetIndex, rowIndex, columnIndex), false, CellLocation.EMPTY_ARRAY, null);
|
||||
|
||||
}
|
||||
private int getSheetIndex(Sheet sheet) {
|
||||
|
@ -175,7 +179,7 @@ public final class WorkbookEvaluator {
|
|||
|
||||
public ValueEval evaluate(Cell srcCell) {
|
||||
int sheetIndex = getSheetIndex(srcCell.getSheet());
|
||||
CellLocation cellLoc = new CellLocation(_workbook, sheetIndex, srcCell.getRowIndex(), srcCell.getCellNum());
|
||||
CellLocation cellLoc = getCellLoc(sheetIndex, srcCell.getRowIndex(), srcCell.getCellNum());
|
||||
return internalEvaluate(srcCell, cellLoc, new EvaluationTracker(_cache));
|
||||
}
|
||||
|
||||
|
@ -471,8 +475,11 @@ public final class WorkbookEvaluator {
|
|||
} else {
|
||||
cell = row.getCell(columnIndex);
|
||||
}
|
||||
CellLocation cellLoc = new CellLocation(_workbook, sheetIndex, rowIndex, columnIndex);
|
||||
CellLocation cellLoc = getCellLoc(sheetIndex, rowIndex, columnIndex);
|
||||
tracker.acceptDependency(cellLoc);
|
||||
return internalEvaluate(cell, cellLoc, tracker);
|
||||
}
|
||||
private CellLocation getCellLoc(int sheetIndex, int rowIndex, int columnIndex) {
|
||||
return new CellLocation(_workbookIx, sheetIndex, rowIndex, columnIndex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -250,20 +250,6 @@ public final class LittleEndian implements LittleEndianConsts {
|
|||
putNumber(data, offset, value, LittleEndianConsts.BYTE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* put a array of shorts into a byte array
|
||||
*
|
||||
*@param data the byte array
|
||||
*@param offset a starting offset into the byte array
|
||||
*@param value the short array
|
||||
*/
|
||||
public static void putShortArray(final byte[] data, final int offset, final short[] value) {
|
||||
putNumber(data, offset, value.length, SHORT_SIZE);
|
||||
for (int i = 0; i < value.length; i++) {
|
||||
putNumber(data, offset + 2 + (i * 2), value[i], SHORT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* put an unsigned short value into a byte array
|
||||
*
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/* ====================================================================
|
||||
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.hslf.examples;
|
||||
|
||||
import org.apache.poi.hslf.usermodel.SlideShow;
|
||||
import org.apache.poi.hslf.usermodel.RichTextRun;
|
||||
import org.apache.poi.hslf.model.*;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Demonstrates how to create hyperlinks in PowerPoint presentations
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class CreateHyperlink {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SlideShow ppt = new SlideShow();
|
||||
|
||||
Slide slide = ppt.createSlide();
|
||||
|
||||
TextBox shape = new TextBox();
|
||||
shape.setText("Apache POI");
|
||||
Rectangle anchor = new Rectangle(100, 100, 200, 50);
|
||||
shape.setAnchor(anchor);
|
||||
|
||||
String text = shape.getText();
|
||||
Hyperlink link = new Hyperlink();
|
||||
link.setAddress("http://www.apache.org");
|
||||
link.setTitle(shape.getText());
|
||||
int linkId = ppt.addHyperlink(link);
|
||||
|
||||
shape.setHyperlink(linkId, 0, text.length());
|
||||
|
||||
slide.addShape(shape);
|
||||
|
||||
FileOutputStream out = new FileOutputStream("hyperlink.ppt");
|
||||
ppt.write(out);
|
||||
out.close();
|
||||
|
||||
}
|
||||
}
|
|
@ -53,6 +53,9 @@ public class Table extends ShapeGroup {
|
|||
public Table(int numrows, int numcols) {
|
||||
super();
|
||||
|
||||
if(numrows < 1) throw new IllegalArgumentException("The number of rows must be greater than 1");
|
||||
if(numcols < 1) throw new IllegalArgumentException("The number of columns must be greater than 1");
|
||||
|
||||
int x=0, y=0, tblWidth=0, tblHeight=0;
|
||||
cells = new TableCell[numrows][numcols];
|
||||
for (int i = 0; i < cells.length; i++) {
|
||||
|
@ -165,11 +168,11 @@ public class Table extends ShapeGroup {
|
|||
Rectangle anchor = sh[i].getAnchor();
|
||||
if(anchor.y != y0){
|
||||
y0 = anchor.y;
|
||||
if(row != null) maxrowlen = Math.max(maxrowlen, row.size());
|
||||
row = new ArrayList();
|
||||
lst.add(row);
|
||||
}
|
||||
row.add(sh[i]);
|
||||
maxrowlen = Math.max(maxrowlen, row.size());
|
||||
}
|
||||
}
|
||||
cells = new TableCell[lst.size()][maxrowlen];
|
||||
|
|
|
@ -547,4 +547,31 @@ public abstract class TextShape extends SimpleShape {
|
|||
return (OEPlaceholderAtom)getClientDataRecord(RecordTypes.OEPlaceholderAtom.typeID);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Assigns a hyperlink to this text shape
|
||||
*
|
||||
* @param linkId id of the hyperlink, @see org.apache.poi.hslf.usermodel.SlideShow#addHyperlink(Hyperlink)
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @see org.apache.poi.hslf.usermodel.SlideShow#addHyperlink(Hyperlink)
|
||||
*/
|
||||
public void setHyperlink(int linkId, int beginIndex, int endIndex){
|
||||
//TODO validate beginIndex and endIndex and throw IllegalArgumentException
|
||||
|
||||
InteractiveInfo info = new InteractiveInfo();
|
||||
InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom();
|
||||
infoAtom.setAction(InteractiveInfoAtom.ACTION_HYPERLINK);
|
||||
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_Url);
|
||||
infoAtom.setHyperlinkID(linkId);
|
||||
|
||||
_txtbox.appendChildRecord(info);
|
||||
|
||||
TxInteractiveInfoAtom txiatom = new TxInteractiveInfoAtom();
|
||||
txiatom.setStartIndex(beginIndex);
|
||||
txiatom.setEndIndex(endIndex);
|
||||
_txtbox.appendChildRecord(txiatom);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public class TxInteractiveInfoAtom extends RecordAtom {
|
|||
/**
|
||||
* Constructs a brand new link related atom record.
|
||||
*/
|
||||
protected TxInteractiveInfoAtom() {
|
||||
public TxInteractiveInfoAtom() {
|
||||
_header = new byte[8];
|
||||
_data = new byte[8];
|
||||
|
||||
|
|
|
@ -60,4 +60,40 @@ public class TestTable extends TestCase {
|
|||
assertEquals(tbl.getNumberOfRows(), tbl3.getNumberOfRows());
|
||||
}
|
||||
|
||||
/**
|
||||
* Error constructing Table when rownum=1
|
||||
*/
|
||||
public void test45889(){
|
||||
SlideShow ppt = new SlideShow();
|
||||
Slide slide = ppt.createSlide();
|
||||
Shape[] shapes;
|
||||
Table tbl1 = new Table(1, 5);
|
||||
assertEquals(5, tbl1.getNumberOfColumns());
|
||||
assertEquals(1, tbl1.getNumberOfRows());
|
||||
slide.addShape(tbl1);
|
||||
|
||||
shapes = slide.getShapes();
|
||||
assertEquals(1, shapes.length);
|
||||
|
||||
Table tbl2 = (Table)shapes[0];
|
||||
assertSame(tbl1.getSpContainer(), tbl2.getSpContainer());
|
||||
|
||||
assertEquals(tbl1.getNumberOfColumns(), tbl2.getNumberOfColumns());
|
||||
assertEquals(tbl1.getNumberOfRows(), tbl2.getNumberOfRows());
|
||||
}
|
||||
|
||||
public void testIllegalCOnstruction(){
|
||||
try {
|
||||
Table tbl = new Table(0, 5);
|
||||
fail("Table(rownum, colnum) must throw IllegalArgumentException if any of tghe arguments is less than 1");
|
||||
} catch (IllegalArgumentException e){
|
||||
|
||||
}
|
||||
try {
|
||||
Table tbl = new Table(5, 0);
|
||||
fail("Table(rownum, colnum) must throw IllegalArgumentException if any of tghe arguments is less than 1");
|
||||
} catch (IllegalArgumentException e){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
==================================================================== */
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.apache.poi.util.HexRead;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.util.HexRead;
|
||||
|
||||
/**
|
||||
* Tests the serialization and deserialization of the TestEmbeddedObjectRefSubRecord
|
||||
* class works correctly. Test data taken directly from a real
|
||||
|
@ -30,53 +30,111 @@ import java.util.Arrays;
|
|||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public class TestEmbeddedObjectRefSubRecord extends TestCase {
|
||||
public final class TestEmbeddedObjectRefSubRecord extends TestCase {
|
||||
|
||||
String data1 = "[20, 00, 05, 00, FC, 10, 76, 01, 02, 24, 14, DF, 00, 03, 10, 00, 00, 46, 6F, 72, 6D, 73, 2E, 43, 68, 65, 63, 6B, 42, 6F, 78, 2E, 31, 00, 00, 00, 00, 00, 70, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ]";
|
||||
String data1 = "[20, 00, 05, 00, FC, 10, 76, 01, 02, 24, 14, DF, 00, 03, 10, 00, 00, 46, 6F, 72, 6D, 73, 2E, 43, 68, 65, 63, 6B, 42, 6F, 78, 2E, 31, 00, 00, 00, 00, 00, 70, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ]";
|
||||
|
||||
public void testStore() throws IOException {
|
||||
public void testStore() {
|
||||
|
||||
byte[] src = HexRead.readFromString(data1);
|
||||
src = TestcaseRecordInputStream.mergeDataAndSid(EmbeddedObjectRefSubRecord.sid, (short)src.length, src);
|
||||
byte[] src = hr(data1);
|
||||
src = TestcaseRecordInputStream.mergeDataAndSid(EmbeddedObjectRefSubRecord.sid, (short)src.length, src);
|
||||
|
||||
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(src));
|
||||
in.nextRecord();
|
||||
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(src));
|
||||
in.nextRecord();
|
||||
|
||||
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in);
|
||||
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord(in);
|
||||
|
||||
byte[] ser = record1.serialize();
|
||||
byte[] ser = record1.serialize();
|
||||
|
||||
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));
|
||||
in2.nextRecord();
|
||||
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);
|
||||
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));
|
||||
in2.nextRecord();
|
||||
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);
|
||||
|
||||
assertTrue(Arrays.equals(src, ser));
|
||||
assertEquals(record1.field_1_stream_id_offset, record2.field_1_stream_id_offset);
|
||||
assertTrue(Arrays.equals(record1.field_2_unknown, record2.field_2_unknown));
|
||||
assertEquals(record1.field_3_unicode_len, record2.field_3_unicode_len);
|
||||
assertEquals(record1.field_4_unicode_flag, record2.field_4_unicode_flag);
|
||||
assertEquals(record1.field_5_ole_classname, record2.field_5_ole_classname);
|
||||
assertEquals(record1.field_6_stream_id, record2.field_6_stream_id);
|
||||
assertTrue(Arrays.equals(record1.remainingBytes, record2.remainingBytes));
|
||||
}
|
||||
assertTrue(Arrays.equals(src, ser));
|
||||
assertEquals(record1.getOLEClassName(), record2.getOLEClassName());
|
||||
|
||||
public void testCreate() throws IOException {
|
||||
byte[] ser2 = record1.serialize();
|
||||
assertTrue(Arrays.equals(ser, ser2));
|
||||
}
|
||||
|
||||
public void testCreate() {
|
||||
|
||||
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord();
|
||||
|
||||
byte[] ser = record1.serialize();
|
||||
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));
|
||||
in2.nextRecord();
|
||||
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);
|
||||
|
||||
assertEquals(record1.getOLEClassName(), record2.getOLEClassName());
|
||||
assertEquals(record1.getStreamId(), record2.getStreamId());
|
||||
|
||||
byte[] ser2 = record1.serialize();
|
||||
assertTrue(Arrays.equals(ser, ser2));
|
||||
}
|
||||
|
||||
|
||||
EmbeddedObjectRefSubRecord record1 = new EmbeddedObjectRefSubRecord();
|
||||
/**
|
||||
* taken from ftPictFmla sub-record in attachment 22645 (offset 0x40AB).
|
||||
*/
|
||||
private static final byte[] data45912 = hr(
|
||||
"09 00 14 00 " +
|
||||
"12 00 0B 00 F8 02 88 04 3B 00 " +
|
||||
"00 00 00 01 00 00 00 01 " +
|
||||
"00 00");
|
||||
|
||||
byte[] ser = record1.serialize();
|
||||
RecordInputStream in2 = new RecordInputStream(new ByteArrayInputStream(ser));
|
||||
in2.nextRecord();
|
||||
EmbeddedObjectRefSubRecord record2 = new EmbeddedObjectRefSubRecord(in2);
|
||||
public void testCameraTool_bug45912() {
|
||||
byte[] data = data45912;
|
||||
RecordInputStream in = new RecordInputStream(new ByteArrayInputStream(data));
|
||||
in.nextRecord();
|
||||
|
||||
assertEquals(record1.field_1_stream_id_offset, record2.field_1_stream_id_offset);
|
||||
assertTrue(Arrays.equals(record1.field_2_unknown, record2.field_2_unknown));
|
||||
assertEquals(record1.field_3_unicode_len, record2.field_3_unicode_len);
|
||||
assertEquals(record1.field_4_unicode_flag, record2.field_4_unicode_flag);
|
||||
assertEquals(record1.field_5_ole_classname, record2.field_5_ole_classname);
|
||||
assertEquals(record1.field_6_stream_id, record2.field_6_stream_id);
|
||||
assertTrue(Arrays.equals(record1.remainingBytes, record2.remainingBytes));
|
||||
EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in);
|
||||
byte[] ser2 = rec.serialize();
|
||||
assertTrue(Arrays.equals(data, ser2));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static byte[] hr(String string) {
|
||||
return HexRead.readFromString(string);
|
||||
}
|
||||
|
||||
/**
|
||||
* tests various examples of OLE controls
|
||||
*/
|
||||
public void testVarious() {
|
||||
String[] rawData = {
|
||||
"12 00 0B 00 70 95 0B 05 3B 01 00 36 00 40 00 18 00 19 00 18",
|
||||
"12 00 0B 00 B0 4D 3E 03 3B 00 00 00 00 01 00 00 80 01 C0 00",
|
||||
"0C 00 05 00 60 AF 3B 03 24 FD FF FE C0 FE",
|
||||
"24 00 05 00 40 42 3E 03 02 80 CD B4 04 03 15 00 00 46 6F 72 6D 73 2E 43 6F 6D 6D 61 6E 64 42 75 74 74 6F 6E 2E 31 00 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00",
|
||||
"22 00 05 00 10 4E 3E 03 02 00 4C CC 04 03 12 00 00 46 6F 72 6D 73 2E 53 70 69 6E 42 75 74 74 6F 6E 2E 31 00 54 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00",
|
||||
"20 00 05 00 E0 41 3E 03 02 00 FC 0B 05 03 10 00 00 46 6F 72 6D 73 2E 43 6F 6D 62 6F 42 6F 78 2E 31 00 74 00 00 00 4C 00 00 00 00 00 00 00 00 00 00 00",
|
||||
"24 00 05 00 00 4C AF 03 02 80 E1 93 05 03 14 00 00 46 6F 72 6D 73 2E 4F 70 74 69 6F 6E 42 75 74 74 6F 6E 2E 31 00 C0 00 00 00 70 00 00 00 00 00 00 00 00 00 00 00",
|
||||
"20 00 05 00 E0 A4 28 04 02 80 EA 93 05 03 10 00 00 46 6F 72 6D 73 2E 43 68 65 63 6B 42 6F 78 2E 31 00 30 01 00 00 6C 00 00 00 00 00 00 00 00 00 00 00",
|
||||
"1C 00 05 00 30 40 3E 03 02 00 CC B4 04 03 0D 00 00 46 6F 72 6D 73 2E 4C 61 62 65 6C 2E 31 9C 01 00 00 54 00 00 00 00 00 00 00 00 00 00 00",
|
||||
"1E 00 05 00 B0 A4 28 04 02 00 D0 0A 05 03 0F 00 00 46 6F 72 6D 73 2E 4C 69 73 74 42 6F 78 2E 31 F0 01 00 00 48 00 00 00 00 00 00 00 00 00 00 00",
|
||||
"24 00 05 00 C0 AF 3B 03 02 80 D1 0A 05 03 14 00 00 46 6F 72 6D 73 2E 54 6F 67 67 6C 65 42 75 74 74 6F 6E 2E 31 00 38 02 00 00 6C 00 00 00 00 00 00 00 00 00 00 00",
|
||||
"1E 00 05 00 90 AF 3B 03 02 80 D4 0A 05 03 0F 00 00 46 6F 72 6D 73 2E 54 65 78 74 42 6F 78 2E 31 A4 02 00 00 48 00 00 00 00 00 00 00 00 00 00 00",
|
||||
"24 00 05 00 60 40 3E 03 02 00 D6 0A 05 03 14 00 00 46 6F 72 6D 73 2E 54 6F 67 67 6C 65 42 75 74 74 6F 6E 2E 31 00 EC 02 00 00 6C 00 00 00 00 00 00 00 00 00 00 00",
|
||||
"20 00 05 00 20 4D 3E 03 02 00 D9 0A 05 03 11 00 00 46 6F 72 6D 73 2E 53 63 72 6F 6C 6C 42 61 72 2E 31 58 03 00 00 20 00 00 00 00 00 00 00 00 00 00 00",
|
||||
"20 00 05 00 00 AF 28 04 02 80 31 AC 04 03 10 00 00 53 68 65 6C 6C 2E 45 78 70 6C 6F 72 65 72 2E 32 00 78 03 00 00 AC 00 00 00 00 00 00 00 00 00 00 00",
|
||||
};
|
||||
|
||||
for (int i = 0; i < rawData.length; i++) {
|
||||
confirmRead(hr(rawData[i]), i);
|
||||
}
|
||||
}
|
||||
|
||||
private static void confirmRead(byte[] data, int i) {
|
||||
RecordInputStream in = new TestcaseRecordInputStream(EmbeddedObjectRefSubRecord.sid, (short)data.length, data);
|
||||
|
||||
EmbeddedObjectRefSubRecord rec = new EmbeddedObjectRefSubRecord(in);
|
||||
byte[] ser2 = rec.serialize();
|
||||
byte[] d2 = (byte[]) data.clone(); // remove sid+len for compare
|
||||
System.arraycopy(ser2, 4, d2, 0, d2.length);
|
||||
if (!Arrays.equals(data, d2)) {
|
||||
fail("re-read NQR for case " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import junit.framework.TestCase;
|
|||
* @author Glen Stampoultzis (glens at apache.org)
|
||||
*/
|
||||
public final class TestSeriesListRecord extends TestCase {
|
||||
byte[] data = new byte[] {
|
||||
private static final byte[] data = {
|
||||
(byte)0x02,(byte)0x00,(byte)0x01,(byte)0x20,(byte)0xff,(byte)0xf0
|
||||
};
|
||||
|
||||
|
@ -43,10 +43,8 @@ public final class TestSeriesListRecord extends TestCase {
|
|||
assertEquals( 4 + 6, record.getRecordSize() );
|
||||
}
|
||||
|
||||
public void testStore()
|
||||
{
|
||||
SeriesListRecord record = new SeriesListRecord();
|
||||
record.setSeriesNumbers( new short[] { (short)0x2001, (short)0xf0ff } );
|
||||
public void testStore() {
|
||||
SeriesListRecord record = new SeriesListRecord(new short[] { (short)0x2001, (short)0xf0ff } );
|
||||
|
||||
byte [] recordBytes = record.serialize();
|
||||
assertEquals(recordBytes.length - 4, data.length);
|
||||
|
|
|
@ -971,7 +971,7 @@ public final class TestBugs extends TestCase {
|
|||
public void test44840() {
|
||||
HSSFWorkbook wb = openSample("WithCheckBoxes.xls");
|
||||
|
||||
// Take a look at the embeded objects
|
||||
// Take a look at the embedded objects
|
||||
List objects = wb.getAllEmbeddedObjects();
|
||||
assertEquals(1, objects.size());
|
||||
|
||||
|
@ -982,10 +982,10 @@ public final class TestBugs extends TestCase {
|
|||
EmbeddedObjectRefSubRecord rec = obj.findObjectRecord();
|
||||
assertNotNull(rec);
|
||||
|
||||
assertEquals(32, rec.field_1_stream_id_offset);
|
||||
assertEquals(0, rec.field_6_stream_id); // WRONG!
|
||||
assertEquals("Forms.CheckBox.1", rec.field_5_ole_classname);
|
||||
assertEquals(12, rec.remainingBytes.length);
|
||||
// assertEquals(32, rec.field_1_stream_id_offset);
|
||||
assertEquals(0, rec.getStreamId().intValue()); // WRONG!
|
||||
assertEquals("Forms.CheckBox.1", rec.getOLEClassName());
|
||||
assertEquals(12, rec.getObjectData().length);
|
||||
|
||||
// Doesn't have a directory
|
||||
assertFalse(obj.hasDirectoryEntry());
|
||||
|
@ -997,7 +997,7 @@ public final class TestBugs extends TestCase {
|
|||
obj.getDirectory();
|
||||
fail();
|
||||
} catch(FileNotFoundException e) {
|
||||
// expectd during successful test
|
||||
// expected during successful test
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
|
@ -26,9 +26,10 @@ import junit.framework.TestSuite;
|
|||
*/
|
||||
public final class AllSSFormulaTests {
|
||||
public static Test suite() {
|
||||
TestSuite result = new TestSuite(AllSSFormulaTests.class.getName());
|
||||
result.addTestSuite(TestEvaluationCache.class);
|
||||
result.addTestSuite(TestWorkbookEvaluator.class);
|
||||
return result;
|
||||
}
|
||||
TestSuite result = new TestSuite(AllSSFormulaTests.class.getName());
|
||||
result.addTestSuite(TestCellCacheEntry.class);
|
||||
result.addTestSuite(TestEvaluationCache.class);
|
||||
result.addTestSuite(TestWorkbookEvaluator.class);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/* ====================================================================
|
||||
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.ss.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests {@link CellCacheEntry}.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class TestCellCacheEntry extends TestCase {
|
||||
|
||||
public void testBasic() {
|
||||
CellCacheEntry cce = new CellCacheEntry();
|
||||
cce.updatePlainValue(new NumberEval(42.0));
|
||||
ValueEval ve = cce.getValue();
|
||||
assertEquals(42, ((NumberEval)ve).getNumberValue(), 0.0);
|
||||
|
||||
cce.setFormulaResult(new NumberEval(10.0), new CellLocation[] { });
|
||||
}
|
||||
|
||||
public void testBlank() {
|
||||
CellCacheEntry cce = new CellCacheEntry();
|
||||
cce.updatePlainValue(BlankEval.INSTANCE);
|
||||
try {
|
||||
cce.updatePlainValue(BlankEval.INSTANCE);
|
||||
} catch (IllegalStateException e) {
|
||||
// bug was visible around svn r700356
|
||||
throw new AssertionFailedError("cache entry does not handle blank values properly");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue