ソースを参照

Merge changes from trunk to the ooxml branch - revisions 634630 to

638000


git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@642554 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_3_5_BETA2
Nick Burch 16年前
コミット
1684f36a8f
35個のファイルの変更1167行の追加336行の削除
  1. 6
    0
      src/documentation/content/xdocs/changes.xml
  2. 6
    0
      src/documentation/content/xdocs/status.xml
  3. 16
    23
      src/java/org/apache/poi/hssf/model/FormulaParser.java
  4. 2
    2
      src/java/org/apache/poi/hssf/model/TextboxShape.java
  5. 69
    24
      src/java/org/apache/poi/hssf/record/formula/AttrPtg.java
  6. 22
    21
      src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
  7. 58
    0
      src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java
  8. 18
    2
      src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java
  9. 4
    0
      src/java/org/apache/poi/poifs/common/POIFSConstants.java
  10. 1
    1
      src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java
  11. 20
    6
      src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java
  12. 45
    18
      src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java
  13. 3
    2
      src/java/org/apache/poi/poifs/storage/RawDataBlockList.java
  14. 71
    0
      src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/PercentEval.java
  15. 1
    75
      src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java
  16. 165
    0
      src/scratchpad/src/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java
  17. 13
    1
      src/scratchpad/src/org/apache/poi/hwpf/model/TextPiece.java
  18. 7
    0
      src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java
  19. 2
    0
      src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java
  20. 217
    0
      src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java
  21. 130
    131
      src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulasFromSpreadsheet.java
  22. 82
    0
      src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java
  23. バイナリ
      src/scratchpad/testcases/org/apache/poi/hwpf/data/Bug28627.doc
  24. 48
    14
      src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestProblems.java
  25. バイナリ
      src/testcases/org/apache/poi/hssf/data/27349-vlookupAcrossSheets.xls
  26. バイナリ
      src/testcases/org/apache/poi/hssf/data/Bug44593.xls
  27. バイナリ
      src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls
  28. 44
    0
      src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java
  29. 41
    0
      src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java
  30. 13
    10
      src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java
  31. 52
    0
      src/testcases/org/apache/poi/hssf/usermodel/TestHSSFTextbox.java
  32. 2
    1
      src/testcases/org/apache/poi/poifs/property/TestPropertyTable.java
  33. 3
    1
      src/testcases/org/apache/poi/poifs/storage/LocalRawDataBlockList.java
  34. 4
    3
      src/testcases/org/apache/poi/poifs/storage/TestRawDataBlockList.java
  35. 2
    1
      src/testcases/org/apache/poi/poifs/storage/TestSmallBlockTableReader.java

+ 6
- 0
src/documentation/content/xdocs/changes.xml ファイルの表示

@@ -36,6 +36,12 @@

<!-- Don't forget to update status.xml too! -->
<release version="3.1-beta1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">44609 - Handle leading spaces in formulas, such as '= 4'</action>
<action dev="POI-DEVELOPERS" type="add">44608 - Support for PercentPtg in the formula evaluator</action>
<action dev="POI-DEVELOPERS" type="fix">44606 - Support calculated string values for evaluated formulas</action>
<action dev="POI-DEVELOPERS" type="add">Add accessors to horizontal and vertical alignment in HSSFTextbox</action>
<action dev="POI-DEVELOPERS" type="add">44593 - Improved handling of short DVRecords</action>
<action dev="POI-DEVELOPERS" type="add">28627 / 44580 - Fix Range.delete() in HWPF</action>
<action dev="POI-DEVELOPERS" type="add">44539 - Support for area references in formulas of rows >= 32768</action>
<action dev="POI-DEVELOPERS" type="add">44536 - Improved support for detecting read-only recommended files</action>
<action dev="POI-DEVELOPERS" type="fix">43901 - Correctly update the internal last cell number when adding and removing cells (previously sometimes off-by-one)</action>

+ 6
- 0
src/documentation/content/xdocs/status.xml ファイルの表示

@@ -33,6 +33,12 @@
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.1-beta1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">44609 - Handle leading spaces in formulas, such as '= 4'</action>
<action dev="POI-DEVELOPERS" type="add">44608 - Support for PercentPtg in the formula evaluator</action>
<action dev="POI-DEVELOPERS" type="fix">44606 - Support calculated string values for evaluated formulas</action>
<action dev="POI-DEVELOPERS" type="add">Add accessors to horizontal and vertical alignment in HSSFTextbox</action>
<action dev="POI-DEVELOPERS" type="add">44593 - Improved handling of short DVRecords</action>
<action dev="POI-DEVELOPERS" type="add">28627 / 44580 - Fix Range.delete() in HWPF</action>
<action dev="POI-DEVELOPERS" type="add">44539 - Support for area references in formulas of rows >= 32768</action>
<action dev="POI-DEVELOPERS" type="add">44536 - Improved support for detecting read-only recommended files</action>
<action dev="POI-DEVELOPERS" type="fix">43901 - Correctly update the internal last cell number when adding and removing cells (previously sometimes off-by-one)</action>

+ 16
- 23
src/java/org/apache/poi/hssf/model/FormulaParser.java ファイルの表示

@@ -943,23 +943,7 @@ end;
}
Stack stack = new Stack();

// Excel allows to have AttrPtg at position 0 (such as Blanks) which
// do not have any operands. Skip them.
int i;
if(ptgs[0] instanceof AttrPtg) {
AttrPtg attrPtg0 = (AttrPtg) ptgs[0];
if(attrPtg0.isSemiVolatile()) {
// no visible formula for semi-volatile
} else {
// TODO -this requirement is unclear and is not addressed by any junits
stack.push(ptgs[0].toFormulaString(book));
}
i=1;
} else {
i=0;
}

