<!-- 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>
<!-- 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>
}
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) {
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();
}
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;
* @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() {
}
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)
{
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)
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) {
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.
*
{
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("");
{
super( parent, anchor );
setShapeType(OBJECT_TYPE_TEXT);
+
+ halign = HORIZONTAL_ALIGNMENT_LEFT;
+ valign = VERTICAL_ALIGNMENT_TOP;
}
/**
{
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;
+ }
}
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;
/**
public class HSSFCellRangeAddress
{
+ private static POILogger logger = POILogFactory.getLogger(HSSFCellRangeAddress.class);
+
/**
* Number of following ADDR structures
*/
{
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);
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;
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
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;
{
private static final Log _logger = LogFactory.getLog(POIFSFileSystem.class);
-
private static final class CloseIgnoringInputStream extends InputStream {
private final InputStream _is;
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();
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);
// 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);
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
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;
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
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;
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
_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();
{
return _xbat_start.get();
}
+
+ /**
+ * @return The Big Block size, normally 512 bytes, sometimes 4096 bytes
+ */
+ public int getBigBlockSize() {
+ return bigBlockSize;
+ }
} // end public class HeaderBlockReader
* 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())
{
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.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();
+ }
+}
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;
/**
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 };
// 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
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);
-
}
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];
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,
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.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;
+ }
+}
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()
int numSections = _sections.size();
int numRuns = _characters.size();
int numParagraphs = _paragraphs.size();
+ int numTextPieces = _text.size();
for (int x = _charStart; x < numRuns; x++)
{
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);
+ }
}
/**
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;
}
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.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);
+ }
+}
* limitations under the License.
*/
-
package org.apache.poi.hssf.record.formula.eval;
import java.io.FileInputStream;
* 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
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;
+ }
}
/**
* @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;
}
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();
}
/**
- * @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");
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hssf.record.formula.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);
+ }
+
+}
*/
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
* @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();
* 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();
* 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
// 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);
+ }
}
}
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"));
+ }
+ }
}
}
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");
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());
+ }
}
-
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
-
package org.apache.poi.hssf.usermodel;
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;
* @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.
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");
--- /dev/null
+/*\r
+* Licensed to the Apache Software Foundation (ASF) under one or more\r
+* contributor license agreements. See the NOTICE file distributed with\r
+* this work for additional information regarding copyright ownership.\r
+* The ASF licenses this file to You under the Apache License, Version 2.0\r
+* (the "License"); you may not use this file except in compliance with\r
+* the License. You may obtain a copy of the License at\r
+*\r
+* http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+*/\r
+package org.apache.poi.hssf.usermodel;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+\r
+import junit.framework.TestCase;\r
+\r
+/**\r
+ * Test <code>HSSFTextbox</code>.\r
+ *\r
+ * @author Yegor Kozlov (yegor at apache.org)\r
+ */\r
+public final class TestHSSFTextbox extends TestCase{\r
+\r
+ /**\r
+ * Test that accessors to horizontal and vertical alignment work properly\r
+ */\r
+ public void testAlignment() {\r
+ HSSFWorkbook wb = new HSSFWorkbook();\r
+ HSSFSheet sh1 = wb.createSheet();\r
+ HSSFPatriarch patriarch = sh1.createDrawingPatriarch();\r
+\r
+ HSSFTextbox textbox = patriarch.createTextbox(new HSSFClientAnchor(0, 0, 0, 0, (short) 1, 1, (short) 6, 4));\r
+ HSSFRichTextString str = new HSSFRichTextString("Hello, World");\r
+ textbox.setString(str);\r
+ textbox.setHorizontalAlignment(HSSFTextbox.HORIZONTAL_ALIGNMENT_CENTERED);\r
+ textbox.setVerticalAlignment(HSSFTextbox.VERTICAL_ALIGNMENT_CENTER);\r
+\r
+ assertEquals(HSSFTextbox.HORIZONTAL_ALIGNMENT_CENTERED, textbox.getHorizontalAlignment());\r
+ assertEquals(HSSFTextbox.VERTICAL_ALIGNMENT_CENTER, textbox.getVerticalAlignment());\r
+ }\r
+\r
+ }\r
import junit.framework.*;
+import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.poifs.storage.BlockAllocationTableReader;
import org.apache.poi.poifs.storage.RawDataBlockList;
( 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
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;
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;
}
import java.io.*;
+import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.util.DummyPOILogger;
import org.apache.poi.util.POILogFactory;
{
data[ j ] = ( byte ) j;
}
- new RawDataBlockList(new ByteArrayInputStream(data));
+ new RawDataBlockList(new ByteArrayInputStream(data), POIFSConstants.BIG_BLOCK_SIZE);
}
/**
public void testEmptyConstructor()
throws IOException
{
- new RawDataBlockList(new ByteArrayInputStream(new byte[ 0 ]));
+ new RawDataBlockList(new ByteArrayInputStream(new byte[ 0 ]), POIFSConstants.BIG_BLOCK_SIZE);
}
/**
// 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());
}
}
import junit.framework.*;
+import org.apache.poi.poifs.common.POIFSConstants;
import org.apache.poi.poifs.property.PropertyTable;
import org.apache.poi.poifs.property.RootProperty;
( 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