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:
Josh Micich 2008-10-09 06:38:50 +00:00
parent cdfa36cada
commit 7e158d23a5
23 changed files with 785 additions and 430 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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();
}
}

View File

@ -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() {

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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
*

View File

@ -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();
}
}

View File

@ -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];

View File

@ -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);
}
}

View File

@ -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];

View File

@ -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){
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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");
}
}
}