for ( ; i < ptgs.length; i++) {
for (int i=0 ; i < ptgs.length; i++) {
Ptg ptg = ptgs[i];
// TODO - what about MemNoMemPtg?
if(ptg instanceof MemAreaPtg || ptg instanceof MemFuncPtg || ptg instanceof MemErrPtg) {
@@ -973,21 +957,30 @@ end;
continue;
}

if (ptg instanceof AttrPtg && ((AttrPtg) ptg).isOptimizedIf()) {
continue;
if (ptg instanceof AttrPtg) {
AttrPtg attrPtg = ((AttrPtg) ptg);
if (attrPtg.isOptimizedIf()) {
continue;
}
if (attrPtg.isSpace()) {
// POI currently doesn't render spaces in formulas
continue;
// but if it ever did, care must be taken:
// tAttrSpace comes *before* the operand it applies to, which may be consistent
// with how the formula text appears but is against the RPN ordering assumed here
}
}

final OperationPtg o = (OperationPtg) ptg;
int nOperands = o.getNumberOfOperands();
final String[] operands = new String[nOperands];

for (int j = nOperands-1; j >= 0; j--) {
for (int j = nOperands-1; j >= 0; j--) { // reverse iteration because args were pushed in-order
if(stack.isEmpty()) {
//TODO: write junit to prove this works
String msg = "Too few arguments suppled to operation token ("
+ o.getClass().getName() + "). Expected (" + nOperands
+ " but got " + (nOperands - j + 1);
throw new FormulaParseException(msg);
+ ") operands but got (" + (nOperands - j + 1) + ")";
throw new IllegalStateException(msg);
}
operands[j] = (String) stack.pop();
}

+ 2
- 2
src/java/org/apache/poi/hssf/model/TextboxShape.java ファイルの表示

@@ -133,8 +133,8 @@ public class TextboxShape
HSSFTextbox shape = hssfShape;

TextObjectRecord obj = new TextObjectRecord();
obj.setHorizontalTextAlignment( TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_LEFT_ALIGNED );
obj.setVerticalTextAlignment( TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_TOP );
obj.setHorizontalTextAlignment( hssfShape.getHorizontalAlignment() );
obj.setVerticalTextAlignment( hssfShape.getVerticalAlignment());
obj.setTextLocked( true );
obj.setTextOrientation( TextObjectRecord.TEXT_ORIENTATION_NONE );
int frLength = ( shape.getString().numFormattingRuns() + 1 ) * 8;

+ 69
- 24
src/java/org/apache/poi/hssf/record/formula/AttrPtg.java ファイルの表示

@@ -33,20 +33,42 @@ import org.apache.poi.util.BitFieldFactory;
* @author Jason Height (jheight at chariot dot net dot au)
*/

public class AttrPtg
extends OperationPtg
{
public final class AttrPtg extends OperationPtg {
public final static byte sid = 0x19;
private final static int SIZE = 4;
private byte field_1_options;
private short field_2_data;
private BitField semiVolatile = BitFieldFactory.getInstance(0x01);
private BitField optiIf = BitFieldFactory.getInstance(0x02);
private BitField optiChoose = BitFieldFactory.getInstance(0x04);
private BitField optGoto = BitFieldFactory.getInstance(0x08);
private BitField sum = BitFieldFactory.getInstance(0x10);
private BitField baxcel = BitFieldFactory.getInstance(0x20);
private BitField space = BitFieldFactory.getInstance(0x40);
// flags 'volatile' and 'space', can be combined.
// OOO spec says other combinations are theoretically possible but not likely to occur.
private static final BitField semiVolatile = BitFieldFactory.getInstance(0x01);
private static final BitField optiIf = BitFieldFactory.getInstance(0x02);
private static final BitField optiChoose = BitFieldFactory.getInstance(0x04);
private static final BitField optGoto = BitFieldFactory.getInstance(0x08); // skip
private static final BitField sum = BitFieldFactory.getInstance(0x10);
private static final BitField baxcel = BitFieldFactory.getInstance(0x20); // 'assignment-style formula in a macro sheet'
private static final BitField space = BitFieldFactory.getInstance(0x40);
public static final class SpaceType {
private SpaceType() {
// no instances of this class
}
/** 00H = Spaces before the next token (not allowed before tParen token) */
public static final int SPACE_BEFORE = 0x00;
/** 01H = Carriage returns before the next token (not allowed before tParen token) */
public static final int CR_BEFORE = 0x01;
/** 02H = Spaces before opening parenthesis (only allowed before tParen token) */
public static final int SPACE_BEFORE_OPEN_PAREN = 0x02;
/** 03H = Carriage returns before opening parenthesis (only allowed before tParen token) */
public static final int CR_BEFORE_OPEN_PAREN = 0x03;
/** 04H = Spaces before closing parenthesis (only allowed before tParen, tFunc, and tFuncVar tokens) */
public static final int SPACE_BEFORE_CLOSE_PAERN = 0x04;
/** 05H = Carriage returns before closing parenthesis (only allowed before tParen, tFunc, and tFuncVar tokens) */
public static final int CR_BEFORE_CLOSE_PAREN = 0x05;
/** 06H = Spaces following the equality sign (only in macro sheets) */
public static final int SPACE_AFTER_EQUALITY = 0x06;
}

public AttrPtg() {
}
@@ -56,6 +78,19 @@ public class AttrPtg
field_1_options = in.readByte();
field_2_data = in.readShort();
}
private AttrPtg(int options, int data) {
field_1_options = (byte) options;
field_2_data = (short) data;
}
/**
* @param type a constant from <tt>SpaceType</tt>
* @param count the number of space characters
*/
public static AttrPtg createSpace(int type, int count) {
int data = type & 0x00FF | (count << 8) & 0x00FFFF;
return new AttrPtg(space.set(0), data);
}

public void setOptions(byte options)
{
@@ -131,21 +166,31 @@ public class AttrPtg
return field_2_data;
}

public String toString()
{
StringBuffer buffer = new StringBuffer();
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");

buffer.append("AttrPtg\n");
buffer.append("options=").append(field_1_options).append("\n");
buffer.append("data =").append(field_2_data).append("\n");
buffer.append("semi =").append(isSemiVolatile()).append("\n");
buffer.append("optimif=").append(isOptimizedIf()).append("\n");
buffer.append("optchos=").append(isOptimizedChoose()).append("\n");
buffer.append("isGoto =").append(isGoto()).append("\n");
buffer.append("isSum =").append(isSum()).append("\n");
buffer.append("isBaxce=").append(isBaxcel()).append("\n");
buffer.append("isSpace=").append(isSpace()).append("\n");
return buffer.toString();
if(isSemiVolatile()) {
sb.append("volatile ");
}
if(isSpace()) {
sb.append("space count=").append((field_2_data >> 8) & 0x00FF);
sb.append(" type=").append(field_2_data & 0x00FF).append(" ");
}
// the rest seem to be mutually exclusive
if(isOptimizedIf()) {
sb.append("if dist=").append(getData());
} else if(isOptimizedChoose()) {
sb.append("choose dist=").append(getData());
} else if(isGoto()) {
sb.append("skip dist=").append(getData());
} else if(isSum()) {
sb.append("sum ");
} else if(isBaxcel()) {
sb.append("assign ");
}
sb.append("]");
return sb.toString();
}

public void writeBytes(byte [] array, int offset)

+ 22
- 21
src/java/org/apache/poi/hssf/usermodel/HSSFCell.java ファイルの表示

@@ -603,29 +603,30 @@ public class HSSFCell implements Cell
if (hvalue == null)
{
setCellType(CELL_TYPE_BLANK, false, row, col, styleIndex);
return;
}
else
{
if ((cellType != CELL_TYPE_STRING ) && ( cellType != CELL_TYPE_FORMULA))
{
setCellType(CELL_TYPE_STRING, false, row, col, styleIndex);
}
int index = 0;

UnicodeString str = hvalue.getUnicodeString();
// jmh if (encoding == ENCODING_COMPRESSED_UNICODE)
// jmh {
// jmh str.setCompressedUnicode();
// jmh } else if (encoding == ENCODING_UTF_16)
// jmh {
// jmh str.setUncompressedUnicode();
// jmh }
index = book.addSSTString(str);
(( LabelSSTRecord ) record).setSSTIndex(index);
stringValue = hvalue;
stringValue.setWorkbookReferences(book, (( LabelSSTRecord ) record));
stringValue.setUnicodeString(book.getSSTString(index));
if (cellType == CELL_TYPE_FORMULA) {
// Set the 'pre-evaluated result' for the formula
// note - formulas do not preserve text formatting.
FormulaRecordAggregate fr = (FormulaRecordAggregate) record;
// must make new sr because fr.getStringRecord() may be null
StringRecord sr = new StringRecord();
sr.setString(hvalue.getString()); // looses format
fr.setStringRecord(sr);
return;
}

if (cellType != CELL_TYPE_STRING) {
setCellType(CELL_TYPE_STRING, false, row, col, styleIndex);
}
int index = 0;

UnicodeString str = hvalue.getUnicodeString();
index = book.addSSTString(str);
(( LabelSSTRecord ) record).setSSTIndex(index);
stringValue = hvalue;
stringValue.setWorkbookReferences(book, (( LabelSSTRecord ) record));
stringValue.setUnicodeString(book.getSSTString(index));
}

public void setCellFormula(String formula) {

+ 58
- 0
src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java ファイルの表示

@@ -19,6 +19,9 @@ package org.apache.poi.hssf.usermodel;

import org.apache.poi.ss.usermodel.RichTextString;

import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;

/**
* A textbox is a shape that may hold a rich text string.
*
@@ -29,7 +32,27 @@ public class HSSFTextbox
{
public final static short OBJECT_TYPE_TEXT = 6;

/**
* How to align text horizontally
*/
public final static short HORIZONTAL_ALIGNMENT_LEFT = 1;
public final static short HORIZONTAL_ALIGNMENT_CENTERED = 2;
public final static short HORIZONTAL_ALIGNMENT_RIGHT = 3;
public final static short HORIZONTAL_ALIGNMENT_JUSTIFIED = 4;
public final static short HORIZONTAL_ALIGNMENT_DISTRIBUTED = 7;

/**
* How to align text vertically
*/
public final static short VERTICAL_ALIGNMENT_TOP = 1;
public final static short VERTICAL_ALIGNMENT_CENTER = 2;
public final static short VERTICAL_ALIGNMENT_BOTTOM = 3;
public final static short VERTICAL_ALIGNMENT_JUSTIFY = 4;
public final static short VERTICAL_ALIGNMENT_DISTRIBUTED= 7;


int marginLeft, marginRight, marginTop, marginBottom;
short halign, valign;

HSSFRichTextString string = new HSSFRichTextString("");

@@ -42,6 +65,9 @@ public class HSSFTextbox
{
super( parent, anchor );
setShapeType(OBJECT_TYPE_TEXT);

halign = HORIZONTAL_ALIGNMENT_LEFT;
valign = VERTICAL_ALIGNMENT_TOP;
}

/**
@@ -123,4 +149,36 @@ public class HSSFTextbox
{
this.marginBottom = marginBottom;
}

/**
* Gets the horizontal alignment.
*/
public short getHorizontalAlignment()
{
return halign;
}

/**
* Sets the horizontal alignment.
*/
public void setHorizontalAlignment( short align )
{
this.halign = align;
}

/**
* Gets the vertical alignment.
*/
public short getVerticalAlignment()
{
return valign;
}

/**
* Sets the vertical alignment.
*/
public void setVerticalAlignment( short align )
{
this.valign = align;
}
}

+ 18
- 2
src/java/org/apache/poi/hssf/util/HSSFCellRangeAddress.java ファイルの表示

@@ -18,6 +18,9 @@ package org.apache.poi.hssf.util;

import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;

import java.util.ArrayList;

/**
@@ -38,6 +41,8 @@ import java.util.ArrayList;

public class HSSFCellRangeAddress
{
private static POILogger logger = POILogFactory.getLogger(HSSFCellRangeAddress.class);
/**
* Number of following ADDR structures
*/
@@ -74,8 +79,19 @@ public class HSSFCellRangeAddress
{
short first_row = in.readShort();
short first_col = in.readShort();
short last_row = in.readShort();
short last_col = in.readShort();
short last_row = first_row;
short last_col = first_col;
if(in.remaining() >= 4) {
last_row = in.readShort();
last_col = in.readShort();
} else {
// Ran out of data
// For now, issue a warning, finish, and
// hope for the best....
logger.log(POILogger.WARN, "Ran out of data reading cell references for DVRecord");
k = this.field_addr_number;
}

AddrStructure region = new AddrStructure(first_row, first_col, last_row, last_col);
this.field_regions_list.add(region);

+ 4
- 0
src/java/org/apache/poi/poifs/common/POIFSConstants.java ファイルの表示

@@ -27,7 +27,11 @@ package org.apache.poi.poifs.common;

public interface POIFSConstants
{
/** Most files use 512 bytes as their big block size */
public static final int BIG_BLOCK_SIZE = 0x0200;
/** Some use 4096 bytes */
public static final int LARGER_BIG_BLOCK_SIZE = 0x1000;
public static final int END_OF_CHAIN = -2;
public static final int PROPERTY_SIZE = 0x0080;
public static final int UNUSED_BLOCK = -1;

+ 1
- 1
src/java/org/apache/poi/poifs/eventfilesystem/POIFSReader.java ファイルの表示

@@ -78,7 +78,7 @@ public class POIFSReader
HeaderBlockReader header_block_reader = new HeaderBlockReader(stream);

// read the rest of the stream into blocks
RawDataBlockList data_blocks = new RawDataBlockList(stream);
RawDataBlockList data_blocks = new RawDataBlockList(stream, header_block_reader.getBigBlockSize());

// set up the block allocation table (necessary for the
// data_blocks to be manageable

+ 20
- 6
src/java/org/apache/poi/poifs/filesystem/POIFSFileSystem.java ファイルの表示

@@ -33,6 +33,7 @@ import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.poifs.dev.POIFSViewable;
import org.apache.poi.poifs.property.DirectoryProperty;
import org.apache.poi.poifs.property.Property;
@@ -63,7 +64,6 @@ public class POIFSFileSystem
{
private static final Log _logger = LogFactory.getLog(POIFSFileSystem.class);
private static final class CloseIgnoringInputStream extends InputStream {

private final InputStream _is;
@@ -91,11 +91,16 @@ public class POIFSFileSystem
private PropertyTable _property_table;
private List _documents;
private DirectoryNode _root;
/**
* What big block size the file uses. Most files
* use 512 bytes, but a few use 4096
*/
private int bigBlockSize = POIFSConstants.BIG_BLOCK_SIZE;

/**
* Constructor, intended for writing
*/

public POIFSFileSystem()
{
_property_table = new PropertyTable();
@@ -138,13 +143,15 @@ public class POIFSFileSystem
this();
boolean success = false;

// read the header block from the stream
HeaderBlockReader header_block_reader;
// read the rest of the stream into blocks
RawDataBlockList data_blocks;
try {
// read the header block from the stream
header_block_reader = new HeaderBlockReader(stream);
data_blocks = new RawDataBlockList(stream);
bigBlockSize = header_block_reader.getBigBlockSize();
// read the rest of the stream into blocks
data_blocks = new RawDataBlockList(stream, bigBlockSize);
success = true;
} finally {
closeInputStream(stream, success);
@@ -307,7 +314,7 @@ public class POIFSFileSystem

// create a list of BATManaged objects: the documents plus the
// property table and the small block table
List bm_objects = new ArrayList();
List bm_objects = new ArrayList();

bm_objects.addAll(_documents);
bm_objects.add(_property_table);
@@ -602,6 +609,13 @@ public class POIFSFileSystem
return "POIFS FileSystem";
}

/**
* @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
*/
public int getBigBlockSize() {
return bigBlockSize;
}
/* ********** END begin implementation of POIFSViewable ********** */
} // end public class POIFSFileSystem


+ 45
- 18
src/java/org/apache/poi/poifs/storage/HeaderBlockReader.java ファイルの表示

@@ -21,8 +21,6 @@ package org.apache.poi.poifs.storage;

import java.io.*;

import java.util.*;

import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
import org.apache.poi.util.IOUtils;
@@ -30,7 +28,6 @@ import org.apache.poi.util.IntegerField;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LongField;
import org.apache.poi.util.ShortField;

/**
* The block containing the archive header
@@ -41,6 +38,11 @@ import org.apache.poi.util.ShortField;
public class HeaderBlockReader
implements HeaderBlockConstants
{
/**
* What big block size the file uses. Most files
* use 512 bytes, but a few use 4096
*/
private int bigBlockSize = POIFSConstants.BIG_BLOCK_SIZE;

// number of big block allocation table blocks (int)
private IntegerField _bat_count;
@@ -69,20 +71,27 @@ public class HeaderBlockReader
public HeaderBlockReader(final InputStream stream)
throws IOException
{
_data = new byte[ POIFSConstants.BIG_BLOCK_SIZE ];
int byte_count = IOUtils.readFully(stream, _data);

if (byte_count != POIFSConstants.BIG_BLOCK_SIZE)
{
if (byte_count == -1)
//Cant have -1 bytes read in the error message!
byte_count = 0;
String type = " byte" + ((byte_count == 1) ? ("")
: ("s"));

throw new IOException("Unable to read entire header; "
+ byte_count + type + " read; expected "
+ POIFSConstants.BIG_BLOCK_SIZE + " bytes");
// At this point, we don't know how big our
// block sizes are
// So, read the first 32 bytes to check, then
// read the rest of the block
byte[] blockStart = new byte[32];
int bsCount = IOUtils.readFully(stream, blockStart);
if(bsCount != 32) {
alertShortRead(bsCount);
}
// Figure out our block size
if(blockStart[30] == 12) {
bigBlockSize = POIFSConstants.LARGER_BIG_BLOCK_SIZE;
}
_data = new byte[ bigBlockSize ];
System.arraycopy(blockStart, 0, _data, 0, blockStart.length);
// Now we can read the rest of our header
int byte_count = IOUtils.readFully(stream, _data, blockStart.length, _data.length - blockStart.length);
if (byte_count+bsCount != bigBlockSize) {
alertShortRead(byte_count);
}

// verify signature
@@ -110,13 +119,24 @@ public class HeaderBlockReader
_xbat_start = new IntegerField(_xbat_start_offset, _data);
_xbat_count = new IntegerField(_xbat_count_offset, _data);
}
private void alertShortRead(int read) throws IOException {
if (read == -1)
//Cant have -1 bytes read in the error message!
read = 0;
String type = " byte" + ((read == 1) ? ("")
: ("s"));

throw new IOException("Unable to read entire header; "
+ read + type + " read; expected "
+ bigBlockSize + " bytes");
}

/**
* get start of Property Table
*
* @return the index of the first block of the Property Table
*/

public int getPropertyStart()
{
return _property_start.get();
@@ -174,5 +194,12 @@ public class HeaderBlockReader
{
return _xbat_start.get();
}
/**
* @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
*/
public int getBigBlockSize() {
return bigBlockSize;
}
} // end public class HeaderBlockReader


+ 3
- 2
src/java/org/apache/poi/poifs/storage/RawDataBlockList.java ファイルの表示

@@ -37,19 +37,20 @@ public class RawDataBlockList
* Constructor RawDataBlockList
*
* @param stream the InputStream from which the data will be read
* @param bigBlockSize The big block size, either 512 bytes or 4096 bytes
*
* @exception IOException on I/O errors, and if an incomplete
* block is read
*/

public RawDataBlockList(final InputStream stream)
public RawDataBlockList(final InputStream stream, int bigBlockSize)
throws IOException
{
List blocks = new ArrayList();

while (true)
{
RawDataBlock block = new RawDataBlock(stream);
RawDataBlock block = new RawDataBlock(stream, bigBlockSize);

if (block.eof())
{

+ 71
- 0
src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/PercentEval.java ファイルの表示

@@ -0,0 +1,71 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */

package org.apache.poi.hssf.record.formula.eval;

import org.apache.poi.hssf.record.formula.PercentPtg;
import org.apache.poi.hssf.record.formula.Ptg;

/**
* Implementation of Excel formula token '%'. <p/>
* @author Josh Micich
*/
public final class PercentEval extends NumericOperationEval {

private PercentPtg _delegate;

private static final ValueEvalToNumericXlator NUM_XLATOR = new ValueEvalToNumericXlator(
(short) (ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.REF_STRING_IS_PARSED));

public PercentEval(Ptg ptg) {
_delegate = (PercentPtg) ptg;
}

protected ValueEvalToNumericXlator getXlator() {
return NUM_XLATOR;
}

public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
if (args.length != 1) {
return ErrorEval.VALUE_INVALID;
}

ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
double d0 = ((NumericValueEval) ve).getNumberValue();
return new NumberEval(d0 / 100);
}

if (ve instanceof BlankEval) {
return NumberEval.ZERO;
}
if (ve instanceof ErrorEval) {
return ve;
}
return ErrorEval.VALUE_INVALID;
}

public int getNumberOfOperands() {
return _delegate.getNumberOfOperands();
}

public int getType() {
return _delegate.getType();
}
}

+ 1
- 75
src/scratchpad/src/org/apache/poi/hssf/usermodel/HSSFFormulaEvaluator.java ファイルの表示

@@ -24,71 +24,40 @@ import java.util.Stack;

import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.formula.AddPtg;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.BoolPtg;
import org.apache.poi.hssf.record.formula.ConcatPtg;
import org.apache.poi.hssf.record.formula.ControlPtg;
import org.apache.poi.hssf.record.formula.DividePtg;
import org.apache.poi.hssf.record.formula.EqualPtg;
import org.apache.poi.hssf.record.formula.FuncPtg;
import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.GreaterEqualPtg;
import org.apache.poi.hssf.record.formula.GreaterThanPtg;
import org.apache.poi.hssf.record.formula.IntPtg;
import org.apache.poi.hssf.record.formula.LessEqualPtg;
import org.apache.poi.hssf.record.formula.LessThanPtg;
import org.apache.poi.hssf.record.formula.MemErrPtg;
import org.apache.poi.hssf.record.formula.MissingArgPtg;
import org.apache.poi.hssf.record.formula.MultiplyPtg;
import org.apache.poi.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.NameXPtg;
import org.apache.poi.hssf.record.formula.NotEqualPtg;
import org.apache.poi.hssf.record.formula.NumberPtg;
import org.apache.poi.hssf.record.formula.OperationPtg;
import org.apache.poi.hssf.record.formula.ParenthesisPtg;
import org.apache.poi.hssf.record.formula.PowerPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
import org.apache.poi.hssf.record.formula.ReferencePtg;
import org.apache.poi.hssf.record.formula.StringPtg;
import org.apache.poi.hssf.record.formula.SubtractPtg;
import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
import org.apache.poi.hssf.record.formula.UnionPtg;
import org.apache.poi.hssf.record.formula.UnknownPtg;
import org.apache.poi.hssf.record.formula.eval.AddEval;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.Area3DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
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.ConcatEval;
import org.apache.poi.hssf.record.formula.eval.DivideEval;
import org.apache.poi.hssf.record.formula.eval.EqualEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.FuncVarEval;
import org.apache.poi.hssf.record.formula.eval.FunctionEval;
import org.apache.poi.hssf.record.formula.eval.GreaterEqualEval;
import org.apache.poi.hssf.record.formula.eval.GreaterThanEval;
import org.apache.poi.hssf.record.formula.eval.LessEqualEval;
import org.apache.poi.hssf.record.formula.eval.LessThanEval;
import org.apache.poi.hssf.record.formula.eval.MultiplyEval;
import org.apache.poi.hssf.record.formula.eval.NameEval;
import org.apache.poi.hssf.record.formula.eval.NotEqualEval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.OperationEval;
import org.apache.poi.hssf.record.formula.eval.PowerEval;
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
import org.apache.poi.hssf.record.formula.eval.Ref3DEval;
import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.SubtractEval;
import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval;
import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;

/**
@@ -98,8 +67,6 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
public class HSSFFormulaEvaluator {
// params to lookup the right constructor using reflection
private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };

private static final Class[] VALUE_CONTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };

private static final Class[] AREA3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval[].class };
@@ -111,8 +78,6 @@ public class HSSFFormulaEvaluator {
// Maps for mapping *Eval to *Ptg
private static final Map VALUE_EVALS_MAP = new HashMap();

private static final Map OPERATION_EVALS_MAP = new HashMap();

/*
* Following is the mapping between the Ptg tokens returned
* by the FormulaParser and the *Eval classes that are used
@@ -124,26 +89,6 @@ public class HSSFFormulaEvaluator {
VALUE_EVALS_MAP.put(NumberPtg.class, NumberEval.class);
VALUE_EVALS_MAP.put(StringPtg.class, StringEval.class);

OPERATION_EVALS_MAP.put(AddPtg.class, AddEval.class);
OPERATION_EVALS_MAP.put(ConcatPtg.class, ConcatEval.class);
OPERATION_EVALS_MAP.put(DividePtg.class, DivideEval.class);
OPERATION_EVALS_MAP.put(EqualPtg.class, EqualEval.class);
//OPERATION_EVALS_MAP.put(ExpPtg.class, ExpEval.class); // TODO: check
// this
OPERATION_EVALS_MAP.put(FuncPtg.class, FuncVarEval.class); // TODO:
// check this
OPERATION_EVALS_MAP.put(FuncVarPtg.class, FuncVarEval.class);
OPERATION_EVALS_MAP.put(GreaterEqualPtg.class, GreaterEqualEval.class);
OPERATION_EVALS_MAP.put(GreaterThanPtg.class, GreaterThanEval.class);
OPERATION_EVALS_MAP.put(LessEqualPtg.class, LessEqualEval.class);
OPERATION_EVALS_MAP.put(LessThanPtg.class, LessThanEval.class);
OPERATION_EVALS_MAP.put(MultiplyPtg.class, MultiplyEval.class);
OPERATION_EVALS_MAP.put(NotEqualPtg.class, NotEqualEval.class);
OPERATION_EVALS_MAP.put(PowerPtg.class, PowerEval.class);
OPERATION_EVALS_MAP.put(SubtractPtg.class, SubtractEval.class);
OPERATION_EVALS_MAP.put(UnaryMinusPtg.class, UnaryMinusEval.class);
OPERATION_EVALS_MAP.put(UnaryPlusPtg.class, UnaryPlusEval.class);

}

@@ -402,7 +347,7 @@ public class HSSFFormulaEvaluator {
if (optg instanceof AttrPtg) { continue; }
if (optg instanceof UnionPtg) { continue; }

OperationEval operation = (OperationEval) getOperationEvalForPtg(optg);
OperationEval operation = OperationEvaluatorFactory.create(optg);

int numops = operation.getNumberOfOperands();
Eval[] ops = new Eval[numops];
@@ -557,25 +502,6 @@ public class HSSFFormulaEvaluator {
return values;
}

/**
* returns the OperationEval concrete impl instance corresponding
* to the suplied operationPtg
* @param ptg
*/
protected static Eval getOperationEvalForPtg(OperationPtg ptg) {
Eval retval = null;

Class clazz = (Class) OPERATION_EVALS_MAP.get(ptg.getClass());
try {
Constructor constructor = clazz.getConstructor(OPERATION_CONSTRUCTOR_CLASS_ARRAY);
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
}
catch (Exception e) {
throw new RuntimeException("Fatal Error: ", e);
}
return retval;
}

/**
* returns an appropriate Eval impl instance for the Ptg. The Ptg must be
* one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,

+ 165
- 0
src/scratchpad/src/org/apache/poi/hssf/usermodel/OperationEvaluatorFactory.java ファイルの表示

@@ -0,0 +1,165 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */

package org.apache.poi.hssf.usermodel;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import org.apache.poi.hssf.record.formula.AddPtg;
import org.apache.poi.hssf.record.formula.ConcatPtg;
import org.apache.poi.hssf.record.formula.DividePtg;
import org.apache.poi.hssf.record.formula.EqualPtg;
import org.apache.poi.hssf.record.formula.ExpPtg;
import org.apache.poi.hssf.record.formula.FuncPtg;
import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.GreaterEqualPtg;
import org.apache.poi.hssf.record.formula.GreaterThanPtg;
import org.apache.poi.hssf.record.formula.LessEqualPtg;
import org.apache.poi.hssf.record.formula.LessThanPtg;
import org.apache.poi.hssf.record.formula.MultiplyPtg;
import org.apache.poi.hssf.record.formula.NotEqualPtg;
import org.apache.poi.hssf.record.formula.OperationPtg;
import org.apache.poi.hssf.record.formula.PercentPtg;
import org.apache.poi.hssf.record.formula.PowerPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.SubtractPtg;
import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
import org.apache.poi.hssf.record.formula.eval.AddEval;
import org.apache.poi.hssf.record.formula.eval.ConcatEval;
import org.apache.poi.hssf.record.formula.eval.DivideEval;
import org.apache.poi.hssf.record.formula.eval.EqualEval;
import org.apache.poi.hssf.record.formula.eval.FuncVarEval;
import org.apache.poi.hssf.record.formula.eval.GreaterEqualEval;
import org.apache.poi.hssf.record.formula.eval.GreaterThanEval;
import org.apache.poi.hssf.record.formula.eval.LessEqualEval;
import org.apache.poi.hssf.record.formula.eval.LessThanEval;
import org.apache.poi.hssf.record.formula.eval.MultiplyEval;
import org.apache.poi.hssf.record.formula.eval.NotEqualEval;
import org.apache.poi.hssf.record.formula.eval.OperationEval;
import org.apache.poi.hssf.record.formula.eval.PercentEval;
import org.apache.poi.hssf.record.formula.eval.PowerEval;
import org.apache.poi.hssf.record.formula.eval.SubtractEval;
import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval;
import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;

/**
* This class creates <tt>OperationEval</tt> instances to help evaluate <tt>OperationPtg</tt>
* formula tokens.
*
* @author Josh Micich
*/
final class OperationEvaluatorFactory {
private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
private static final Map _constructorsByPtgClass = initialiseConstructorsMap();
private OperationEvaluatorFactory() {
// no instances of this class
}
private static Map initialiseConstructorsMap() {
Map m = new HashMap(32);
add(m, AddPtg.class, AddEval.class);
add(m, ConcatPtg.class, ConcatEval.class);
add(m, DividePtg.class, DivideEval.class);
add(m, EqualPtg.class, EqualEval.class);
add(m, FuncPtg.class, FuncVarEval.class);
add(m, FuncVarPtg.class, FuncVarEval.class);
add(m, GreaterEqualPtg.class, GreaterEqualEval.class);
add(m, GreaterThanPtg.class, GreaterThanEval.class);
add(m, LessEqualPtg.class, LessEqualEval.class);
add(m, LessThanPtg.class, LessThanEval.class);
add(m, MultiplyPtg.class, MultiplyEval.class);
add(m, NotEqualPtg.class, NotEqualEval.class);
add(m, PercentPtg.class, PercentEval.class);
add(m, PowerPtg.class, PowerEval.class);
add(m, SubtractPtg.class, SubtractEval.class);
add(m, UnaryMinusPtg.class, UnaryMinusEval.class);
add(m, UnaryPlusPtg.class, UnaryPlusEval.class);
return m;
}

private static void add(Map m, Class ptgClass, Class evalClass) {
// perform some validation now, to keep later exception handlers simple
if(!Ptg.class.isAssignableFrom(ptgClass)) {
throw new IllegalArgumentException("Expected Ptg subclass");
}
if(!OperationEval.class.isAssignableFrom(evalClass)) {
throw new IllegalArgumentException("Expected OperationEval subclass");
}
if (!Modifier.isPublic(evalClass.getModifiers())) {
throw new RuntimeException("Eval class must be public");
}
if (Modifier.isAbstract(evalClass.getModifiers())) {
throw new RuntimeException("Eval class must not be abstract");
}
Constructor constructor;
try {
constructor = evalClass.getDeclaredConstructor(OPERATION_CONSTRUCTOR_CLASS_ARRAY);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Missing constructor");
}
if (!Modifier.isPublic(constructor.getModifiers())) {
throw new RuntimeException("Eval constructor must be public");
}
m.put(ptgClass, constructor);
}
/**
* returns the OperationEval concrete impl instance corresponding
* to the supplied operationPtg
*/
public static OperationEval create(OperationPtg ptg) {
if(ptg == null) {
throw new IllegalArgumentException("ptg must not be null");
}
Class ptgClass = ptg.getClass();
Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass);
if(constructor == null) {
if(ptgClass == ExpPtg.class) {
// ExpPtg is used for array formulas and shared formulas.
// it is currently unsupported, and may not even get implemented here
throw new RuntimeException("ExpPtg currently not supported");
}
throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")");
}
Object result;
Object[] initargs = { ptg };
try {
result = constructor.newInstance(initargs);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
return (OperationEval) result;
}
}

+ 13
- 1
src/scratchpad/src/org/apache/poi/hwpf/model/TextPiece.java ファイルの表示

@@ -90,7 +90,19 @@ public class TextPiece extends PropertyNode implements Comparable

public void adjustForDelete(int start, int length)
{

int myStart = getStart();
int myEnd = getEnd();
int end = start + length;

/* do we have to delete from this text piece? */
if (start <= myEnd && end >= myStart) {
/* find where the deleted area overlaps with this text piece */
int overlapStart = Math.max(myStart, start);
int overlapEnd = Math.min(myEnd, end);
((StringBuffer)_buf).delete(overlapStart, overlapEnd);
super.adjustForDelete(start, length);
}
}

public int characterLength()

+ 7
- 0
src/scratchpad/src/org/apache/poi/hwpf/usermodel/Range.java ファイルの表示

@@ -494,6 +494,7 @@ public class Range
int numSections = _sections.size();
int numRuns = _characters.size();
int numParagraphs = _paragraphs.size();
int numTextPieces = _text.size();

for (int x = _charStart; x < numRuns; x++)
{
@@ -512,6 +513,12 @@ public class Range
SEPX sepx = (SEPX)_sections.get(x);
sepx.adjustForDelete(_start, _end - _start);
}
for (int x = _textStart; x < numTextPieces; x++)
{
TextPiece piece = (TextPiece)_text.get(x);
piece.adjustForDelete(_start, _end - _start);
}
}

/**

+ 2
- 0
src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/AllFormulaEvalTests.java ファイルの表示

@@ -31,7 +31,9 @@ public class AllFormulaEvalTests {
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula.eval");
result.addTestSuite(TestCircularReferences.class);
result.addTestSuite(TestExternalFunction.class);
result.addTestSuite(TestFormulaBugs.class);
result.addTestSuite(TestFormulasFromSpreadsheet.class);
result.addTestSuite(TestPercentEval.class);
result.addTestSuite(TestUnaryPlusEval.class);
return result;
}

+ 217
- 0
src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulaBugs.java ファイルの表示

@@ -0,0 +1,217 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */

package org.apache.poi.hssf.record.formula.eval;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import junit.framework.AssertionFailedError;
import junit.framework.TestCase;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;

/**
* Miscellaneous tests for bugzilla entries.<p/> The test name contains the
* bugzilla bug id.
*
*
* @author Josh Micich
*/
public final class TestFormulaBugs extends TestCase {

private static final String TEST_DATA_DIR_SYS_PROPERTY_NAME = "HSSF.testdata.path";

/**
* Opens a sample file from the standard HSSF test data directory
*
* @return an open <tt>InputStream</tt> for the specified sample file
*/
private static InputStream openSampleFileStream(String sampleFileName) {
// TODO - move this method somewhere common
String dataDirName = System
.getProperty(TEST_DATA_DIR_SYS_PROPERTY_NAME);
if (dataDirName == null) {
throw new RuntimeException("Must set system property '"
+ TEST_DATA_DIR_SYS_PROPERTY_NAME
+ "' before running tests");
}
File dataDir = new File(dataDirName);
if (!dataDir.exists()) {
throw new RuntimeException("Data dir '" + dataDirName
+ "' specified by system property '"
+ TEST_DATA_DIR_SYS_PROPERTY_NAME + "' does not exist");
}
File f = new File(dataDir, sampleFileName);
if (!f.exists()) {
throw new RuntimeException("Sample file '" + sampleFileName
+ "' not found in data dir '" + dataDirName + "'");
}
InputStream is;
try {
is = new FileInputStream(f);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
return is;
}

/**
* Bug 27349 - VLOOKUP with reference to another sheet.<p/> This test was
* added <em>long</em> after the relevant functionality was fixed.
*/
public void test27349() {
// 27349-vlookupAcrossSheets.xls is bugzilla/attachment.cgi?id=10622
InputStream is = openSampleFileStream("27349-vlookupAcrossSheets.xls");
HSSFWorkbook wb;
try {
// original bug may have thrown exception here, or output warning to
// stderr
wb = new HSSFWorkbook(is);
} catch (IOException e) {
throw new RuntimeException(e);
}

HSSFSheet sheet = wb.getSheetAt(0);
HSSFRow row = sheet.getRow(1);
HSSFCell cell = row.getCell(0);

// this definitely would have failed due to 27349
assertEquals("VLOOKUP(1,'DATA TABLE'!$A$8:'DATA TABLE'!$B$10,2)", cell
.getCellFormula());

// We might as well evaluate the formula
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
fe.setCurrentRow(row);
CellValue cv = fe.evaluate(cell);

assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(3.0, cv.getNumberValue(), 0.0);
}

/**
* Bug 27405 - isnumber() formula always evaluates to false in if statement<p/>
*
* seems to be a duplicate of 24925
*/
public void test27405() {

HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("input");
// input row 0
HSSFRow row = sheet.createRow((short) 0);
HSSFCell cell = row.createCell((short) 0);
cell = row.createCell((short) 1);
cell.setCellValue(1); // B1
// input row 1
row = sheet.createRow((short) 1);
cell = row.createCell((short) 1);
cell.setCellValue(999); // B2

int rno = 4;
row = sheet.createRow(rno);
cell = row.createCell((short) 1); // B5
cell.setCellFormula("isnumber(b1)");
cell = row.createCell((short) 3); // D5
cell.setCellFormula("IF(ISNUMBER(b1),b1,b2)");

if (false) { // set true to check excel file manually
// bug report mentions 'Editing the formula in excel "fixes" the problem.'
try {
FileOutputStream fileOut = new FileOutputStream("27405output.xls");
wb.write(fileOut);
fileOut.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// use POI's evaluator as an extra sanity check
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
fe.setCurrentRow(row);
CellValue cv;
cv = fe.evaluate(cell);
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(1.0, cv.getNumberValue(), 0.0);
cv = fe.evaluate(row.getCell(1));
assertEquals(HSSFCell.CELL_TYPE_BOOLEAN, cv.getCellType());
assertEquals(true, cv.getBooleanValue());
}

/**
* Bug 42448 - Can't parse SUMPRODUCT(A!C7:A!C67, B8:B68) / B69 <p/>
*/
public void test42448() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet1 = wb.createSheet("Sheet1");

HSSFRow row = sheet1.createRow(0);
HSSFCell cell = row.createCell((short) 0);

// it's important to create the referenced sheet first
HSSFSheet sheet2 = wb.createSheet("A"); // note name 'A'
// TODO - POI crashes if the formula is added before this sheet
// RuntimeException("Zero length string is an invalid sheet name")
// Excel doesn't crash but the formula doesn't work until it is
// re-entered

String inputFormula = "SUMPRODUCT(A!C7:A!C67, B8:B68) / B69"; // as per bug report
try {
cell.setCellFormula(inputFormula);
} catch (StringIndexOutOfBoundsException e) {
throw new AssertionFailedError("Identified bug 42448");
}

assertEquals("SUMPRODUCT(A!C7:C67,B8:B68)/B69", cell.getCellFormula());

// might as well evaluate the sucker...

addCell(sheet2, 5, 2, 3.0); // A!C6
addCell(sheet2, 6, 2, 4.0); // A!C7
addCell(sheet2, 66, 2, 5.0); // A!C67
addCell(sheet2, 67, 2, 6.0); // A!C68

addCell(sheet1, 6, 1, 7.0); // B7
addCell(sheet1, 7, 1, 8.0); // B8
addCell(sheet1, 67, 1, 9.0); // B68
addCell(sheet1, 68, 1, 10.0); // B69

double expectedResult = (4.0 * 8.0 + 5.0 * 9.0) / 10.0;

HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet1, wb);
fe.setCurrentRow(row);
CellValue cv = fe.evaluate(cell);

assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(expectedResult, cv.getNumberValue(), 0.0);
}

private static void addCell(HSSFSheet sheet, int rowIx, int colIx,
double value) {
sheet.createRow(rowIx).createCell((short) colIx).setCellValue(value);
}
}

+ 130
- 131
src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/TestFormulasFromSpreadsheet.java ファイルの表示

@@ -15,7 +15,6 @@
* limitations under the License.
*/


package org.apache.poi.hssf.record.formula.eval;

import java.io.FileInputStream;
@@ -59,36 +58,36 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
* Name of the test spreadsheet (found in the standard test data folder)
*/
public final static String FILENAME = "FormulaEvalTestData.xls";
/**
* Row (zero-based) in the test spreadsheet where the operator examples start.
*/
/**
* Row (zero-based) in the test spreadsheet where the operator examples start.
*/
public static final int START_OPERATORS_ROW_INDEX = 22; // Row '23'
/**
* Row (zero-based) in the test spreadsheet where the function examples start.
*/
public static final int START_FUNCTIONS_ROW_INDEX = 83; // Row '84'
/**
* Row (zero-based) in the test spreadsheet where the function examples start.
*/
public static final int START_FUNCTIONS_ROW_INDEX = 87; // Row '88'
/**
* Index of the column that contains the function names
*/
public static final short COLUMN_INDEX_FUNCTION_NAME = 1; // Column 'B'
public static final short COLUMN_INDEX_FUNCTION_NAME = 1; // Column 'B'
/**
* Used to indicate when there are no more functions left
*/
/**
* Used to indicate when there are no more functions left
*/
public static final String FUNCTION_NAMES_END_SENTINEL = "<END-OF-FUNCTIONS>";
/**
* Index of the column where the test values start (for each function)
*/
public static final short COLUMN_INDEX_FIRST_TEST_VALUE = 3; // Column 'D'
/**
* Each function takes 4 rows in the test spreadsheet
*/
public static final short COLUMN_INDEX_FIRST_TEST_VALUE = 3; // Column 'D'
/**
* Each function takes 4 rows in the test spreadsheet
*/
public static final int NUMBER_OF_ROWS_PER_FUNCTION = 4;
}

private HSSFWorkbook workbook;
private HSSFWorkbook workbook;
private HSSFSheet sheet;
// Note - multiple failures are aggregated before ending.
// If one or more functions fail, a single AssertionFailedError is thrown at the end
@@ -97,138 +96,138 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
private int _evaluationFailureCount;
private int _evaluationSuccessCount;

private static final HSSFCell getExpectedValueCell(HSSFRow row, short columnIndex) {
if (row == null) {
return null;
}
return row.getCell(columnIndex);
}
private static final HSSFCell getExpectedValueCell(HSSFRow row, short columnIndex) {
if (row == null) {
return null;
}
return row.getCell(columnIndex);
}


private static void confirmExpectedResult(String msg, HSSFCell expected, HSSFFormulaEvaluator.CellValue actual) {
if (expected == null) {
private static void confirmExpectedResult(String msg, HSSFCell expected, HSSFFormulaEvaluator.CellValue actual) {
if (expected == null) {
throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
}
if(actual == null) {
throw new AssertionFailedError(msg + " - actual value was null");
}
if (expected.getCellType() == HSSFCell.CELL_TYPE_STRING) {
String value = expected.getRichStringCellValue().getString();
if (value.startsWith("#")) {
// TODO - this code never called
expected.setCellType(HSSFCell.CELL_TYPE_ERROR);
// expected.setCellErrorValue(...?);
}
String value = expected.getRichStringCellValue().getString();
if (value.startsWith("#")) {
// TODO - this code never called
expected.setCellType(HSSFCell.CELL_TYPE_ERROR);
// expected.setCellErrorValue(...?);
}
}
switch (expected.getCellType()) {
case HSSFCell.CELL_TYPE_BLANK:
assertEquals(msg, HSSFCell.CELL_TYPE_BLANK, actual.getCellType());
break;
assertEquals(msg, HSSFCell.CELL_TYPE_BLANK, actual.getCellType());
break;
case HSSFCell.CELL_TYPE_BOOLEAN:
assertEquals(msg, HSSFCell.CELL_TYPE_BOOLEAN, actual.getCellType());
assertEquals(msg, expected.getBooleanCellValue(), actual.getBooleanValue());
break;
assertEquals(msg, HSSFCell.CELL_TYPE_BOOLEAN, actual.getCellType());
assertEquals(msg, expected.getBooleanCellValue(), actual.getBooleanValue());
break;
case HSSFCell.CELL_TYPE_ERROR:
assertEquals(msg, HSSFCell.CELL_TYPE_ERROR, actual.getCellType());
if(false) { // TODO: fix ~45 functions which are currently returning incorrect error values
assertEquals(msg, expected.getErrorCellValue(), actual.getErrorValue());
}
break;
assertEquals(msg, HSSFCell.CELL_TYPE_ERROR, actual.getCellType());
if(false) { // TODO: fix ~45 functions which are currently returning incorrect error values
assertEquals(msg, expected.getErrorCellValue(), actual.getErrorValue());
}
break;
case HSSFCell.CELL_TYPE_FORMULA: // will never be used, since we will call method after formula evaluation
throw new AssertionFailedError("Cannot expect formula as result of formula evaluation: " + msg);
throw new AssertionFailedError("Cannot expect formula as result of formula evaluation: " + msg);
case HSSFCell.CELL_TYPE_NUMERIC:
assertEquals(msg, HSSFCell.CELL_TYPE_NUMERIC, actual.getCellType());
TestMathX.assertEquals(msg, expected.getNumericCellValue(), actual.getNumberValue(), TestMathX.POS_ZERO, TestMathX.DIFF_TOLERANCE_FACTOR);
// double delta = Math.abs(expected.getNumericCellValue()-actual.getNumberValue());
// double pctExpected = Math.abs(0.00001*expected.getNumericCellValue());
// assertTrue(msg, delta <= pctExpected);
break;
assertEquals(msg, HSSFCell.CELL_TYPE_NUMERIC, actual.getCellType());
TestMathX.assertEquals(msg, expected.getNumericCellValue(), actual.getNumberValue(), TestMathX.POS_ZERO, TestMathX.DIFF_TOLERANCE_FACTOR);
// double delta = Math.abs(expected.getNumericCellValue()-actual.getNumberValue());
// double pctExpected = Math.abs(0.00001*expected.getNumericCellValue());
// assertTrue(msg, delta <= pctExpected);
break;
case HSSFCell.CELL_TYPE_STRING:
assertEquals(msg, HSSFCell.CELL_TYPE_STRING, actual.getCellType());
assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getRichTextStringValue().getString());
break;
assertEquals(msg, HSSFCell.CELL_TYPE_STRING, actual.getCellType());
assertEquals(msg, expected.getRichStringCellValue().getString(), actual.getRichTextStringValue().getString());
break;
}
}
}


protected void setUp() throws Exception {
if (workbook == null) {
String filePath = System.getProperty("HSSF.testdata.path")+ "/" + SS.FILENAME;
FileInputStream fin = new FileInputStream( filePath );
workbook = new HSSFWorkbook( fin );
sheet = workbook.getSheetAt( 0 );
}
_functionFailureCount = 0;
_functionSuccessCount = 0;
_evaluationFailureCount = 0;
_evaluationSuccessCount = 0;
}
public void testFunctionsFromTestSpreadsheet() {
processFunctionGroup(SS.START_OPERATORS_ROW_INDEX, null);
processFunctionGroup(SS.START_FUNCTIONS_ROW_INDEX, null);
// example for debugging individual functions/operators:
// processFunctionGroup(SS.START_OPERATORS_ROW_INDEX, "ConcatEval");
// processFunctionGroup(SS.START_FUNCTIONS_ROW_INDEX, "AVERAGE");
// confirm results
String successMsg = "There were "
+ _evaluationSuccessCount + " successful evaluation(s) and "
if (workbook == null) {
String filePath = System.getProperty("HSSF.testdata.path")+ "/" + SS.FILENAME;
FileInputStream fin = new FileInputStream( filePath );
workbook = new HSSFWorkbook( fin );
sheet = workbook.getSheetAt( 0 );
}
_functionFailureCount = 0;
_functionSuccessCount = 0;
_evaluationFailureCount = 0;
_evaluationSuccessCount = 0;
}
public void testFunctionsFromTestSpreadsheet() {
processFunctionGroup(SS.START_OPERATORS_ROW_INDEX, null);
processFunctionGroup(SS.START_FUNCTIONS_ROW_INDEX, null);
// example for debugging individual functions/operators:
// processFunctionGroup(SS.START_OPERATORS_ROW_INDEX, "ConcatEval");
// processFunctionGroup(SS.START_FUNCTIONS_ROW_INDEX, "AVERAGE");
// confirm results
String successMsg = "There were "
+ _evaluationSuccessCount + " successful evaluation(s) and "
+ _functionSuccessCount + " function(s) without error";
if(_functionFailureCount > 0) {
String msg = _functionFailureCount + " function(s) failed in "
+ _evaluationFailureCount + " evaluation(s). " + successMsg;
throw new AssertionFailedError(msg);
}
throw new AssertionFailedError(msg);
}
if(false) { // normally no output for successful tests
System.out.println(getClass().getName() + ": " + successMsg);
}
}

/**
* @param startRowIndex row index in the spreadsheet where the first function/operator is found
* @param testFocusFunctionName name of a single function/operator to test alone.
* Typically pass <code>null</code> to test all functions
*/
/**
* @param startRowIndex row index in the spreadsheet where the first function/operator is found
* @param testFocusFunctionName name of a single function/operator to test alone.
* Typically pass <code>null</code> to test all functions
*/
private void processFunctionGroup(int startRowIndex, String testFocusFunctionName) {
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, workbook);

int rowIndex = startRowIndex;
while (true) {
HSSFRow r = sheet.getRow(rowIndex);
String targetFunctionName = getTargetFunctionName(r);
if(targetFunctionName == null) {
throw new AssertionFailedError("Test spreadsheet cell empty on row ("
+ (rowIndex+1) + "). Expected function name or '"
+ SS.FUNCTION_NAMES_END_SENTINEL + "'");
}
if(targetFunctionName.equals(SS.FUNCTION_NAMES_END_SENTINEL)) {
// found end of functions list
break;
}
if(testFocusFunctionName == null || targetFunctionName.equalsIgnoreCase(testFocusFunctionName)) {
// expected results are on the row below
HSSFRow expectedValuesRow = sheet.getRow(rowIndex + 1);
if(expectedValuesRow == null) {
int missingRowNum = rowIndex + 2; //+1 for 1-based, +1 for next row
throw new AssertionFailedError("Missing expected values row for function '"
+ targetFunctionName + " (row " + missingRowNum + ")");
}
switch(processFunctionRow(evaluator, targetFunctionName, r, expectedValuesRow)) {
case Result.ALL_EVALUATIONS_SUCCEEDED: _functionSuccessCount++; break;
case Result.SOME_EVALUATIONS_FAILED: _functionFailureCount++; break;
default:
throw new RuntimeException("unexpected result");
case Result.NO_EVALUATIONS_FOUND: // do nothing
}
}
rowIndex += SS.NUMBER_OF_ROWS_PER_FUNCTION;
}
int rowIndex = startRowIndex;
while (true) {
HSSFRow r = sheet.getRow(rowIndex);
String targetFunctionName = getTargetFunctionName(r);
if(targetFunctionName == null) {
throw new AssertionFailedError("Test spreadsheet cell empty on row ("
+ (rowIndex+1) + "). Expected function name or '"
+ SS.FUNCTION_NAMES_END_SENTINEL + "'");
}
if(targetFunctionName.equals(SS.FUNCTION_NAMES_END_SENTINEL)) {
// found end of functions list
break;
}
if(testFocusFunctionName == null || targetFunctionName.equalsIgnoreCase(testFocusFunctionName)) {
// expected results are on the row below
HSSFRow expectedValuesRow = sheet.getRow(rowIndex + 1);
if(expectedValuesRow == null) {
int missingRowNum = rowIndex + 2; //+1 for 1-based, +1 for next row
throw new AssertionFailedError("Missing expected values row for function '"
+ targetFunctionName + " (row " + missingRowNum + ")");
}
switch(processFunctionRow(evaluator, targetFunctionName, r, expectedValuesRow)) {
case Result.ALL_EVALUATIONS_SUCCEEDED: _functionSuccessCount++; break;
case Result.SOME_EVALUATIONS_FAILED: _functionFailureCount++; break;
default:
throw new RuntimeException("unexpected result");
case Result.NO_EVALUATIONS_FOUND: // do nothing
}
}
rowIndex += SS.NUMBER_OF_ROWS_PER_FUNCTION;
}
}

/**
@@ -236,16 +235,16 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
* @return a constant from the local Result class denoting whether there were any evaluation
* cases, and whether they all succeeded.
*/
private int processFunctionRow(HSSFFormulaEvaluator evaluator, String targetFunctionName,
HSSFRow formulasRow, HSSFRow expectedValuesRow) {
int result = Result.NO_EVALUATIONS_FOUND; // so far
short endcolnum = formulasRow.getLastCellNum();
evaluator.setCurrentRow(formulasRow);
private int processFunctionRow(HSSFFormulaEvaluator evaluator, String targetFunctionName,
HSSFRow formulasRow, HSSFRow expectedValuesRow) {
int result = Result.NO_EVALUATIONS_FOUND; // so far
short endcolnum = formulasRow.getLastCellNum();
evaluator.setCurrentRow(formulasRow);

// iterate across the row for all the evaluation cases
for (short colnum=SS.COLUMN_INDEX_FIRST_TEST_VALUE; colnum < endcolnum; colnum++) {
HSSFCell c = formulasRow.getCell(colnum);
// iterate across the row for all the evaluation cases
for (short colnum=SS.COLUMN_INDEX_FIRST_TEST_VALUE; colnum < endcolnum; colnum++) {
HSSFCell c = formulasRow.getCell(colnum);
if (c == null || c.getCellType() != HSSFCell.CELL_TYPE_FORMULA) {
continue;
}
@@ -265,13 +264,13 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
printShortStackTrace(System.err, e);
result = Result.SOME_EVALUATIONS_FAILED;
}
}
}
return result;
}

/**
* Useful to keep output concise when expecting many failures to be reported by this test case
*/
/**
* Useful to keep output concise when expecting many failures to be reported by this test case
*/
private static void printShortStackTrace(PrintStream ps, AssertionFailedError e) {
StackTraceElement[] stes = e.getStackTrace();
@@ -304,8 +303,8 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
}

/**
* @return <code>null</code> if cell is missing, empty or blank
*/
* @return <code>null</code> if cell is missing, empty or blank
*/
private static String getTargetFunctionName(HSSFRow r) {
if(r == null) {
System.err.println("Warning - given null row, can't figure out function name");

+ 82
- 0
src/scratchpad/testcases/org/apache/poi/hssf/record/formula/eval/TestPercentEval.java ファイルの表示

@@ -0,0 +1,82 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */

package org.apache.poi.hssf.record.formula.eval;

import junit.framework.AssertionFailedError;
import junit.framework.TestCase;

import org.apache.poi.hssf.record.formula.PercentPtg;
import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;

/**
* Test for percent operator evaluator.
*
* @author Josh Micich
*/
public final class TestPercentEval extends TestCase {
private static void confirm(ValueEval arg, double expectedResult) {
Eval[] args = {
arg,
};
PercentEval opEval = new PercentEval(new PercentPtg());
double result = NumericFunctionInvoker.invoke(opEval, args, -1, (short)-1);
assertEquals(expectedResult, result, 0);
}

public void testBasic() {
confirm(new NumberEval(5), 0.05);
confirm(new NumberEval(3000), 30.0);
confirm(new NumberEval(-150), -1.5);
confirm(new StringEval("0.2"), 0.002);
confirm(BoolEval.TRUE, 0.01);
}

public void testInSpreadSheet() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1");
HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell((short)0);
cell.setCellFormula("B1%");
row.createCell((short)1).setCellValue(50.0);
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
fe.setCurrentRow(row);
CellValue cv;
try {
cv = fe.evaluate(cell);
} catch (RuntimeException e) {
if(e.getCause() instanceof NullPointerException) {
throw new AssertionFailedError("Identified bug 44608");
}
// else some other unexpected error
throw e;
}
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
assertEquals(0.5, cv.getNumberValue(), 0.0);
}

}

バイナリ
src/scratchpad/testcases/org/apache/poi/hwpf/data/Bug28627.doc ファイルの表示


+ 48
- 14
src/scratchpad/testcases/org/apache/poi/hwpf/usermodel/TestProblems.java ファイルの表示

@@ -16,19 +16,14 @@
*/
package org.apache.poi.hwpf.usermodel;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Iterator;
import java.util.List;
import java.io.FileOutputStream;

import junit.framework.TestCase;

import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.model.StyleSheet;
import org.apache.poi.hwpf.model.TextPiece;
import org.apache.poi.hwpf.usermodel.Paragraph;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.util.LittleEndian;

import junit.framework.TestCase;

/**
* Test various problem documents
@@ -36,16 +31,18 @@ import junit.framework.TestCase;
* @author Nick Burch (nick at torchbox dot com)
*/
public class TestProblems extends TestCase {

private String dirname = System.getProperty("HWPF.testdata.path");
protected void setUp() throws Exception {
}
}
/**
* ListEntry passed no ListTable
*/
public void testListEntryNoListTable() throws Exception {
HWPFDocument doc = new HWPFDocument(new FileInputStream(dirname + "/ListEntryNoListTable.doc"));
HWPFDocument doc = new HWPFDocument(new FileInputStream(
new File(dirname, "ListEntryNoListTable.doc")));
Range r = doc.getRange();
StyleSheet styleSheet = doc.getStyleSheet();
@@ -62,7 +59,8 @@ public class TestProblems extends TestCase {
* AIOOB for TableSprmUncompressor.unCompressTAPOperation
*/
public void testSprmAIOOB() throws Exception {
HWPFDocument doc = new HWPFDocument(new FileInputStream(dirname + "/AIOOB-Tap.doc"));
HWPFDocument doc = new HWPFDocument(new FileInputStream(
new File(dirname, "AIOOB-Tap.doc")));
Range r = doc.getRange();
StyleSheet styleSheet = doc.getStyleSheet();
@@ -79,7 +77,8 @@ public class TestProblems extends TestCase {
* Test for TableCell not skipping the last paragraph
*/
public void testTableCellLastParagraph() throws Exception {
HWPFDocument doc = new HWPFDocument(new FileInputStream(dirname + "/Bug44292.doc"));
HWPFDocument doc = new HWPFDocument(new FileInputStream(
new File(dirname, "Bug44292.doc")));
Range r = doc.getRange();
//get the table
@@ -104,4 +103,39 @@ public class TestProblems extends TestCase {
// Last cell should have one paragraph
assertEquals(1, cell.numParagraphs());
}

public void testRangeDelete() throws Exception {
HWPFDocument doc = new HWPFDocument(new FileInputStream(
new File(dirname, "Bug28627.doc")));

Range range = doc.getRange();
int numParagraphs = range.numParagraphs();
int totalLength = 0, deletedLength = 0;
for (int i = 0; i < numParagraphs; i++) {
Paragraph para = range.getParagraph(i);
String text = para.text();

totalLength += text.length();
if (text.indexOf("{delete me}") > -1) {
para.delete();
deletedLength = text.length();
}
}
// check the text length after deletion
int newLength = 0;
range = doc.getRange();
numParagraphs = range.numParagraphs();
for (int i = 0; i < numParagraphs; i++) {
Paragraph para = range.getParagraph(i);
String text = para.text();

newLength += text.length();
}
assertEquals(newLength, totalLength - deletedLength);
}
}

バイナリ
src/testcases/org/apache/poi/hssf/data/27349-vlookupAcrossSheets.xls ファイルの表示


バイナリ
src/testcases/org/apache/poi/hssf/data/Bug44593.xls ファイルの表示


バイナリ
src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls ファイルの表示


+ 44
- 0
src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java ファイルの表示

@@ -844,4 +844,48 @@ public final class TestFormulaParser extends TestCase {
}
assertEquals("SUM(A32769:A32770)", cell.getCellFormula());
}

public void testSpaceAtStartOfFormula() {
// Simulating cell formula of "= 4" (note space)
// The same Ptg array can be observed if an excel file is saved with that exact formula

AttrPtg spacePtg = AttrPtg.createSpace(AttrPtg.SpaceType.SPACE_BEFORE, 1);
Ptg[] ptgs = { spacePtg, new IntPtg(4), };
String formulaString;
try {
formulaString = FormulaParser.toFormulaString(null, ptgs);
} catch (IllegalStateException e) {
if(e.getMessage().equalsIgnoreCase("too much stuff left on the stack")) {
throw new AssertionFailedError("Identified bug 44609");
}
// else some unexpected error
throw e;
}
// FormulaParser strips spaces anyway
assertEquals("4", formulaString);

ptgs = new Ptg[] { new IntPtg(3), spacePtg, new IntPtg(4), spacePtg, new AddPtg()};
formulaString = FormulaParser.toFormulaString(null, ptgs);
assertEquals("3+4", formulaString);
}

/**
* Checks some internal error detecting logic ('stack underflow error' in toFormulaString)
*/
public void testTooFewOperandArgs() {
// Simulating badly encoded cell formula of "=/1"
// Not sure if Excel could ever produce this
Ptg[] ptgs = {
// Excel would probably have put tMissArg here
new IntPtg(1),
new DividePtg(),
};
try {
FormulaParser.toFormulaString(null, ptgs);
fail("Expected exception was not thrown");
} catch (IllegalStateException e) {
// expected during successful test
assertTrue(e.getMessage().startsWith("Too few arguments suppled to operation token"));
}
}
}

+ 41
- 0
src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java ファイルの表示

@@ -508,6 +508,30 @@ extends TestCase {
}
assertTrue("No Exceptions till here!", true);
}

public void test28031() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
wb.setSheetName(0, "Sheet1");

HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell((short)0);
String formulaText =
"IF(ROUND(A2*B2*C2,2)>ROUND(B2*D2,2),ROUND(A2*B2*C2,2),ROUND(B2*D2,2))";
cell.setCellFormula(formulaText);

assertEquals(formulaText, cell.getCellFormula());
if(false) {
// this file can be inspected manually
try {
OutputStream os = new FileOutputStream("/tmp/output28031.xls");
wb.write(os);
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public void test33082() throws java.io.IOException {
String filename = System.getProperty("HSSF.testdata.path");
@@ -1127,6 +1151,23 @@ extends TestCase {
in.close();
assertFalse(wb.isWriteProtected());
}
/**
* Some files were having problems with the DVRecord,
* probably due to dropdowns
*/
public void test44593() throws Exception {
FileInputStream in = new FileInputStream(new File(cwd, "Bug44593.xls"));
// Used to blow up with an IllegalArgumentException
// when creating a DVRecord
// Now won't, but no idea if this means we have
// rubbish in the DVRecord or not...
HSSFWorkbook wb = new HSSFWorkbook(in);
in.close();
assertEquals(2, wb.getNumberOfSheets());
}
}



+ 13
- 10
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFCell.java ファイルの表示

@@ -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,7 +14,6 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */

package org.apache.poi.hssf.usermodel;

@@ -24,12 +22,11 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import junit.framework.AssertionFailedError;
import junit.framework.TestCase;

import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.record.HyperlinkRecord;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.TempFile;
@@ -41,12 +38,7 @@ import org.apache.poi.util.TempFile;
* @author Dan Sherman (dsherman at isisph.com)
* @author Alex Jacoby (ajacoby at gmail.com)
*/

public class TestHSSFCell
extends TestCase {
public TestHSSFCell(String s) {
super(s);
}
public final class TestHSSFCell extends TestCase {

/**
* test that Boolean and Error types (BoolErrRecord) are supported properly.
@@ -388,6 +380,17 @@ extends TestCase {
assertEquals("Formula", "A1+B1", c.toString());
}
public void testSetStringInFormulaCell_bug44606() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFCell cell = wb.createSheet("Sheet1").createRow(0).createCell((short)0);
cell.setCellFormula("B1&C1");
try {
cell.setCellValue(new HSSFRichTextString("hello"));
} catch (ClassCastException e) {
throw new AssertionFailedError("Identified bug 44606");
}
}
public static void main(String [] args) {
System.out
.println("Testing org.apache.poi.hssf.usermodel.TestHSSFCell");

+ 52
- 0
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFTextbox.java ファイルの表示

@@ -0,0 +1,52 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.poi.hssf.usermodel;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import junit.framework.TestCase;
/**
* Test <code>HSSFTextbox</code>.
*
* @author Yegor Kozlov (yegor at apache.org)
*/
public final class TestHSSFTextbox extends TestCase{
/**
* Test that accessors to horizontal and vertical alignment work properly
*/
public void testAlignment() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sh1 = wb.createSheet();
HSSFPatriarch patriarch = sh1.createDrawingPatriarch();
HSSFTextbox textbox = patriarch.createTextbox(new HSSFClientAnchor(0, 0, 0, 0, (short) 1, 1, (short) 6, 4));
HSSFRichTextString str = new HSSFRichTextString("Hello, World");
textbox.setString(str);
textbox.setHorizontalAlignment(HSSFTextbox.HORIZONTAL_ALIGNMENT_CENTERED);
textbox.setVerticalAlignment(HSSFTextbox.VERTICAL_ALIGNMENT_CENTER);
assertEquals(HSSFTextbox.HORIZONTAL_ALIGNMENT_CENTERED, textbox.getHorizontalAlignment());
assertEquals(HSSFTextbox.VERTICAL_ALIGNMENT_CENTER, textbox.getVerticalAlignment());
}
}

+ 2
- 1
src/testcases/org/apache/poi/poifs/property/TestPropertyTable.java ファイルの表示

@@ -25,6 +25,7 @@ import java.util.*;

import junit.framework.*;

import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.poifs.storage.BlockAllocationTableReader;
import org.apache.poi.poifs.storage.RawDataBlockList;

@@ -2598,7 +2599,7 @@ public class TestPropertyTable
( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF
};
RawDataBlockList data_blocks =
new RawDataBlockList(new ByteArrayInputStream(raw_data_array));
new RawDataBlockList(new ByteArrayInputStream(raw_data_array), POIFSConstants.BIG_BLOCK_SIZE);
int[] bat_array =
{
15

+ 3
- 1
src/testcases/org/apache/poi/poifs/storage/LocalRawDataBlockList.java ファイルの表示

@@ -19,6 +19,8 @@

package org.apache.poi.poifs.storage;

import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts;

@@ -47,7 +49,7 @@ public class LocalRawDataBlockList
public LocalRawDataBlockList()
throws IOException
{
super(new ByteArrayInputStream(new byte[ 0 ]));
super(new ByteArrayInputStream(new byte[ 0 ]), POIFSConstants.BIG_BLOCK_SIZE);
_list = new ArrayList();
_array = null;
}

+ 4
- 3
src/testcases/org/apache/poi/poifs/storage/TestRawDataBlockList.java ファイルの表示

@@ -21,6 +21,7 @@ package org.apache.poi.poifs.storage;

import java.io.*;

import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.util.DummyPOILogger;
import org.apache.poi.util.POILogFactory;

@@ -69,7 +70,7 @@ public class TestRawDataBlockList
{
data[ j ] = ( byte ) j;
}
new RawDataBlockList(new ByteArrayInputStream(data));
new RawDataBlockList(new ByteArrayInputStream(data), POIFSConstants.BIG_BLOCK_SIZE);
}

/**
@@ -81,7 +82,7 @@ public class TestRawDataBlockList
public void testEmptyConstructor()
throws IOException
{
new RawDataBlockList(new ByteArrayInputStream(new byte[ 0 ]));
new RawDataBlockList(new ByteArrayInputStream(new byte[ 0 ]), POIFSConstants.BIG_BLOCK_SIZE);
}

/**
@@ -108,7 +109,7 @@ public class TestRawDataBlockList

// Check we logged the error
logger.reset();
new RawDataBlockList(new ByteArrayInputStream(data));
new RawDataBlockList(new ByteArrayInputStream(data), POIFSConstants.BIG_BLOCK_SIZE);
assertEquals(1, logger.logged.size());
}
}

+ 2
- 1
src/testcases/org/apache/poi/poifs/storage/TestSmallBlockTableReader.java ファイルの表示

@@ -25,6 +25,7 @@ import java.util.*;

import junit.framework.*;

import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.poifs.property.PropertyTable;
import org.apache.poi.poifs.property.RootProperty;

@@ -2112,7 +2113,7 @@ public class TestSmallBlockTableReader
( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF
};
RawDataBlockList data_blocks =
new RawDataBlockList(new ByteArrayInputStream(raw_data_array));
new RawDataBlockList(new ByteArrayInputStream(raw_data_array), POIFSConstants.BIG_BLOCK_SIZE);
int[] bat_array =
{
15

読み込み中…
キャンセル
保存