<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.1.1-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">45720 Fixed HSSFWorkbook.cloneSheet to correctly clone sheets with drawings</action>
+ <action dev="POI-DEVELOPERS" type="fix">45728 Fix for SlideShow.reorderSlide in HSLF</action>
<action dev="POI-DEVELOPERS" type="add">Initial support for embedded movies and controls in HSLF</action>
<action dev="POI-DEVELOPERS" type="fix">45358 - signed/unsigned error when parsing 3-d area refs, performance problem evaluating area refs, and ClassCastExcecption in IF()</action>
<action dev="POI-DEVELOPERS" type="add">Support for HPBF Publisher hyperlinks, including during text extraction</action>
<action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
</release>
<release version="3.1.1-alpha1" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">45720 Fixed HSSFWorkbook.cloneSheet to correctly clone sheets with drawings</action>
+ <action dev="POI-DEVELOPERS" type="fix">45728 Fix for SlideShow.reorderSlide in HSLF</action>
<action dev="POI-DEVELOPERS" type="add">Initial support for embedded movies and controls in HSLF</action>
<action dev="POI-DEVELOPERS" type="fix">45358 - signed/unsigned error when parsing 3-d area refs, performance problem evaluating area refs, and ClassCastExcecption in IF()</action>
<action dev="POI-DEVELOPERS" type="add">Support for HPBF Publisher hyperlinks, including during text extraction</action>
return null;
}
+ /**
+ * Recursively find records with the specified record ID
+ *
+ * @param out - list to store found records
+ */
+ public void getRecordsById(short recordId, List out){
+ for(Iterator it = childRecords.iterator(); it.hasNext();) {
+ Object er = it.next();
+ if(er instanceof EscherContainerRecord) {
+ EscherContainerRecord c = (EscherContainerRecord)er;
+ c.getRecordsById(recordId, out );
+ } else if (er instanceof EscherSpRecord){
+ out.add(er);
+ }
+ }
+ }
+
}
* @return a new shape id.
*/
public int allocateShapeId(short drawingGroupId)
+ {
+ EscherDgRecord dg = getDrawingGroup(drawingGroupId);
+ return allocateShapeId(drawingGroupId, dg);
+ }
+
+ /**
+ * Allocates new shape id for the new drawing group id.
+ *
+ * @return a new shape id.
+ */
+ public int allocateShapeId(short drawingGroupId, EscherDgRecord dg)
{
dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 );
{
int result = c.getNumShapeIdsUsed() + (1024 * (i+1));
c.incrementShapeId();
- EscherDgRecord dg = getDrawingGroup(drawingGroupId);
dg.setNumShapes( dg.getNumShapes() + 1 );
dg.setLastMSOSPID( result );
if (result >= dgg.getShapeIdMax())
// Create new cluster
dgg.addCluster( drawingGroupId, 0 );
dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId();
- EscherDgRecord dg = getDrawingGroup(drawingGroupId);
dg.setNumShapes( dg.getNumShapes() + 1 );
int result = (1024 * dgg.getFileIdClusters().length);
dg.setLastMSOSPID( result );
dgg.setShapeIdMax( result + 1 );
return result;
}
-
//////////// Non-public methods /////////////
/**
import java.util.List;
import java.util.Locale;
-import org.apache.poi.ddf.EscherBSERecord;
-import org.apache.poi.ddf.EscherBoolProperty;
-import org.apache.poi.ddf.EscherContainerRecord;
-import org.apache.poi.ddf.EscherDggRecord;
-import org.apache.poi.ddf.EscherOptRecord;
-import org.apache.poi.ddf.EscherProperties;
-import org.apache.poi.ddf.EscherRGBProperty;
-import org.apache.poi.ddf.EscherRecord;
-import org.apache.poi.ddf.EscherSplitMenuColorsRecord;
-import org.apache.poi.hssf.record.BOFRecord;
-import org.apache.poi.hssf.record.BackupRecord;
-import org.apache.poi.hssf.record.BookBoolRecord;
-import org.apache.poi.hssf.record.BoundSheetRecord;
-import org.apache.poi.hssf.record.CodepageRecord;
-import org.apache.poi.hssf.record.CountryRecord;
-import org.apache.poi.hssf.record.DSFRecord;
-import org.apache.poi.hssf.record.DateWindow1904Record;
-import org.apache.poi.hssf.record.DrawingGroupRecord;
-import org.apache.poi.hssf.record.EOFRecord;
-import org.apache.poi.hssf.record.ExtSSTRecord;
-import org.apache.poi.hssf.record.ExtendedFormatRecord;
-import org.apache.poi.hssf.record.ExternSheetRecord;
-import org.apache.poi.hssf.record.FileSharingRecord;
-import org.apache.poi.hssf.record.FnGroupCountRecord;
-import org.apache.poi.hssf.record.FontRecord;
-import org.apache.poi.hssf.record.FormatRecord;
-import org.apache.poi.hssf.record.HideObjRecord;
-import org.apache.poi.hssf.record.HyperlinkRecord;
-import org.apache.poi.hssf.record.InterfaceEndRecord;
-import org.apache.poi.hssf.record.InterfaceHdrRecord;
-import org.apache.poi.hssf.record.MMSRecord;
-import org.apache.poi.hssf.record.NameRecord;
-import org.apache.poi.hssf.record.PaletteRecord;
-import org.apache.poi.hssf.record.PasswordRecord;
-import org.apache.poi.hssf.record.PasswordRev4Record;
-import org.apache.poi.hssf.record.PrecisionRecord;
-import org.apache.poi.hssf.record.ProtectRecord;
-import org.apache.poi.hssf.record.ProtectionRev4Record;
-import org.apache.poi.hssf.record.RecalcIdRecord;
-import org.apache.poi.hssf.record.Record;
-import org.apache.poi.hssf.record.RefreshAllRecord;
-import org.apache.poi.hssf.record.SSTRecord;
-import org.apache.poi.hssf.record.StyleRecord;
-import org.apache.poi.hssf.record.SupBookRecord;
-import org.apache.poi.hssf.record.TabIdRecord;
-import org.apache.poi.hssf.record.UnicodeString;
-import org.apache.poi.hssf.record.UseSelFSRecord;
-import org.apache.poi.hssf.record.WindowOneRecord;
-import org.apache.poi.hssf.record.WindowProtectRecord;
-import org.apache.poi.hssf.record.WriteAccessRecord;
-import org.apache.poi.hssf.record.WriteProtectRecord;
+import org.apache.poi.ddf.*;
+import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.record.formula.NameXPtg;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.hssf.util.SheetReferences;
// contains a EscherDggRecord
for(Iterator rit = records.iterator(); rit.hasNext();) {
Record r = (Record)rit.next();
-
+
if(r instanceof DrawingGroupRecord) {
DrawingGroupRecord dg = (DrawingGroupRecord)r;
dg.processChildRecords();
-
+
EscherContainerRecord cr =
dg.getEscherContainer();
if(cr == null) {
continue;
}
-
+
EscherDggRecord dgg = null;
for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) {
Object er = it.next();
dgg = (EscherDggRecord)er;
}
}
-
+
if(dgg != null) {
drawingManager = new DrawingManager2(dgg);
return;
// Look for the DrawingGroup record
int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid);
-
+
// If there is one, does it have a EscherDggRecord?
if(dgLoc != -1) {
DrawingGroupRecord dg =
dgg = (EscherDggRecord)er;
}
}
-
+
if(dgg != null) {
drawingManager = new DrawingManager2(dgg);
}
public NameXPtg getNameXPtg(String name) {
return getOrCreateLinkTable().getNameXPtg(name);
}
+
+ /**
+ * Check if the cloned sheet has drawings. If yes, then allocate a new drawing group ID and
+ * re-generate shape IDs
+ *
+ * @param sheet the cloned sheet
+ */
+ public void cloneDrawings(Sheet sheet){
+
+ findDrawingGroup();
+
+ if(drawingManager == null) {
+ //this workbook does not have drawings
+ return;
+ }
+
+ //check if the cloned sheet has drawings
+ int aggLoc = sheet.aggregateDrawingRecords(drawingManager, false);
+ if(aggLoc != -1) {
+ EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid);
+
+ EscherDggRecord dgg = drawingManager.getDgg();
+
+ //register a new drawing group for the cloned sheet
+ int dgId = drawingManager.findNewDrawingGroupId();
+ dgg.addCluster( dgId, 0 );
+ dgg.setDrawingsSaved(dgg.getDrawingsSaved() + 1);
+
+ EscherDgRecord dg = null;
+ for(Iterator it = agg.getEscherContainer().getChildRecords().iterator(); it.hasNext();) {
+ Object er = it.next();
+ if(er instanceof EscherDgRecord) {
+ dg = (EscherDgRecord)er;
+ //update id of the drawing in the cloned sheet
+ dg.setOptions( (short) ( dgId << 4 ) );
+ } else if (er instanceof EscherContainerRecord){
+ //recursively find shape records and re-generate shapeId
+ ArrayList spRecords = new ArrayList();
+ EscherContainerRecord cp = (EscherContainerRecord)er;
+ cp.getRecordsById(EscherSpRecord.RECORD_ID, spRecords);
+ for(Iterator spIt = spRecords.iterator(); spIt.hasNext();) {
+ EscherSpRecord sp = (EscherSpRecord)spIt.next();
+ int shapeId = drawingManager.allocateShapeId((short)dgId, dg);
+ sp.setShapeId(shapeId);
+ }
+ }
+ }
+
+ }
+ }
}
package org.apache.poi.hssf.record;
-import java.util.Iterator;
+import java.util.ArrayList;
import java.util.List;
-import java.util.Stack;
import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.record.formula.Area3DPtg;
*/
public final class NameRecord extends Record {
public final static short sid = 0x0018;
- /**Included for completeness sake, not implemented
- */
- public final static byte BUILTIN_CONSOLIDATE_AREA = (byte)1;
-
- /**Included for completeness sake, not implemented
- */
- public final static byte BUILTIN_AUTO_OPEN = (byte)2;
-
- /**Included for completeness sake, not implemented
- */
- public final static byte BUILTIN_AUTO_CLOSE = (byte)3;
-
- /**Included for completeness sake, not implemented
- */
- public final static byte BUILTIN_DATABASE = (byte)4;
-
- /**Included for completeness sake, not implemented
- */
- public final static byte BUILTIN_CRITERIA = (byte)5;
-
- public final static byte BUILTIN_PRINT_AREA = (byte)6;
- public final static byte BUILTIN_PRINT_TITLE = (byte)7;
-
- /**Included for completeness sake, not implemented
- */
- public final static byte BUILTIN_RECORDER = (byte)8;
-
- /**Included for completeness sake, not implemented
- */
- public final static byte BUILTIN_DATA_FORM = (byte)9;
-
- /**Included for completeness sake, not implemented
- */
-
- public final static byte BUILTIN_AUTO_ACTIVATE = (byte)10;
-
- /**Included for completeness sake, not implemented
- */
+ /**Included for completeness sake, not implemented */
+ public final static byte BUILTIN_CONSOLIDATE_AREA = 1;
+ /**Included for completeness sake, not implemented */
+ public final static byte BUILTIN_AUTO_OPEN = 2;
+ /**Included for completeness sake, not implemented */
+ public final static byte BUILTIN_AUTO_CLOSE = 3;
+ /**Included for completeness sake, not implemented */
+ public final static byte BUILTIN_DATABASE = 4;
+ /**Included for completeness sake, not implemented */
+ public final static byte BUILTIN_CRITERIA = 5;
+
+ public final static byte BUILTIN_PRINT_AREA = 6;
+ public final static byte BUILTIN_PRINT_TITLE = 7;
+
+ /**Included for completeness sake, not implemented */
+ public final static byte BUILTIN_RECORDER = 8;
+ /**Included for completeness sake, not implemented */
+ public final static byte BUILTIN_DATA_FORM = 9;
+ /**Included for completeness sake, not implemented */
+ public final static byte BUILTIN_AUTO_ACTIVATE = 10;
+ /**Included for completeness sake, not implemented */
+ public final static byte BUILTIN_AUTO_DEACTIVATE = 11;
+ /**Included for completeness sake, not implemented */
+ public final static byte BUILTIN_SHEET_TITLE = 12;
+
+ public final static byte BUILTIN_FILTER_DB = 13;
- public final static byte BUILTIN_AUTO_DEACTIVATE = (byte)11;
-
- /**Included for completeness sake, not implemented
- */
- public final static byte BUILTIN_SHEET_TITLE = (byte)12;
-
private static final class Option {
public static final int OPT_HIDDEN_NAME = 0x0001;
public static final int OPT_FUNCTION_NAME = 0x0002;
public static final int OPT_BUILTIN = 0x0020;
public static final int OPT_BINDATA = 0x1000;
}
-
+
private short field_1_option_flag;
private byte field_2_keyboard_shortcut;
- private byte field_3_length_name_text;
- private short field_4_length_name_definition;
private short field_5_index_to_sheet; // unused: see field_6
/** the one based sheet number. Zero if this is a global name */
private int field_6_sheetNumber;
- private byte field_7_length_custom_menu;
- private byte field_8_length_description_text;
- private byte field_9_length_help_topic_text;
- private byte field_10_length_status_bar_text;
- private byte field_11_compressed_unicode_flag; // not documented
- private byte field_12_builtIn_name;
+ private boolean field_11_nameIsMultibyte;
+ private byte field_12_built_in_code;
private String field_12_name_text;
- private Stack field_13_name_definition;
+ private Ptg[] field_13_name_definition;
private String field_14_custom_menu_text;
private String field_15_description_text;
private String field_16_help_topic_text;
/** Creates new NameRecord */
public NameRecord() {
- field_13_name_definition = new Stack();
+ field_13_name_definition = Ptg.EMPTY_PTG_ARRAY;
- field_12_name_text = new String();
- field_14_custom_menu_text = new String();
- field_15_description_text = new String();
- field_16_help_topic_text = new String();
- field_17_status_bar_text = new String();
+ field_12_name_text = "";
+ field_14_custom_menu_text = "";
+ field_15_description_text = "";
+ field_16_help_topic_text = "";
+ field_17_status_bar_text = "";
}
/**
*/
public NameRecord(byte builtin, int sheetNumber)
{
- this();
- this.field_12_builtIn_name = builtin;
- this.setOptionFlag((short)(this.field_1_option_flag | Option.OPT_BUILTIN));
- this.setNameTextLength((byte)1);
+ this();
+ field_12_built_in_code = builtin;
+ setOptionFlag((short)(field_1_option_flag | Option.OPT_BUILTIN));
field_6_sheetNumber = sheetNumber; //the extern sheets are set through references
-
- //clearing these because they are not used with builtin records
- this.setCustomMenuLength((byte)0);
- this.setDescriptionTextLength((byte)0);
- this.setHelpTopicLength((byte)0);
- this.setStatusBarLength((byte)0);
-
-
}
/** sets the option flag for the named range
field_2_keyboard_shortcut = shortcut;
}
- /** sets the name of the named range length
- * @param length name length
- */
- public void setNameTextLength(byte length){
- field_3_length_name_text = length;
- }
-
- /** sets the definition (reference - formula) length
- * @param length defenition length
- */
- public void setDefinitionTextLength(short length){
- field_4_length_name_definition = length;
- }
-
- /** sets the index number to the extern sheet (thats is what writen in documentation
- * but as i saw , it works differently)
- * @param index extern sheet index
- */
- public void setUnused(short index){
- field_5_index_to_sheet = index;
-
- // field_6_equals_to_index_to_sheet is equal to field_5_index_to_sheet
-// field_6_equals_to_index_to_sheet = index;
- }
-
/**
* For named ranges, and built-in names
- * @return the 1-based sheet number. Zero if this is a global name
+ * @return the 1-based sheet number. Zero if this is a global name
*/
public int getSheetNumber()
{
}
- /** sets the custom menu length
- * @param length custom menu length
- */
- public void setCustomMenuLength(byte length){
- field_7_length_custom_menu = length;
- }
-
- /** sets the length of named range description
- * @param length description length
- */
- public void setDescriptionTextLength(byte length){
- field_8_length_description_text = length;
- }
-
- /** sets the help topic length
- * @param length help topic length
- */
- public void setHelpTopicLength(byte length){
- field_9_length_help_topic_text = length;
- }
-
- /** sets the length of the status bar text
- * @param length status bar text length
- */
- public void setStatusBarLength(byte length){
- field_10_length_status_bar_text = length;
- }
-
- /** sets the compressed unicode flag
- * @param flag unicode flag
- */
- public void setCompressedUnicodeFlag(byte flag) {
- field_11_compressed_unicode_flag = flag;
- }
-
/** sets the name of the named range
* @param name named range name
*/
public void setNameText(String name){
field_12_name_text = name;
- setCompressedUnicodeFlag(
- StringUtil.hasMultibyte(name) ? (byte)1 : (byte)0
- );
+ field_11_nameIsMultibyte = StringUtil.hasMultibyte(name);
}
/** sets the custom menu text
return field_2_keyboard_shortcut ;
}
- /**
+ /**
* gets the name length, in characters
* @return name length
*/
- public byte getNameTextLength(){
- return field_3_length_name_text;
- }
-
- /**
- * gets the name length, in bytes
- * @return raw name length
- */
- public byte getRawNameTextLength(){
- if( (field_11_compressed_unicode_flag & 0x01) == 1 ) {
- return (byte)(2 * field_3_length_name_text);
+ private int getNameTextLength(){
+ if (isBuiltInName()) {
+ return 1;
}
- return field_3_length_name_text;
- }
-
- /** get the definition length
- * @return definition length
- */
- public short getDefinitionLength(){
- return field_4_length_name_definition;
- }
-
- /** gets the index to extern sheet
- * @return index to extern sheet
- */
- public short getUnused(){
- return field_5_index_to_sheet;
- }
-
- /** gets the custom menu length
- * @return custom menu length
- */
- public byte getCustomMenuLength(){
- return field_7_length_custom_menu;
- }
-
- /** gets the description text length
- * @return description text length
- */
- public byte getDescriptionTextLength(){
- return field_8_length_description_text;
- }
-
- /** gets the help topic length
- * @return help topic length
- */
- public byte getHelpTopicLength(){
- return field_9_length_help_topic_text;
- }
-
- /** get the status bar text length
- * @return satus bar length
- */
- public byte getStatusBarLength(){
- return field_10_length_status_bar_text;
- }
-
- /** gets the name compressed Unicode flag
- * @return compressed unicode flag
- */
- public byte getCompressedUnicodeFlag() {
- return field_11_compressed_unicode_flag;
+ return field_12_name_text.length();
}
public boolean isHiddenName() {
return (field_1_option_flag & Option.OPT_HIDDEN_NAME) != 0;
}
+ public void setHidden(boolean b) {
+ if (b) {
+ field_1_option_flag |= Option.OPT_HIDDEN_NAME;
+ } else {
+ field_1_option_flag &= (~Option.OPT_HIDDEN_NAME);
+ }
+ }
/**
- * @return true if name is a function
+ * @return <code>true</code> if name is a function
*/
public boolean isFunctionName() {
return (field_1_option_flag & Option.OPT_FUNCTION_NAME) != 0;
}
+ /**
+ * @return <code>true</code> if name has a formula (named range or defined value)
+ */
+ public boolean hasFormula() {
+ return field_1_option_flag == 0 && field_13_name_definition.length > 0;
+ }
/**
* @return true if name is a command
*/
public boolean isBuiltInName()
{
- return ((this.field_1_option_flag & Option.OPT_BUILTIN) != 0);
+ return ((field_1_option_flag & Option.OPT_BUILTIN) != 0);
}
*/
public String getNameText(){
- return this.isBuiltInName() ? this.translateBuiltInName(this.getBuiltInName()) : field_12_name_text;
+ return isBuiltInName() ? translateBuiltInName(getBuiltInName()) : field_12_name_text;
}
/** Gets the Built In Name
*/
public byte getBuiltInName()
{
- return this.field_12_builtIn_name;
+ return field_12_built_in_code;
}
/** gets the definition, reference (Formula)
- * @return definition -- can be null if we cant parse ptgs
+ * @return the name formula. never <code>null</code>
*/
- public List getNameDefinition() {
- return field_13_name_definition;
+ public Ptg[] getNameDefinition() {
+ return (Ptg[]) field_13_name_definition.clone();
}
- public void setNameDefinition(Stack nameDefinition) {
- field_13_name_definition = nameDefinition;
+ public void setNameDefinition(Ptg[] ptgs) {
+ field_13_name_definition = (Ptg[]) ptgs.clone();
}
/** get the custom menu text
throw new RecordFormatException("NOT A valid Name RECORD");
}
}
-
+
+
/**
* called by the class that is responsible for writing this sucker.
* Subclasses should implement this so that their data is passed back in a
* @param data byte array containing instance data
* @return number of bytes written
*/
- public int serialize( int offset, byte[] data )
- {
- LittleEndian.putShort( data, 0 + offset, sid );
- short size = (short)( 15 + getTextsLength() + getNameDefinitionSize());
- LittleEndian.putShort( data, 2 + offset, size );
- // size defined below
- LittleEndian.putShort( data, 4 + offset, getOptionFlag() );
- data[6 + offset] = getKeyboardShortcut();
- data[7 + offset] = getNameTextLength();
- LittleEndian.putShort( data, 8 + offset, getDefinitionLength() );
- LittleEndian.putShort( data, 10 + offset, getUnused() );
- LittleEndian.putUShort( data, 12 + offset, field_6_sheetNumber);
- data[14 + offset] = getCustomMenuLength();
- data[15 + offset] = getDescriptionTextLength();
- data[16 + offset] = getHelpTopicLength();
- data[17 + offset] = getStatusBarLength();
- data[18 + offset] = getCompressedUnicodeFlag();
-
- int start_of_name_definition = 19 + field_3_length_name_text;
-
- if (this.isBuiltInName()) {
- //can send the builtin name directly in
- data [19 + offset] = this.getBuiltInName();
- } else if ((this.getCompressedUnicodeFlag() & 0x01) == 1) {
- StringUtil.putUnicodeLE( getNameText(), data, 19 + offset );
- start_of_name_definition = 19 + (2 * field_3_length_name_text);
- } else {
- StringUtil.putCompressedUnicode( getNameText(), data, 19 + offset );
- }
-
-
- Ptg.serializePtgStack(field_13_name_definition, data, start_of_name_definition + offset );
-
-
- int start_of_custom_menu_text = start_of_name_definition + field_4_length_name_definition;
- StringUtil.putCompressedUnicode( getCustomMenuText(), data, start_of_custom_menu_text + offset );
-
- int start_of_description_text = start_of_custom_menu_text + field_7_length_custom_menu;
- StringUtil.putCompressedUnicode( getDescriptionText(), data, start_of_description_text + offset );
-
- int start_of_help_topic_text = start_of_description_text + field_8_length_description_text;
- StringUtil.putCompressedUnicode( getHelpTopicText(), data, start_of_help_topic_text + offset );
-
- int start_of_status_bar_text = start_of_help_topic_text + field_9_length_help_topic_text;
- StringUtil.putCompressedUnicode( getStatusBarText(), data, start_of_status_bar_text + offset );
-
- return getRecordSize();
- /* } */
- }
-
- /**
- * Gets the length of all texts, in bytes
- * @return total length
- */
- public int getTextsLength(){
- int result;
-
- result = getRawNameTextLength() + getDescriptionTextLength() +
- getHelpTopicLength() + getStatusBarLength();
+ public int serialize( int offset, byte[] data ) {
- return result;
- }
-
- private int getNameDefinitionSize() {
- int result = 0;
- List list = field_13_name_definition;
+ int field_7_length_custom_menu = field_14_custom_menu_text.length();
+ int field_8_length_description_text = field_15_description_text.length();
+ int field_9_length_help_topic_text = field_16_help_topic_text.length();
+ int field_10_length_status_bar_text = field_17_status_bar_text.length();
+ int rawNameSize = getNameRawSize();
- for (int k = 0; k < list.size(); k++)
- {
- Ptg ptg = ( Ptg ) list.get(k);
-
- result += ptg.getSize();
+ int formulaTotalSize = Ptg.getEncodedSize(field_13_name_definition);
+ int dataSize = 15 // 4 shorts + 7 bytes
+ + rawNameSize
+ + field_7_length_custom_menu
+ + field_8_length_description_text
+ + field_9_length_help_topic_text
+ + field_10_length_status_bar_text
+ + formulaTotalSize;
+
+ LittleEndian.putShort(data, 0 + offset, sid);
+ LittleEndian.putUShort(data, 2 + offset, dataSize);
+ // size defined below
+ LittleEndian.putShort(data, 4 + offset, getOptionFlag());
+ LittleEndian.putByte(data, 6 + offset, getKeyboardShortcut());
+ LittleEndian.putByte(data, 7 + offset, getNameTextLength());
+ // Note -
+ LittleEndian.putUShort(data, 8 + offset, Ptg.getEncodedSizeWithoutArrayData(field_13_name_definition));
+ LittleEndian.putUShort(data, 10 + offset, field_5_index_to_sheet);
+ LittleEndian.putUShort(data, 12 + offset, field_6_sheetNumber);
+ LittleEndian.putByte(data, 14 + offset, field_7_length_custom_menu);
+ LittleEndian.putByte(data, 15 + offset, field_8_length_description_text);
+ LittleEndian.putByte(data, 16 + offset, field_9_length_help_topic_text);
+ LittleEndian.putByte(data, 17 + offset, field_10_length_status_bar_text);
+ LittleEndian.putByte(data, 18 + offset, field_11_nameIsMultibyte ? 1 : 0);
+ int pos = 19 + offset;
+
+ if (isBuiltInName()) {
+ //can send the builtin name directly in
+ LittleEndian.putByte(data, pos, field_12_built_in_code);
+ } else {
+ String nameText = field_12_name_text;
+ if (field_11_nameIsMultibyte) {
+ StringUtil.putUnicodeLE(nameText, data, pos);
+ } else {
+ StringUtil.putCompressedUnicode(nameText, data, pos);
+ }
}
- return result;
+ pos += rawNameSize;
+
+ Ptg.serializePtgs(field_13_name_definition, data, pos);
+ pos += formulaTotalSize;
+
+ StringUtil.putCompressedUnicode( getCustomMenuText(), data, pos);
+ pos += field_7_length_custom_menu;
+ StringUtil.putCompressedUnicode( getDescriptionText(), data, pos);
+ pos += field_8_length_description_text;
+ StringUtil.putCompressedUnicode( getHelpTopicText(), data, pos);
+ pos += field_9_length_help_topic_text;
+ StringUtil.putCompressedUnicode( getStatusBarText(), data, pos);
+
+ return 4 + dataSize;
+ }
+ private int getNameRawSize() {
+ if (isBuiltInName()) {
+ return 1;
+ }
+ int nChars = field_12_name_text.length();
+ if(field_11_nameIsMultibyte) {
+ return 2 * nChars;
+ }
+ return nChars;
}
/** returns the record size
*/
public int getRecordSize(){
- int result;
-
- result = 19 + getTextsLength() + getNameDefinitionSize();
-
-
- return result;
+ return 4 // sid + size
+ + 15 // 4 shorts + 7 bytes
+ + getNameRawSize()
+ + field_14_custom_menu_text.length()
+ + field_15_description_text.length()
+ + field_16_help_topic_text.length()
+ + field_17_status_bar_text.length()
+ + Ptg.getEncodedSize(field_13_name_definition);
}
/** gets the extern sheet number
* @return extern sheet index
*/
public short getExternSheetNumber(){
- if (field_13_name_definition == null || field_13_name_definition.isEmpty()) return 0;
- Ptg ptg = (Ptg) field_13_name_definition.peek();
- short result = 0;
+ if (field_13_name_definition.length < 1) {
+ return 0;
+ }
+ Ptg ptg = field_13_name_definition[0];
if (ptg.getClass() == Area3DPtg.class){
- result = ((Area3DPtg) ptg).getExternSheetIndex();
+ return ((Area3DPtg) ptg).getExternSheetIndex();
- } else if (ptg.getClass() == Ref3DPtg.class){
- result = ((Ref3DPtg) ptg).getExternSheetIndex();
}
-
- return result;
+ if (ptg.getClass() == Ref3DPtg.class){
+ return ((Ref3DPtg) ptg).getExternSheetIndex();
+ }
+ return 0;
}
/** sets the extern sheet number
public void setExternSheetNumber(short externSheetNumber){
Ptg ptg;
- if (field_13_name_definition == null || field_13_name_definition.isEmpty()){
- field_13_name_definition = new Stack();
+ if (field_13_name_definition.length < 1){
ptg = createNewPtg();
+ field_13_name_definition = new Ptg[] {
+ ptg,
+ };
} else {
- ptg = (Ptg) field_13_name_definition.peek();
+ ptg = field_13_name_definition[0];
}
if (ptg.getClass() == Area3DPtg.class){
}
- private Ptg createNewPtg(){
- Ptg ptg = new Area3DPtg("A1", 0); // TODO - change to not be partially initialised
- field_13_name_definition.push(ptg);
-
- return ptg;
+ private static Ptg createNewPtg(){
+ return new Area3DPtg("A1:A1", 0); // TODO - change to not be partially initialised
}
/** gets the reference , the area only (range)
//Trying to find if what ptg do we need
RangeAddress ra = new RangeAddress(ref);
Ptg oldPtg;
- Ptg ptg;
- if (field_13_name_definition==null ||field_13_name_definition.isEmpty()){
- field_13_name_definition = new Stack();
+ if (field_13_name_definition.length < 1){
oldPtg = createNewPtg();
} else {
//Trying to find extern sheet index
- oldPtg = (Ptg) field_13_name_definition.pop();
+ oldPtg = field_13_name_definition[0];
}
-
+ List temp = new ArrayList();
short externSheetIndex = 0;
if (oldPtg.getClass() == Area3DPtg.class){
if (ra.hasRange()) {
// Is it contiguous or not?
- AreaReference[] refs =
- AreaReference.generateContiguous(ref);
- this.setDefinitionTextLength((short)0);
+ AreaReference[] refs = AreaReference.generateContiguous(ref);
- // Add the area reference(s)
+ // Add the area reference(s)
for(int i=0; i<refs.length; i++) {
- ptg = new Area3DPtg(refs[i].formatAsString(), externSheetIndex);
- field_13_name_definition.push(ptg);
- this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) );
+ Ptg ptg = new Area3DPtg(refs[i].formatAsString(), externSheetIndex);
+ temp.add(ptg);
}
// And then a union if we had more than one area
if(refs.length > 1) {
- ptg = UnionPtg.instance;
- field_13_name_definition.push(ptg);
- this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) );
+ Ptg ptg = UnionPtg.instance;
+ temp.add(ptg);
}
} else {
- ptg = new Ref3DPtg();
+ Ptg ptg = new Ref3DPtg();
((Ref3DPtg) ptg).setExternSheetIndex(externSheetIndex);
((Ref3DPtg) ptg).setArea(ref);
- field_13_name_definition.push(ptg);
- this.setDefinitionTextLength((short)ptg.getSize());
+ temp.add(ptg);
}
+ Ptg[] ptgs = new Ptg[temp.size()];
+ temp.toArray(ptgs);
+ field_13_name_definition = ptgs;
}
/**
* @param in the RecordInputstream to read the record from
*/
protected void fillFields(RecordInputStream in) {
- field_1_option_flag = in.readShort();
- field_2_keyboard_shortcut = in.readByte();
- field_3_length_name_text = in.readByte();
- field_4_length_name_definition = in.readShort();
- field_5_index_to_sheet = in.readShort();
- field_6_sheetNumber = in.readUShort();
- field_7_length_custom_menu = in.readByte();
- field_8_length_description_text = in.readByte();
- field_9_length_help_topic_text = in.readByte();
- field_10_length_status_bar_text = in.readByte();
-
- //store the name in byte form if it's a builtin name
- field_11_compressed_unicode_flag= in.readByte();
- if (this.isBuiltInName()) {
- field_12_builtIn_name = in.readByte();
- } else {
- if (field_11_compressed_unicode_flag == 1) {
- field_12_name_text = in.readUnicodeLEString(field_3_length_name_text);
- } else {
- field_12_name_text = in.readCompressedUnicode(field_3_length_name_text);
- }
+ field_1_option_flag = in.readShort();
+ field_2_keyboard_shortcut = in.readByte();
+ int field_3_length_name_text = in.readByte();
+ int field_4_length_name_definition = in.readShort();
+ field_5_index_to_sheet = in.readShort();
+ field_6_sheetNumber = in.readUShort();
+ int field_7_length_custom_menu = in.readUByte();
+ int field_8_length_description_text = in.readUByte();
+ int field_9_length_help_topic_text = in.readUByte();
+ int field_10_length_status_bar_text = in.readUByte();
+
+ //store the name in byte form if it's a built-in name
+ field_11_nameIsMultibyte = (in.readByte() != 0);
+ if (isBuiltInName()) {
+ field_12_built_in_code = in.readByte();
+ } else {
+ if (field_11_nameIsMultibyte) {
+ field_12_name_text = in.readUnicodeLEString(field_3_length_name_text);
+ } else {
+ field_12_name_text = in.readCompressedUnicode(field_3_length_name_text);
+ }
}
-
- field_13_name_definition = Ptg.createParsedExpressionTokens(field_4_length_name_definition, in);
-
+
+ field_13_name_definition = Ptg.readTokens(field_4_length_name_definition, in);
+
//Who says that this can only ever be compressed unicode???
- field_14_custom_menu_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_7_length_custom_menu));
-
- field_15_description_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_8_length_description_text));
-
- field_16_help_topic_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_9_length_help_topic_text));
-
- field_17_status_bar_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(field_10_length_status_bar_text));
- /*} */
+ field_14_custom_menu_text = in.readCompressedUnicode(field_7_length_custom_menu);
+ field_15_description_text = in.readCompressedUnicode(field_8_length_description_text);
+ field_16_help_topic_text = in.readCompressedUnicode(field_9_length_help_topic_text);
+ field_17_status_bar_text = in.readCompressedUnicode(field_10_length_status_bar_text);
}
/**
return sid;
}
/*
- 20 00
- 00
- 01
+ 20 00
+ 00
+ 01
1A 00 // sz = 0x1A = 26
- 00 00
- 01 00
- 00
- 00
- 00
- 00
+ 00 00
+ 01 00
+ 00
+ 00
+ 00
+ 00
00 // unicode flag
07 // name
-
+
29 17 00 3B 00 00 00 00 FF FF 00 00 02 00 3B 00 //{ 26
00 07 00 07 00 00 00 FF 00 10 // }
-
-
-
- 20 00
- 00
- 01
+
+
+
+ 20 00
+ 00
+ 01
0B 00 // sz = 0xB = 11
- 00 00
- 01 00
- 00
- 00
- 00
- 00
+ 00 00
+ 01 00
+ 00
+ 00
+ 00
+ 00
00 // unicode flag
07 // name
-
+
3B 00 00 07 00 07 00 00 00 FF 00 // { 11 }
*/
/*
- 18, 00,
- 1B, 00,
-
- 20, 00,
- 00,
- 01,
- 0B, 00,
- 00,
- 00,
- 00,
- 00,
- 00,
- 07,
- 3B 00 00 07 00 07 00 00 00 FF 00 ]
+ 18, 00,
+ 1B, 00,
+
+ 20, 00,
+ 00,
+ 01,
+ 0B, 00,
+ 00,
+ 00,
+ 00,
+ 00,
+ 00,
+ 07,
+ 3B 00 00 07 00 07 00 00 00 FF 00 ]
*/
- /**
- * @see Object#toString()
- */
public String toString() {
- StringBuffer buffer = new StringBuffer();
-
- buffer.append("[NAME]\n");
- buffer.append(" .option flags = ").append( HexDump.toHex( field_1_option_flag ) )
- .append("\n");
- buffer.append(" .keyboard shortcut = ").append( HexDump.toHex( field_2_keyboard_shortcut ) )
- .append("\n");
- buffer.append(" .length of the name = ").append( field_3_length_name_text )
- .append("\n");
- buffer.append(" .size of the formula data = ").append( field_4_length_name_definition )
- .append("\n");
- buffer.append(" .unused = ").append( field_5_index_to_sheet )
- .append("\n");
- buffer.append(" .index to sheet (1-based, 0=Global) = ").append( field_6_sheetNumber )
- .append("\n");
- buffer.append(" .Length of menu text (character count) = ").append( field_7_length_custom_menu )
- .append("\n");
- buffer.append(" .Length of description text (character count) = ").append( field_8_length_description_text )
- .append("\n");
- buffer.append(" .Length of help topic text (character count) = ").append( field_9_length_help_topic_text )
- .append("\n");
- buffer.append(" .Length of status bar text (character count) = ").append( field_10_length_status_bar_text )
- .append("\n");
- buffer.append(" .Name (Unicode flag) = ").append( field_11_compressed_unicode_flag )
- .append("\n");
- buffer.append(" .Name (Unicode text) = ").append( getNameText() )
- .append("\n");
-
- buffer.append(" .Parts (" + field_13_name_definition.size() +"):")
- .append("\n");
- Iterator it = field_13_name_definition.iterator();
- while(it.hasNext()) {
- Ptg ptg = (Ptg)it.next();
- buffer.append(" " + ptg.toString()).append("\n");
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("[NAME]\n");
+ sb.append(" .option flags = ").append(HexDump.shortToHex(field_1_option_flag)).append("\n");
+ sb.append(" .keyboard shortcut = ").append(HexDump.byteToHex(field_2_keyboard_shortcut)).append("\n");
+ sb.append(" .length of the name = ").append(getNameTextLength()).append("\n");
+ sb.append(" .unused = ").append( field_5_index_to_sheet ).append("\n");
+ sb.append(" .index to sheet (1-based, 0=Global) = ").append( field_6_sheetNumber ).append("\n");
+ sb.append(" .Menu text length = ").append(field_14_custom_menu_text.length()).append("\n");
+ sb.append(" .Description text length= ").append(field_15_description_text.length()).append("\n");
+ sb.append(" .Help topic text length = ").append(field_16_help_topic_text.length()).append("\n");
+ sb.append(" .Status bar text length = ").append(field_17_status_bar_text.length()).append("\n");
+ sb.append(" .NameIsMultibyte = ").append(field_11_nameIsMultibyte).append("\n");
+ sb.append(" .Name (Unicode text) = ").append( getNameText() ).append("\n");
+ sb.append(" .Formula (nTokens=").append(field_13_name_definition.length).append("):") .append("\n");
+ for (int i = 0; i < field_13_name_definition.length; i++) {
+ Ptg ptg = field_13_name_definition[i];
+ sb.append(" " + ptg.toString()).append(ptg.getRVAType()).append("\n");
}
-
- buffer.append(" .Menu text (Unicode string without length field) = ").append( field_14_custom_menu_text )
- .append("\n");
- buffer.append(" .Description text (Unicode string without length field) = ").append( field_15_description_text )
- .append("\n");
- buffer.append(" .Help topic text (Unicode string without length field) = ").append( field_16_help_topic_text )
- .append("\n");
- buffer.append(" .Status bar text (Unicode string without length field) = ").append( field_17_status_bar_text )
- .append("\n");
- buffer.append("[/NAME]\n");
-
- return buffer.toString();
+
+ sb.append(" .Menu text = ").append(field_14_custom_menu_text).append("\n");
+ sb.append(" .Description text= ").append(field_15_description_text).append("\n");
+ sb.append(" .Help topic text = ").append(field_16_help_topic_text).append("\n");
+ sb.append(" .Status bar text = ").append(field_17_status_bar_text).append("\n");
+ sb.append("[/NAME]\n");
+
+ return sb.toString();
}
/**Creates a human readable name for built in types
* @return Unknown if the built-in name cannot be translated
*/
- protected String translateBuiltInName(byte name)
+ private static String translateBuiltInName(byte name)
{
switch (name)
{
case NameRecord.BUILTIN_CONSOLIDATE_AREA : return "Consolidate_Area";
case NameRecord.BUILTIN_CRITERIA : return "Criteria";
case NameRecord.BUILTIN_DATABASE : return "Database";
- case NameRecord.BUILTIN_DATA_FORM : return "Data_Form";
+ case NameRecord.BUILTIN_DATA_FORM : return "Data_Form";
case NameRecord.BUILTIN_PRINT_AREA : return "Print_Area";
case NameRecord.BUILTIN_PRINT_TITLE : return "Print_Titles";
case NameRecord.BUILTIN_RECORDER : return "Recorder";
case NameRecord.BUILTIN_SHEET_TITLE : return "Sheet_Title";
-
+ case NameRecord.BUILTIN_FILTER_DB : return "_FilterDatabase";
+
}
-
+
return "Unknown";
}
}
buffer.append("columns = ").append(getColumnCount()).append("\n");
buffer.append("rows = ").append(getRowCount()).append("\n");
- for (int x=0;x<getColumnCount();x++) {
- for (int y=0;y<getRowCount();y++) {
- Object o = token_3_arrayValues[getValueIndex(x, y)];
- buffer.append("[").append(x).append("][").append(y).append("] = ").append(o).append("\n");
+ if (token_3_arrayValues == null) {
+ buffer.append(" #values#uninitialised#\n");
+ } else {
+ for (int x=0;x<getColumnCount();x++) {
+ for (int y=0;y<getRowCount();y++) {
+ Object o = token_3_arrayValues[getValueIndex(x, y)];
+ buffer.append("[").append(x).append("][").append(y).append("] = ").append(o).append("\n");
+ }
}
}
return buffer.toString();
private static final BitField colRelative = BitFieldFactory.getInstance(0x4000);
private final static int SIZE = 7; // 6 + 1 for Ptg
- private short field_1_index_extern_sheet;
+ private int field_1_index_extern_sheet;
/** The row index - zero based unsigned 16 bit value */
private int field_2_row;
/** Field 2
}
public short getExternSheetIndex(){
- return field_1_index_extern_sheet;
+ return (short)field_1_index_extern_sheet;
}
- public void setExternSheetIndex(short index){
+ public void setExternSheetIndex(int index){
field_1_index_extern_sheet = index;
}
r(m, "IMSUB", null);
r(m, "IMSUM", null);
r(m, "INTRATE", null);
- r(m, "ISEVEN", null);
- r(m, "ISODD", null);
+ r(m, "ISEVEN", ParityFunction.IS_EVEN);
+ r(m, "ISODD", ParityFunction.IS_ODD);
r(m, "LCM", null);
r(m, "MDURATION", null);
r(m, "MROUND", null);
--- /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.atp;
+
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.BoolEval;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+import org.apache.poi.hssf.record.formula.eval.OperandResolver;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+/**
+ * Implementation of Excel 'Analysis ToolPak' function ISEVEN() ISODD()<br/>
+ *
+ * @author Josh Micich
+ */
+final class ParityFunction implements FreeRefFunction {
+
+ public static final FreeRefFunction IS_EVEN = new ParityFunction(0);
+ public static final FreeRefFunction IS_ODD = new ParityFunction(1);
+ private final int _desiredParity;
+
+ private ParityFunction(int desiredParity) {
+ _desiredParity = desiredParity;
+ }
+
+ public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook,
+ Sheet sheet) {
+ if (args.length != 1) {
+ return ErrorEval.VALUE_INVALID;
+ }
+
+ int val;
+ try {
+ val = evaluateArgParity(args[0], srcCellRow, srcCellCol);
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+
+ return BoolEval.valueOf(val == _desiredParity);
+ }
+
+ private static int evaluateArgParity(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
+ ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+
+ if (ve == BlankEval.INSTANCE) {
+ return 0;
+ }
+ double d = OperandResolver.coerceValueToDouble(ve);
+ if (d < 0) {
+ d = -d;
+ }
+ long v = (long) Math.floor(d);
+ return (int) (v & 0x0001);
+ }
+}
import java.util.GregorianCalendar;
import java.util.regex.Pattern;
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
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.EvaluationException;
Calendar date = parseDate(strVal);
return DateUtil.getExcelDate(date, false);
}
+ if (ve instanceof BlankEval) {
+ return 0.0;
+ }
return OperandResolver.coerceValueToDouble(ve);
}
} catch (NumberFormatException e) {
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
- if (f0<0 || f1<0 || f2<0 || f0>12 || f1>12 || f2>12) {
+ if (f0<0 || f1<0 || f2<0 || (f0>12 && f1>12 && f2>12)) {
// easy to see this cannot be a valid date
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
if (day <1 || day>cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
+ cal.set(Calendar.DAY_OF_MONTH, day);
return cal;
}
-/*
-* 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.
-*/
-/*
- * Created on May 8, 2005
- *
- */
+/* ====================================================================
+ 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.AddPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
* <li> 1+A1 = 2 if A1 contains TRUE or =TRUE
* <li> 1+A1 = #VALUE! if A1 contains "TRUE" or ="TRUE"
*/
-public class AddEval extends NumericOperationEval {
-
- private AddPtg 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 AddEval(Ptg ptg) {
- delegate = (AddPtg) ptg;
- }
+public final class AddEval extends TwoOperandNumericOperation {
- public ValueEvalToNumericXlator getXlator() {
- return NUM_XLATOR;
- }
-
-
- public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
- if(args.length != 2) {
- return ErrorEval.VALUE_INVALID;
- }
-
- double d = 0;
- for (int i = 0; i < 2; i++) {
- ValueEval ve = singleOperandEvaluate(args[i], srcRow, srcCol);
- if(ve instanceof ErrorEval) {
- return ve;
- }
- if (ve instanceof NumericValueEval) {
- d += ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- return ErrorEval.VALUE_INVALID;
- }
- }
- if(Double.isNaN(d) || Double.isInfinite(d)) {
- return ErrorEval.NUM_ERROR;
- }
- return new NumberEval(d);
- }
+ public static final OperationEval instance = new AddEval();
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
+ private AddEval() {
+ }
- public int getType() {
- return delegate.getType();
- }
+ protected double evaluate(double d0, double d1) {
+ return d0 + d1;
+ }
}
-/*
-* 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.
-*/
+/* ====================================================================
+ 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.Ptg;
-import org.apache.poi.hssf.record.formula.DividePtg;
-
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
*/
-public final class DivideEval extends NumericOperationEval {
-
- private DividePtg 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 DivideEval(Ptg ptg) {
- delegate = (DividePtg) ptg;
- }
-
- protected ValueEvalToNumericXlator getXlator() {
- return NUM_XLATOR;
- }
-
- public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
- if(args.length != 2) {
- return ErrorEval.VALUE_INVALID;
- }
- Eval retval = null;
- double d0 = 0;
- double d1 = 0;
- ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d0 = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
-
- if (retval == null) { // no error yet
- ve = singleOperandEvaluate(args[1], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d1 = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
+public final class DivideEval extends TwoOperandNumericOperation {
- if (retval == null) {
- retval = (d1 == 0)
- ? ErrorEval.DIV_ZERO
- : (Double.isNaN(d0) || Double.isNaN(d1))
- ? (ValueEval) ErrorEval.VALUE_INVALID
- : new NumberEval(d0 / d1);
- }
- return retval;
- }
+ public static final OperationEval instance = new DivideEval();
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
+ private DivideEval() {
+ }
- public int getType() {
- return delegate.getType();
- }
+ protected double evaluate(double d0, double d1) throws EvaluationException {
+ if (d1 == 0.0) {
+ throw new EvaluationException(ErrorEval.DIV_ZERO);
+ }
+ return d0 / d1;
+ }
}
-/*
-* 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.
-*/
-/*
- * Created on May 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+ 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.
+==================================================================== */
-import org.apache.poi.hssf.record.formula.EqualPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class EqualEval extends RelationalOperationEval {
-
- private EqualPtg delegate;
-
- public EqualEval(Ptg ptg) {
- this.delegate = (EqualPtg) ptg;
- }
-
-
- public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
- ValueEval retval = null;
-
- RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
- retval = rvs.ee;
- int result = 0;
- if (retval == null) {
- result = doComparison(rvs.bs);
- if (result == 0) {
- result = doComparison(rvs.ss);
- }
- if (result == 0) {
- result = doComparison(rvs.ds);
- }
-
- retval = (result == 0) ? BoolEval.TRUE : BoolEval.FALSE;
- }
-
- return retval;
- }
+public final class EqualEval extends RelationalOperationEval {
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
+ public static final OperationEval instance = new EqualEval();
+
+ private EqualEval() {
+ }
- public int getType() {
- return delegate.getType();
- }
+ protected boolean convertComparisonResult(int cmpResult) {
+ return cmpResult == 0;
+ }
}
FreeRefFunction targetFunc;
try {
if (nameArg instanceof NameEval) {
- targetFunc = findInternalUserDefinedFunction(workbook, (NameEval) nameArg);
+ targetFunc = findInternalUserDefinedFunction((NameEval) nameArg);
} else if (nameArg instanceof NameXEval) {
targetFunc = findExternalUserDefinedFunction(workbook, (NameXEval) nameArg);
} else {
if(false) {
System.out.println("received call to external user defined function (" + functionName + ")");
}
- // currently only looking for functions from the 'Analysis TookPak'
+ // currently only looking for functions from the 'Analysis TookPak' e.g. "YEARFRAC" or "ISEVEN"
// not sure how much this logic would need to change to support other or multiple add-ins.
FreeRefFunction result = AnalysisToolPak.findFunction(functionName);
if (result != null) {
throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED);
}
- private FreeRefFunction findInternalUserDefinedFunction(Workbook workbook, NameEval functionNameEval) throws EvaluationException {
-
- int numberOfNames = workbook.getNumberOfNames();
-
- int nameIndex = functionNameEval.getIndex();
- if(nameIndex < 0 || nameIndex >= numberOfNames) {
- throw new RuntimeException("Bad name index (" + nameIndex
- + "). Allowed range is (0.." + (numberOfNames-1) + ")");
- }
-
- String functionName = workbook.getNameName(nameIndex);
+ private FreeRefFunction findInternalUserDefinedFunction(NameEval functionNameEval) throws EvaluationException {
+ String functionName = functionNameEval.getFunctionName();
if(false) {
System.out.println("received call to internal user defined function (" + functionName + ")");
}
- // TODO - detect if the NameRecord corresponds to a named range, function, or something undefined
- // throw the right errors in these cases
-
- // TODO find the implementation for the external function e.g. "YEARFRAC" or "ISEVEN"
+ // TODO find the implementation for the user defined function
throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED);
}
-/*
-* 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.
-*/
-/*
- * Created on May 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+ 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.
+==================================================================== */
-import org.apache.poi.hssf.record.formula.GreaterEqualPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class GreaterEqualEval extends RelationalOperationEval {
-
- private GreaterEqualPtg delegate;
-
- public GreaterEqualEval(Ptg ptg) {
- this.delegate = (GreaterEqualPtg) ptg;
- }
-
- public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
- ValueEval retval = null;
-
- RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
- retval = rvs.ee;
- int result = 0;
- if (retval == null) {
- result = doComparison(rvs.bs);
- if (result == 0) {
- result = doComparison(rvs.ss);
- }
- if (result == 0) {
- result = doComparison(rvs.ds);
- }
-
- retval = (result >= 0) ? BoolEval.TRUE : BoolEval.FALSE;
- }
-
- return retval;
- }
-
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
+public final class GreaterEqualEval extends RelationalOperationEval {
- public int getType() {
- return delegate.getType();
- }
+ public static final OperationEval instance = new GreaterEqualEval();
+
+ private GreaterEqualEval() {
+ }
+ protected boolean convertComparisonResult(int cmpResult) {
+ return cmpResult >= 0;
+ }
}
-/*
-* 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.
-*/
-/*
- * Created on May 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+ 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.
+==================================================================== */
-import org.apache.poi.hssf.record.formula.GreaterThanPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class GreaterThanEval extends RelationalOperationEval {
-
- private GreaterThanPtg delegate;
-
- public GreaterThanEval(Ptg ptg) {
- this.delegate = (GreaterThanPtg) ptg;
- }
-
- public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
- ValueEval retval = null;
-
- RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
- retval = rvs.ee;
- int result = 0;
- if (retval == null) {
- result = doComparison(rvs.bs);
- if (result == 0) {
- result = doComparison(rvs.ss);
- }
- if (result == 0) {
- result = doComparison(rvs.ds);
- }
-
- retval = (result > 0) ? BoolEval.TRUE : BoolEval.FALSE;;
- }
-
- return retval;
- }
-
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
+public final class GreaterThanEval extends RelationalOperationEval {
- public int getType() {
- return delegate.getType();
- }
+ public static final OperationEval instance = new GreaterThanEval();
+
+ private GreaterThanEval() {
+ }
+ protected boolean convertComparisonResult(int cmpResult) {
+ return cmpResult > 0;
+ }
}
-/*
-* 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.
-*/
+/* ====================================================================
+ 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.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellReference;
/**
- * @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
+ *
+ * @author Josh Micich
*/
public final class LazyAreaEval extends AreaEvalBase {
private final Sheet _sheet;
- private Workbook _workbook;
+ private FormulaEvaluator _evaluator;
- public LazyAreaEval(AreaI ptg, Sheet sheet, Workbook workbook) {
+ public LazyAreaEval(AreaI ptg, Sheet sheet, FormulaEvaluator evaluator) {
super(ptg);
_sheet = sheet;
- _workbook = workbook;
+ _evaluator = evaluator;
}
public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
if (cell == null) {
return BlankEval.INSTANCE;
}
- return FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook);
+ return _evaluator.getEvalForCell(cell, _sheet);
}
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
- return new LazyAreaEval(area, _sheet, _workbook);
+ return new LazyAreaEval(area, _sheet, _evaluator);
+ }
+ public String toString() {
+ CellReference crA = new CellReference(getFirstRow(), getFirstColumn());
+ CellReference crB = new CellReference(getLastRow(), getLastColumn());
+ StringBuffer sb = new StringBuffer();
+ sb.append(getClass().getName()).append("[");
+ String sheetName = _evaluator.getSheetName(_sheet);
+ sb.append(sheetName);
+ sb.append('!');
+ sb.append(crA.formatAsString());
+ sb.append(':');
+ sb.append(crB.formatAsString());
+ sb.append("]");
+ return sb.toString();
}
}
+/* ====================================================================\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
+\r
package org.apache.poi.hssf.record.formula.eval;\r
\r
import org.apache.poi.hssf.record.formula.AreaI;\r
-import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;\r
import org.apache.poi.hssf.record.formula.Ref3DPtg;\r
import org.apache.poi.hssf.record.formula.RefPtg;\r
+import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;\r
import org.apache.poi.ss.usermodel.Cell;\r
import org.apache.poi.ss.usermodel.FormulaEvaluator;\r
import org.apache.poi.ss.usermodel.Row;\r
import org.apache.poi.ss.usermodel.Sheet;\r
import org.apache.poi.ss.usermodel.Workbook;\r
+import org.apache.poi.ss.util.CellReference;\r
\r
+/**\r
+*\r
+* @author Josh Micich \r
+*/\r
public final class LazyRefEval extends RefEvalBase {\r
-\r
private final Sheet _sheet;\r
- private final Workbook _workbook;\r
+ private final FormulaEvaluator _evaluator;\r
\r
\r
- public LazyRefEval(RefPtg ptg, Sheet sheet, Workbook workbook) {\r
+ public LazyRefEval(RefPtg ptg, Sheet sheet, FormulaEvaluator evaluator) {\r
super(ptg.getRow(), ptg.getColumn());\r
_sheet = sheet;\r
- _workbook = workbook;\r
+ _evaluator = evaluator;\r
}\r
- public LazyRefEval(Ref3DPtg ptg, Sheet sheet, Workbook workbook) {\r
+ public LazyRefEval(Ref3DPtg ptg, Sheet sheet, FormulaEvaluator evaluator) {\r
super(ptg.getRow(), ptg.getColumn());\r
_sheet = sheet;\r
- _workbook = workbook;\r
+ _evaluator = evaluator;\r
}\r
\r
public ValueEval getInnerValueEval() {\r
if (cell == null) {\r
return BlankEval.INSTANCE;\r
}\r
- return FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook);\r
+ return _evaluator.getEvalForCell(cell, _sheet);\r
}\r
\r
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {\r
AreaI area = new OffsetArea(getRow(), getColumn(),\r
relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);\r
\r
- return new LazyAreaEval(area, _sheet, _workbook);\r
+ return new LazyAreaEval(area, _sheet, _evaluator);\r
+ }\r
+ \r
+ public String toString() {\r
+ CellReference cr = new CellReference(getRow(), getColumn());\r
+ StringBuffer sb = new StringBuffer();\r
+ sb.append(getClass().getName()).append("[");\r
+ String sheetName = _evaluator.getSheetName(_sheet);\r
+ sb.append(sheetName);\r
+ sb.append('!');\r
+ sb.append(cr.formatAsString());\r
+ sb.append("]");\r
+ return sb.toString();\r
}\r
}\r
-/*
-* 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.
-*/
-/*
- * Created on May 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+ 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.
+==================================================================== */
-import org.apache.poi.hssf.record.formula.LessEqualPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class LessEqualEval extends RelationalOperationEval {
-
- private LessEqualPtg delegate;
-
- public LessEqualEval(Ptg ptg) {
- this.delegate = (LessEqualPtg) ptg;
- }
-
- public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
- ValueEval retval = null;
-
- RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
- retval = rvs.ee;
- int result = 0;
- if (retval == null) {
- result = doComparison(rvs.bs);
- if (result == 0) {
- result = doComparison(rvs.ss);
- }
- if (result == 0) {
- result = doComparison(rvs.ds);
- }
-
- retval = (result <= 0) ? BoolEval.TRUE : BoolEval.FALSE;;
- }
-
- return retval;
- }
-
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
+public final class LessEqualEval extends RelationalOperationEval {
- public int getType() {
- return delegate.getType();
- }
+ public static final OperationEval instance = new LessEqualEval();
+
+ private LessEqualEval() {
+ }
+ protected boolean convertComparisonResult(int cmpResult) {
+ return cmpResult <= 0;
+ }
}
-/*
-* 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.
-*/
-/*
- * Created on May 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+ 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.
+==================================================================== */
-import org.apache.poi.hssf.record.formula.LessThanPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class LessThanEval extends RelationalOperationEval {
-
- private LessThanPtg delegate;
-
- public LessThanEval(Ptg ptg) {
- this.delegate = (LessThanPtg) ptg;
- }
-
-
- public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
- ValueEval retval = null;
-
- RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
- retval = rvs.ee;
- int result = 0;
- if (retval == null) {
- result = doComparison(rvs.bs);
- if (result == 0) {
- result = doComparison(rvs.ss);
- }
- if (result == 0) {
- result = doComparison(rvs.ds);
- }
-
- retval = (result < 0) ? BoolEval.TRUE : BoolEval.FALSE;;
- }
-
- return retval;
- }
-
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
+public final class LessThanEval extends RelationalOperationEval {
- public int getType() {
- return delegate.getType();
- }
+ public static final OperationEval instance = new LessThanEval();
+
+ private LessThanEval() {
+ }
+ protected boolean convertComparisonResult(int cmpResult) {
+ return cmpResult < 0;
+ }
}
-/*
-* 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.
-*/
+/* ====================================================================
+ 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.Ptg;
-import org.apache.poi.hssf.record.formula.MultiplyPtg;
-
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
*/
-public final class MultiplyEval extends NumericOperationEval {
-
- private MultiplyPtg 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 MultiplyEval(Ptg ptg) {
- delegate = (MultiplyPtg) ptg;
- }
-
- protected ValueEvalToNumericXlator getXlator() {
- return NUM_XLATOR;
- }
+public final class MultiplyEval extends TwoOperandNumericOperation {
- public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
- if(args.length != 2) {
- return ErrorEval.VALUE_INVALID;
- }
-
- double d0 = 0;
- double d1 = 0;
- ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d0 = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- return ErrorEval.VALUE_INVALID;
- }
-
- ve = singleOperandEvaluate(args[1], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d1 = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- return ErrorEval.VALUE_INVALID;
- }
-
- if (Double.isNaN(d0) || Double.isNaN(d1)) {
- return ErrorEval.NUM_ERROR;
- }
- return new NumberEval(d0 * d1);
- }
+ public static final OperationEval instance = new MultiplyEval();
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
+ private MultiplyEval() {
+ }
- public int getType() {
- return delegate.getType();
- }
+ protected double evaluate(double d0, double d1) {
+ return d0 * d1;
+ }
}
*/
public final class NameEval implements Eval {
- private final int _index;
+ private final String _functionName;
/**
- * @param index zero based index to a defined name record
+ * Creates a NameEval representing a function name
*/
- public NameEval(int index) {
- _index = index;
+ public NameEval(String functionName) {
+ _functionName = functionName;
}
- /**
- * @return zero based index to a defined name record
- */
- public int getIndex() {
- return _index;
+
+ public String getFunctionName() {
+ return _functionName;
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");
- sb.append(_index);
+ sb.append(_functionName);
sb.append("]");
return sb.toString();
}
-/*
-* 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.
-*/
-/*
- * Created on May 8, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
+/* ====================================================================
+ 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.
+==================================================================== */
-import org.apache.poi.hssf.record.formula.NotEqualPtg;
-import org.apache.poi.hssf.record.formula.Ptg;
+package org.apache.poi.hssf.record.formula.eval;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public class NotEqualEval extends RelationalOperationEval {
-
- private NotEqualPtg delegate;
-
- public NotEqualEval(Ptg ptg) {
- this.delegate = (NotEqualPtg) ptg;
- }
-
-
- public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
- ValueEval retval = null;
-
- RelationalValues rvs = super.doEvaluate(operands, srcRow, srcCol);
- retval = rvs.ee;
- int result = 0;
- if (retval == null) {
- result = doComparison(rvs.bs);
- if (result == 0) {
- result = doComparison(rvs.ss);
- }
- if (result == 0) {
- result = doComparison(rvs.ds);
- }
-
- retval = (result != 0) ? BoolEval.TRUE : BoolEval.FALSE;
- }
-
- return retval;
- }
-
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
+public final class NotEqualEval extends RelationalOperationEval {
- public int getType() {
- return delegate.getType();
- }
+ public static final OperationEval instance = new NotEqualEval();
+
+ private NotEqualEval() {
+ }
+ protected boolean convertComparisonResult(int cmpResult) {
+ return cmpResult != 0;
+ }
}
+++ /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.
-*/
-/*
- * Created on May 14, 2005
- *
- */
-package org.apache.poi.hssf.record.formula.eval;
-
-/**
- * @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
- */
-public abstract class NumericOperationEval implements OperationEval {
-
- protected abstract ValueEvalToNumericXlator getXlator();
-
- protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) {
- ValueEval retval;
- if (eval instanceof AreaEval) {
- AreaEval ae = (AreaEval) eval;
- if (ae.contains(srcRow, srcCol)) { // circular ref!
- retval = ErrorEval.CIRCULAR_REF_ERROR;
- }
- else if (ae.isRow()) {
- if (ae.containsColumn(srcCol)) {
- ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
- ve = getXlator().attemptXlateToNumeric(ve);
- retval = getXlator().attemptXlateToNumeric(ve);
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- else if (ae.isColumn()) {
- if (ae.containsRow(srcRow)) {
- ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
- retval = getXlator().attemptXlateToNumeric(ve);
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
- else {
- retval = getXlator().attemptXlateToNumeric((ValueEval) eval);
- }
- return retval;
- }
-}
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 {
+public final class PercentEval implements OperationEval {
- private PercentPtg _delegate;
+ public static final OperationEval instance = new PercentEval();
- 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;
+ private PercentEval() {
}
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);
+ double d0;
+ try {
+ ValueEval ve = OperandResolver.getSingleValue(args[0], srcRow, srcCol);
+ if (ve instanceof BlankEval) {
+ return NumberEval.ZERO;
+ }
+ d0 = OperandResolver.coerceValueToDouble(ve);
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
}
-
- if (ve instanceof BlankEval) {
- return NumberEval.ZERO;
- }
- if (ve instanceof ErrorEval) {
- return ve;
- }
- return ErrorEval.VALUE_INVALID;
+ return new NumberEval(d0 / 100);
}
public int getNumberOfOperands() {
- return _delegate.getNumberOfOperands();
- }
-
- public int getType() {
- return _delegate.getType();
+ return 1;
}
+ public final int getType() {
+ // TODO - remove
+ throw new RuntimeException("obsolete code should not be called");
+ }
}
-/*
-* 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.
-*/
+/* ====================================================================
+ 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.Ptg;
-import org.apache.poi.hssf.record.formula.PowerPtg;
-
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
*/
-public final class PowerEval extends NumericOperationEval {
-
- private PowerPtg 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 PowerEval(Ptg ptg) {
- delegate = (PowerPtg) ptg;
- }
-
- protected ValueEvalToNumericXlator getXlator() {
- return NUM_XLATOR;
- }
-
- public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
- if(args.length != 2) {
- return ErrorEval.VALUE_INVALID;
- }
- double d0 = 0;
- double d1 = 0;
-
- ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d0 = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- return ErrorEval.VALUE_INVALID;
- }
-
- ve = singleOperandEvaluate(args[1], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d1 = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- return ErrorEval.VALUE_INVALID;
- }
+public final class PowerEval extends TwoOperandNumericOperation {
- double p = Math.pow(d0, d1);
- if (Double.isNaN(p)) {
- return ErrorEval.VALUE_INVALID;
- }
- return new NumberEval(p);
- }
+ public static final OperationEval instance = new PowerEval();
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
+ private PowerEval() {
+ }
- public int getType() {
- return delegate.getType();
- }
+ protected double evaluate(double d0, double d1) {
+ return Math.pow(d0, d1);
+ }
}
-/*
-* 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.
-*/
-/*
- * Created on May 10, 2005
- *
- */
+/* ====================================================================
+ 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;
/**
+ * Base class for all comparison operator evaluators
+ *
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
*/
public abstract class RelationalOperationEval implements OperationEval {
- protected class RelationalValues {
- public Double[] ds = new Double[2];
- public Boolean[] bs = new Boolean[2];
- public String[] ss = new String[3];
- public ErrorEval ee = null;
- }
+ /**
+ * Converts a standard compare result (-1, 0, 1) to <code>true</code> or <code>false</code>
+ * according to subclass' comparison type.
+ */
+ protected abstract boolean convertComparisonResult(int cmpResult);
+
+ /**
+ * This is a description of how the relational operators apply in MS Excel.
+ * Use this as a guideline when testing/implementing the evaluate methods
+ * for the relational operators Evals.
+ *
+ * <pre>
+ * Bool.TRUE > any number.
+ * Bool > any string. ALWAYS
+ * Bool.TRUE > Bool.FALSE
+ * Bool.FALSE == Blank
+ *
+ * Strings are never converted to numbers or booleans
+ * String > any number. ALWAYS
+ * Non-empty String > Blank
+ * Empty String == Blank
+ * String are sorted dictionary wise
+ *
+ * Blank > Negative numbers
+ * Blank == 0
+ * Blank < Positive numbers
+ * </pre>
+ */
+ public final Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
+ if (operands.length != 2) {
+ return ErrorEval.VALUE_INVALID;
+ }
+
+ ValueEval vA;
+ ValueEval vB;
+ try {
+ vA = OperandResolver.getSingleValue(operands[0], srcRow, srcCol);
+ vB = OperandResolver.getSingleValue(operands[1], srcRow, srcCol);
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+ int cmpResult = doCompare(vA, vB);
+ boolean result = convertComparisonResult(cmpResult);
+ return BoolEval.valueOf(result);
+ }
+
+ private static int doCompare(ValueEval va, ValueEval vb) {
+ // special cases when one operand is blank
+ if (va == BlankEval.INSTANCE) {
+ return compareBlank(vb);
+ }
+ if (vb == BlankEval.INSTANCE) {
+ return -compareBlank(va);
+ }
+
+ if (va instanceof BoolEval) {
+ if (vb instanceof BoolEval) {
+ BoolEval bA = (BoolEval) va;
+ BoolEval bB = (BoolEval) vb;
+ if (bA.getBooleanValue() == bB.getBooleanValue()) {
+ return 0;
+ }
+ return bA.getBooleanValue() ? 1 : -1;
+ }
+ return 1;
+ }
+ if (vb instanceof BoolEval) {
+ return -1;
+ }
+ if (va instanceof StringEval) {
+ if (vb instanceof StringEval) {
+ StringEval sA = (StringEval) va;
+ StringEval sB = (StringEval) vb;
+ return sA.getStringValue().compareTo(sB.getStringValue());
+ }
+ return 1;
+ }
+ if (vb instanceof StringEval) {
+ return -1;
+ }
+ if (va instanceof NumberEval) {
+ if (vb instanceof NumberEval) {
+ NumberEval nA = (NumberEval) va;
+ NumberEval nB = (NumberEval) vb;
+ return Double.compare(nA.getNumberValue(), nB.getNumberValue());
+ }
+ }
+ throw new IllegalArgumentException("Bad operand types (" + va.getClass().getName() + "), ("
+ + vb.getClass().getName() + ")");
+ }
-
- /*
- * This is a description of how the relational operators apply in MS Excel.
- * Use this as a guideline when testing/implementing the evaluate methods
- * for the relational operators Evals.
- *
- * Bool > any number. ALWAYS
- * Bool > any string. ALWAYS
- * Bool.TRUE > Bool.FALSE
- *
- * String > any number. ALWAYS
- * String > Blank. ALWAYS
- * String are sorted dictionary wise
- *
- * Blank == 0 (numeric)
- */
- public RelationalValues doEvaluate(Eval[] operands, int srcRow, short srcCol) {
- RelationalValues retval = new RelationalValues();
-
- switch (operands.length) {
- default:
- retval.ee = ErrorEval.VALUE_INVALID;
- break;
- case 2:
- internalDoEvaluate(operands, srcRow, srcCol, retval, 0);
- internalDoEvaluate(operands, srcRow, srcCol, retval, 1);
- } // end switch
- return retval;
- }
-
- /**
- * convenience method to avoid code duplication for multiple operands
- * @param operands
- * @param srcRow
- * @param srcCol
- * @param retval
- * @param index
- */
- private void internalDoEvaluate(Eval[] operands, int srcRow, short srcCol, RelationalValues retval, int index) {
- if (operands[index] instanceof BoolEval) {
- BoolEval be = (BoolEval) operands[index];
- retval.bs[index] = Boolean.valueOf(be.getBooleanValue());
- }
- else if (operands[index] instanceof NumericValueEval) {
- NumericValueEval ne = (NumericValueEval) operands[index];
- retval.ds[index] = new Double(ne.getNumberValue());
- }
- else if (operands[index] instanceof StringValueEval) {
- StringValueEval se = (StringValueEval) operands[index];
- retval.ss[index] = se.getStringValue();
- }
- else if (operands[index] instanceof RefEval) {
- RefEval re = (RefEval) operands[index];
- ValueEval ve = re.getInnerValueEval();
- if (ve instanceof BoolEval) {
- BoolEval be = (BoolEval) ve;
- retval.bs[index] = Boolean.valueOf(be.getBooleanValue());
- }
- else if (ve instanceof BlankEval) {
- retval.ds[index] = new Double(0);
- }
- else if (ve instanceof NumericValueEval) {
- NumericValueEval ne = (NumericValueEval) ve;
- retval.ds[index] = new Double(ne.getNumberValue());
- }
- else if (ve instanceof StringValueEval) {
- StringValueEval se = (StringValueEval) ve;
- retval.ss[index] = se.getStringValue();
- }
- }
- else if (operands[index] instanceof AreaEval) {
- AreaEval ae = (AreaEval) operands[index];
- if (ae.isRow()) {
- if (ae.containsColumn(srcCol)) {
- ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
- if (ve instanceof BoolEval) {
- BoolEval be = (BoolEval) ve;
- retval.bs[index] = Boolean.valueOf(be.getBooleanValue());
- }
- else if (ve instanceof BlankEval) {
- retval.ds[index] = new Double(0);
- }
- else if (ve instanceof NumericValueEval) {
- NumericValueEval ne = (NumericValueEval) ve;
- retval.ds[index] = new Double(ne.getNumberValue());
- }
- else if (ve instanceof StringValueEval) {
- StringValueEval se = (StringValueEval) ve;
- retval.ss[index] = se.getStringValue();
- }
- else {
- retval.ee = ErrorEval.VALUE_INVALID;
- }
- }
- else {
- retval.ee = ErrorEval.VALUE_INVALID;
- }
- }
- else if (ae.isColumn()) {
- if (ae.containsRow(srcRow)) {
- ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
- if (ve instanceof BoolEval) {
- BoolEval be = (BoolEval) ve;
- retval.bs[index] = Boolean.valueOf(be.getBooleanValue());
- }
- else if (ve instanceof BlankEval) {
- retval.ds[index] = new Double(0);
- }
- else if (ve instanceof NumericValueEval) {
- NumericValueEval ne = (NumericValueEval) ve;
- retval.ds[index] = new Double(ne.getNumberValue());
- }
- else if (ve instanceof StringValueEval) {
- StringValueEval se = (StringValueEval) ve;
- retval.ss[index] = se.getStringValue();
- }
- else {
- retval.ee = ErrorEval.VALUE_INVALID;
- }
- }
- else {
- retval.ee = ErrorEval.VALUE_INVALID;
- }
- }
- else {
- retval.ee = ErrorEval.VALUE_INVALID;
- }
- }
- }
-
- // if both null return 0, else non null wins, else TRUE wins
- protected int doComparison(Boolean[] bs) {
- int retval = 0;
- if (bs[0] != null || bs[1] != null) {
- retval = bs[0] != null
- ? bs[1] != null
- ? bs[0].booleanValue()
- ? bs[1].booleanValue()
- ? 0
- : 1
- : bs[1].booleanValue()
- ? -1
- : 0
- : 1
- : bs[1] != null
- ? -1
- : 0;
- }
- return retval;
- }
+ private static int compareBlank(ValueEval v) {
+ if (v == BlankEval.INSTANCE) {
+ return 0;
+ }
+ if (v instanceof BoolEval) {
+ BoolEval boolEval = (BoolEval) v;
+ return boolEval.getBooleanValue() ? -1 : 0;
+ }
+ if (v instanceof NumberEval) {
+ NumberEval ne = (NumberEval) v;
+ return Double.compare(0, ne.getNumberValue());
+ }
+ if (v instanceof StringEval) {
+ StringEval se = (StringEval) v;
+ return se.getStringValue().length() < 1 ? 0 : -1;
+ }
+ throw new IllegalArgumentException("bad value class (" + v.getClass().getName() + ")");
+ }
- // if both null return 0, else non null wins, else string compare
- protected int doComparison(String[] ss) {
- int retval = 0;
- if (ss[0] != null || ss[1] != null) {
- retval = ss[0] != null
- ? ss[1] != null
- ? ss[0].compareTo(ss[1])
- : 1
- : ss[1] != null
- ? -1
- : 0;
- }
- return retval;
- }
+ public final int getNumberOfOperands() {
+ return 2;
+ }
- // if both null return 0, else non null wins, else doublevalue compare
- protected int doComparison(Double[] ds) {
- int retval = 0;
- if (ds[0] != null || ds[1] != null) {
- retval = ds[0] != null
- ? ds[1] != null
- ? ds[0].compareTo(ds[1])
- : 1
- : ds[1] != null
- ? -1
- : 0;
- }
- return retval;
- }
+ public final int getType() {
+ // TODO - get rid of this method
+ throw new RuntimeException("Obsolete code - should not be called");
+ }
}
-/*
-* 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.
-*/
+/* ====================================================================
+ 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.Ptg;
-import org.apache.poi.hssf.record.formula.SubtractPtg;
-
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
*/
-public final class SubtractEval extends NumericOperationEval {
-
- private SubtractPtg 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 SubtractEval(Ptg ptg) {
- delegate = (SubtractPtg) ptg;
- }
-
- protected ValueEvalToNumericXlator getXlator() {
- return NUM_XLATOR;
- }
+public final class SubtractEval extends TwoOperandNumericOperation {
- public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
- if(args.length != 2) {
- return ErrorEval.VALUE_INVALID;
- }
- Eval retval = null;
- double d0 = 0;
- double d1 = 0;
- ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d0 = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
-
- if (retval == null) { // no error yet
- ve = singleOperandEvaluate(args[1], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d1 = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else {
- retval = ErrorEval.VALUE_INVALID;
- }
- }
-
- if (retval == null) {
- retval = (Double.isNaN(d0) || Double.isNaN(d1))
- ? (ValueEval) ErrorEval.VALUE_INVALID
- : new NumberEval(d0 - d1);
- }
- return retval;
- }
+ public static final OperationEval instance = new SubtractEval();
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
+ private SubtractEval() {
+ }
- public int getType() {
- return delegate.getType();
- }
+ protected double evaluate(double d0, double d1) {
+ return d0 - d1;
+ }
}
--- /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;
+
+/**
+ * @author Josh Micich
+ */
+abstract class TwoOperandNumericOperation implements OperationEval {
+
+ public final int getType() {
+ // TODO - remove
+ throw new RuntimeException("obsolete code should not be called");
+ }
+ protected final double singleOperandEvaluate(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
+ ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+ if (ve instanceof BlankEval) {
+ return 0.0;
+ }
+ return OperandResolver.coerceValueToDouble(ve);
+ }
+
+ public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+ double result;
+ try {
+ double d0 = singleOperandEvaluate(args[0], srcCellRow, srcCellCol);
+ double d1 = singleOperandEvaluate(args[1], srcCellRow, srcCellCol);
+ result = evaluate(d0, d1);
+ if (Double.isNaN(result) || Double.isInfinite(result)) {
+ return ErrorEval.NUM_ERROR;
+ }
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+ return new NumberEval(result);
+ }
+ protected abstract double evaluate(double d0, double d1) throws EvaluationException;
+ public final int getNumberOfOperands() {
+ return 2;
+ }
+}
-/*
-* 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.
-*/
+/* ====================================================================
+ 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.Ptg;
-import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*
*/
-public final class UnaryMinusEval extends NumericOperationEval {
-
- private UnaryMinusPtg 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 UnaryMinusEval(Ptg ptg) {
- this.delegate = (UnaryMinusPtg) ptg;
- }
-
- protected ValueEvalToNumericXlator getXlator() {
- return NUM_XLATOR;
- }
-
- public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
- if(args.length != 1) {
- return ErrorEval.VALUE_INVALID;
- }
- double d = 0;
-
- ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
- if (ve instanceof NumericValueEval) {
- d = ((NumericValueEval) ve).getNumberValue();
- }
- else if (ve instanceof BlankEval) {
- // do nothing
- }
- else if (ve instanceof ErrorEval) {
- return ve;
- }
-
- return new NumberEval(-d);
+public final class UnaryMinusEval implements OperationEval {
+
+ public static final OperationEval instance = new UnaryMinusEval();
+
+ private UnaryMinusEval() {
+ }
+
+ public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
+ if (args.length != 1) {
+ return ErrorEval.VALUE_INVALID;
+ }
+ double d;
+ try {
+ ValueEval ve = OperandResolver.getSingleValue(args[0], srcRow, srcCol);
+ if (ve instanceof BlankEval) {
+ return NumberEval.ZERO;
+ }
+ d = OperandResolver.coerceValueToDouble(ve);
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+ return new NumberEval(-d);
+ }
+
+ public int getNumberOfOperands() {
+ return 1;
+ }
+ public final int getType() {
+ // TODO - remove
+ throw new RuntimeException("obsolete code should not be called");
}
-
- public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
- }
-
- public int getType() {
- return delegate.getType();
- }
-
}
package org.apache.poi.hssf.record.formula.eval;
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
*/
public final class UnaryPlusEval implements OperationEval {
- private UnaryPlusPtg delegate;
+ public static final OperationEval instance = new UnaryPlusEval();
- /**
- * called by reflection
- */
- public UnaryPlusEval(Ptg ptg) {
- this.delegate = (UnaryPlusPtg) ptg;
+ private UnaryPlusEval() {
}
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
}
public int getNumberOfOperands() {
- return delegate.getNumberOfOperands();
+ return 1;
}
public int getType() {
- return delegate.getType();
+ throw new RuntimeException("obsolete code should not be called");
}
}
* @param eval
*/
public ValueEval attemptXlateToNumeric(ValueEval eval) {
- ValueEval retval = null;
if (eval == null) {
- retval = BlankEval.INSTANCE;
+ throw new IllegalArgumentException("eval must not be null");
}
// most common case - least worries :)
- else if (eval instanceof NumberEval) {
- retval = eval;
+ if (eval instanceof NumberEval) {
+ return eval;
}
- // booleval
- else if (eval instanceof BoolEval) {
- retval = ((flags & BOOL_IS_PARSED) > 0)
+ if (eval instanceof BoolEval) {
+ return ((flags & BOOL_IS_PARSED) > 0)
? (NumericValueEval) eval
: xlateBlankEval(BLANK_IS_PARSED);
}
- // stringeval
- else if (eval instanceof StringEval) {
- retval = xlateStringEval((StringEval) eval); // TODO: recursive call needed
+ if (eval instanceof StringEval) {
+ return xlateStringEval((StringEval) eval); // TODO: recursive call needed
}
- // refeval
- else if (eval instanceof RefEval) {
- retval = xlateRefEval((RefEval) eval);
+ if (eval instanceof RefEval) {
+ return xlateRefEval((RefEval) eval);
}
- // erroreval
- else if (eval instanceof ErrorEval) {
- retval = eval;
+ if (eval instanceof ErrorEval) {
+ return eval;
}
- else if (eval instanceof BlankEval) {
- retval = xlateBlankEval(BLANK_IS_PARSED);
+ if (eval instanceof BlankEval) {
+ return xlateBlankEval(BLANK_IS_PARSED);
}
// probably AreaEval? then not acceptable.
- else {
- throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass());
- }
-
- return retval;
+ throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass());
}
/**
AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]);
boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol);
int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createRowVector(tableArray, 0), isRangeLookup);
- ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol);
- int rowIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex);
+ int rowIndex = LookupUtils.resolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol);
ValueVector resultCol = createResultColumnVector(tableArray, rowIndex);
return resultCol.getItem(colIndex);
} catch (EvaluationException e) {
/**
* Returns one column from an <tt>AreaEval</tt>
*
- * @throws EvaluationException (#VALUE!) if colIndex is negative, (#REF!) if colIndex is too high
+ * @param rowIndex assumed to be non-negative
+ *
+ * @throws EvaluationException (#REF!) if colIndex is too high
*/
- private ValueVector createResultColumnVector(AreaEval tableArray, int colIndex) throws EvaluationException {
- if(colIndex < 0) {
- throw EvaluationException.invalidValue();
- }
- if(colIndex >= tableArray.getWidth()) {
+ private ValueVector createResultColumnVector(AreaEval tableArray, int rowIndex) throws EvaluationException {
+ if(rowIndex >= tableArray.getHeight()) {
throw EvaluationException.invalidRef();
}
- return LookupUtils.createRowVector(tableArray, colIndex);
+ return LookupUtils.createRowVector(tableArray, rowIndex);
}
}
* <tr><td><blank></td><td> </td><td>#VALUE!</td></tr>
* </table><br/>
*
- * * Note - out of range errors (both too high and too low) are handled by the caller.
- * @return column or row index as a zero-based value
- *
+ * Note - out of range errors (result index too high) are handled by the caller.
+ * @return column or row index as a zero-based value, never negative.
+ * @throws EvaluationException when the specified arg cannot be coerced to a non-negative integer
*/
- public static int resolveRowOrColIndexArg(ValueEval veRowColIndexArg) throws EvaluationException {
- if(veRowColIndexArg == null) {
+ public static int resolveRowOrColIndexArg(Eval rowColIndexArg, int srcCellRow, int srcCellCol) throws EvaluationException {
+ if(rowColIndexArg == null) {
throw new IllegalArgumentException("argument must not be null");
}
- if(veRowColIndexArg instanceof BlankEval) {
- throw EvaluationException.invalidValue();
+
+ ValueEval veRowColIndexArg;
+ try {
+ veRowColIndexArg = OperandResolver.getSingleValue(rowColIndexArg, srcCellRow, (short)srcCellCol);
+ } catch (EvaluationException e) {
+ // All errors get translated to #REF!
+ throw EvaluationException.invalidRef();
}
- if(veRowColIndexArg instanceof StringEval) {
- StringEval se = (StringEval) veRowColIndexArg;
- String strVal = se.getStringValue();
- Double dVal = OperandResolver.parseDouble(strVal);
- if(dVal == null) {
- // String does not resolve to a number. Raise #VALUE! error.
- throw EvaluationException.invalidRef();
- // This includes text booleans "TRUE" and "FALSE". They are not valid.
+ int oneBasedIndex;
+ if(veRowColIndexArg instanceof BlankEval) {
+ oneBasedIndex = 0;
+ } else {
+ if(veRowColIndexArg instanceof StringEval) {
+ StringEval se = (StringEval) veRowColIndexArg;
+ String strVal = se.getStringValue();
+ Double dVal = OperandResolver.parseDouble(strVal);
+ if(dVal == null) {
+ // String does not resolve to a number. Raise #REF! error.
+ throw EvaluationException.invalidRef();
+ // This includes text booleans "TRUE" and "FALSE". They are not valid.
+ }
+ // else - numeric value parses OK
}
- // else - numeric value parses OK
+ // actual BoolEval values get interpreted as FALSE->0 and TRUE->1
+ oneBasedIndex = OperandResolver.coerceValueToInt(veRowColIndexArg);
+ }
+ if (oneBasedIndex < 1) {
+ // note this is asymmetric with the errors when the index is too large (#REF!)
+ throw EvaluationException.invalidValue();
}
- // actual BoolEval values get interpreted as FALSE->0 and TRUE->1
- return OperandResolver.coerceValueToInt(veRowColIndexArg) - 1;
+ return oneBasedIndex - 1; // convert to zero based
}
return maxIx - 1;
}
- public static LookupValueComparer createLookupComparer(ValueEval lookupValue) throws EvaluationException {
+ public static LookupValueComparer createLookupComparer(ValueEval lookupValue) {
- if (lookupValue instanceof BlankEval) {
- // blank eval can never be found in a lookup array
- throw new EvaluationException(ErrorEval.NA);
+ if (lookupValue == BlankEval.INSTANCE) {
+ // blank eval translates to zero
+ // Note - a blank eval in the lookup column/row never matches anything
+ // empty string in the lookup column/row can only be matched by explicit emtpty string
+ return new NumberLookupComparer(NumberEval.ZERO);
}
if (lookupValue instanceof StringEval) {
return new StringLookupComparer((StringEval) lookupValue);
AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]);
boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol);
int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createColumnVector(tableArray, 0), isRangeLookup);
- ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol);
- int colIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex);
+ int colIndex = LookupUtils.resolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol);
ValueVector resultCol = createResultColumnVector(tableArray, colIndex);
return resultCol.getItem(rowIndex);
} catch (EvaluationException e) {
/**
* Returns one column from an <tt>AreaEval</tt>
*
- * @throws EvaluationException (#VALUE!) if colIndex is negative, (#REF!) if colIndex is too high
+ * @param colIndex assumed to be non-negative
+ *
+ * @throws EvaluationException (#REF!) if colIndex is too high
*/
private ValueVector createResultColumnVector(AreaEval tableArray, int colIndex) throws EvaluationException {
- if(colIndex < 0) {
- throw EvaluationException.invalidValue();
- }
if(colIndex >= tableArray.getWidth()) {
throw EvaluationException.invalidRef();
}
import org.apache.poi.ss.usermodel.Name;
/**
- * High Level Representation of a 'defined name' which could be a 'built-in' name,
+ * High Level Representation of a 'defined name' which could be a 'built-in' name,
* 'named range' or name of a user defined function.
- *
+ *
* @author Libin Roman (Vista Portal LDT. Developer)
*/
public class HSSFName implements Name {
private HSSFWorkbook _book;
private NameRecord _definedNameRec;
-
+
/** Creates new HSSFName - called by HSSFWorkbook to create a sheet from
* scratch.
*
_book = book;
_definedNameRec = name;
}
-
+
/** Get the sheets name which this named range is referenced to
* @return sheet name, which this named range referred to
- */
+ */
public String getSheetName() {
short indexToExternSheet = _definedNameRec.getExternSheetNumber();
-
+
return _book.getWorkbook().findSheetNameFromExternSheet(indexToExternSheet);
}
-
- /**
+
+ /**
* @return text name of this defined name
- */
+ */
public String getNameName(){
return _definedNameRec.getNameText();
}
-
- /**
+
+ /**
* sets the name of the named range
* @param nameName named range name to set
- */
+ */
public void setNameName(String nameName){
_definedNameRec.setNameText(nameName);
- _definedNameRec.setNameTextLength((byte)nameName.length());
Workbook wb = _book.getWorkbook();
-
+
//Check to ensure no other names have the same case-insensitive name
for ( int i = wb.getNumNames()-1; i >=0; i-- )
{
- NameRecord rec = wb.getNameRecord(i);
- if (rec != _definedNameRec) {
- if (rec.getNameText().equalsIgnoreCase(getNameName()))
- throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)");
- }
+ NameRecord rec = wb.getNameRecord(i);
+ if (rec != _definedNameRec) {
+ if (rec.getNameText().equalsIgnoreCase(getNameName()))
+ throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)");
+ }
}
}
- /**
+ /**
* Note - this method only applies to named ranges
* @return the formula text defining the named range
- */
+ */
public String getReference() {
- if (_definedNameRec.isFunctionName()) {
- throw new IllegalStateException("Only applicable to named ranges");
- }
+ if (_definedNameRec.isFunctionName()) {
+ throw new IllegalStateException("Only applicable to named ranges");
+ }
return _definedNameRec.getAreaReference(_book);
}
- /**
+ /**
* sets the sheet name which this named range referenced to
* @param sheetName the sheet name of the reference
- */
+ */
private void setSheetName(String sheetName){
int sheetNumber = _book.getSheetIndex(sheetName);
short externSheetNumber = (short)
_definedNameRec.setExternSheetNumber(externSheetNumber);
}
-
- /**
+
+ /**
* sets the reference of this named range
* @param ref the reference to set
- */
+ */
public void setReference(String ref){
RangeAddress ra = new RangeAddress(ref);
setSheetName(sheetName);
}
- //allow the poi utilities to parse it out
+ //allow the poi utilities to parse it out
_definedNameRec.setAreaReference(ref);
}
return "#REF!".endsWith(ref);
}
public boolean isFunctionName() {
- return _definedNameRec.isFunctionName();
+ return _definedNameRec.isFunctionName();
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
-import java.util.Stack;
import org.apache.poi.POIDocument;
import org.apache.poi.ddf.EscherBSERecord;
import org.apache.poi.ddf.EscherRecord;
import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook;
+import org.apache.poi.hssf.model.DrawingManager2;
import org.apache.poi.hssf.record.AbstractEscherHolderRecord;
import org.apache.poi.hssf.record.BackupRecord;
import org.apache.poi.hssf.record.DrawingGroupRecord;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.MemFuncPtg;
import org.apache.poi.hssf.record.formula.NameXPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
+import org.apache.poi.hssf.record.formula.Ref3DPtg;
import org.apache.poi.hssf.record.formula.UnionPtg;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.hssf.util.SheetReferences;
* @return HSSFSheet representing the cloned sheet.
*/
- public HSSFSheet cloneSheet(int sheetNum) {
- validateSheetIndex(sheetNum);
- HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetNum);
- String srcName = workbook.getSheetName(sheetNum);
+ public HSSFSheet cloneSheet(int sheetIndex) {
+ validateSheetIndex(sheetIndex);
+ HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetIndex);
+ String srcName = workbook.getSheetName(sheetIndex);
HSSFSheet clonedSheet = srcSheet.cloneSheet(this);
clonedSheet.setSelected(false);
clonedSheet.setActive(false);
+ String name = getUniqueSheetName(srcName);
+ int newSheetIndex = _sheets.size();
_sheets.add(clonedSheet);
- int i = 1;
+ workbook.setSheetName(newSheetIndex, name);
+
+ // Check this sheet has an autofilter, (which has a built-in NameRecord at workbook level)
+ int filterDbNameIndex = findExistingBuiltinNameRecordIdx(sheetIndex, NameRecord.BUILTIN_FILTER_DB);
+ if (filterDbNameIndex >=0) {
+ NameRecord origNameRecord = workbook.getNameRecord(filterDbNameIndex);
+ // copy original formula but adjust 3D refs to the new external sheet index
+ int newExtSheetIx = getExternalSheetIndex(newSheetIndex);
+ Ptg[] ptgs = origNameRecord.getNameDefinition();
+ for (int i=0; i< ptgs.length; i++) {
+ Ptg ptg = ptgs[i];
+ ptg = ptg.copy();
+
+ if (ptg instanceof Area3DPtg) {
+ Area3DPtg a3p = (Area3DPtg) ptg;
+ a3p.setExternSheetIndex(newExtSheetIx);
+ } else if (ptg instanceof Ref3DPtg) {
+ Ref3DPtg r3p = (Ref3DPtg) ptg;
+ r3p.setExternSheetIndex(newExtSheetIx);
+ }
+ ptgs[i] = ptg;
+ }
+ NameRecord newNameRecord = workbook.createBuiltInName(NameRecord.BUILTIN_FILTER_DB, newSheetIndex+1);
+ newNameRecord.setNameDefinition(ptgs);
+ newNameRecord.setHidden(true);
+ HSSFName newName = new HSSFName(this, newNameRecord);
+ names.add(newName);
+
+ workbook.cloneDrawings(clonedSheet.getSheet());
+ }
+ // TODO - maybe same logic required for other/all built-in name records
+
+ return clonedSheet;
+ }
+
+ private String getUniqueSheetName(String srcName) {
+ int uniqueIndex = 2;
+ String baseName = srcName;
+ int bracketPos = srcName.lastIndexOf('(');
+ if (bracketPos > 0 && srcName.endsWith(")")) {
+ String suffix = srcName.substring(bracketPos + 1, srcName.length() - ")".length());
+ try {
+ uniqueIndex = Integer.parseInt(suffix.trim());
+ uniqueIndex++;
+ baseName=srcName.substring(0, bracketPos).trim();
+ } catch (NumberFormatException e) {
+ // contents of brackets not numeric
+ }
+ }
while (true) {
// Try and find the next sheet name that is unique
- String name = srcName;
- String index = Integer.toString(i++);
- if (name.length() + index.length() + 2 < 31) {
- name = name + "(" + index + ")";
+ String index = Integer.toString(uniqueIndex++);
+ String name;
+ if (baseName.length() + index.length() + 2 < 31) {
+ name = baseName + " (" + index + ")";
} else {
- name = name.substring(0, 31 - index.length() - 2) + "(" + index + ")";
+ name = baseName.substring(0, 31 - index.length() - 2) + "(" + index + ")";
}
//If the sheet name is unique, then set it otherwise move on to the next number.
if (workbook.getSheetIndex(name) == -1) {
- workbook.setSheetName(_sheets.size()-1, name);
- break;
+ return name;
}
}
- return clonedSheet;
}
/**
boolean removingRange =
startColumn == -1 && endColumn == -1 && startRow == -1 && endRow == -1;
- int rowColHeaderNameIndex = findExistingRowColHeaderNameRecordIdx(sheetIndex);
+ int rowColHeaderNameIndex = findExistingBuiltinNameRecordIdx(sheetIndex, NameRecord.BUILTIN_PRINT_TITLE);
if (removingRange) {
if (rowColHeaderNameIndex >= 0) {
workbook.removeName(rowColHeaderNameIndex);
isNewRecord = false;
}
- short definitionTextLength = settingRowAndColumn ? (short)0x001a : (short)0x000b;
- nameRecord.setDefinitionTextLength(definitionTextLength); // TODO - remove
-
- Stack ptgs = new Stack();
+ List temp = new ArrayList();
if (settingRowAndColumn) {
final int exprsSize = 2 * 11 + 1; // 2 * Area3DPtg.SIZE + UnionPtg.SIZE
- ptgs.add(new MemFuncPtg(exprsSize));
+ temp.add(new MemFuncPtg(exprsSize));
}
if (startColumn >= 0) {
Area3DPtg colArea = new Area3DPtg(0, MAX_ROW, startColumn, endColumn,
false, false, false, false, externSheetIndex);
- ptgs.add(colArea);
+ temp.add(colArea);
}
if (startRow >= 0) {
Area3DPtg rowArea = new Area3DPtg(startRow, endRow, 0, MAX_COLUMN,
false, false, false, false, externSheetIndex);
- ptgs.add(rowArea);
+ temp.add(rowArea);
}
- if (settingRowAndColumn)
- {
- ptgs.add(UnionPtg.instance);
+ if (settingRowAndColumn) {
+ temp.add(UnionPtg.instance);
}
+ Ptg[] ptgs = new Ptg[temp.size()];
+ temp.toArray(ptgs);
nameRecord.setNameDefinition(ptgs);
if (isNewRecord)
}
- private int findExistingRowColHeaderNameRecordIdx(int sheetIndex) {
+ private int findExistingBuiltinNameRecordIdx(int sheetIndex, byte builtinCode) {
for(int defNameIndex =0; defNameIndex<names.size(); defNameIndex++) {
NameRecord r = workbook.getNameRecord(defNameIndex);
if (r == null) {
throw new RuntimeException("Unable to find all defined names to iterate over");
}
- if (!isRowColHeaderRecord( r )) {
+ if (!r.isBuiltInName() || r.getBuiltInName() != builtinCode) {
continue;
}
if(r.getSheetNumber() == 0) {
return -1;
}
- private static boolean isRowColHeaderRecord(NameRecord r) {
- return r.isBuiltInName() && r.getBuiltInName() == NameRecord.BUILTIN_PRINT_TITLE;
- }
-
/**
* create a new Font and add it to the workbook's font table
* @return new font object
return result;
}
+ public NameRecord getNameRecord(int nameIndex) {
+ return getWorkbook().getNameRecord(nameIndex);
+ }
/** gets the named range name
* @param index the named range index (0 based)
--- /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 };
+ // TODO - use singleton instances directly instead of reflection
+ private static final Map _constructorsByPtgClass = initialiseConstructorsMap();
+ private static final Map _instancesByPtgClass = initialiseInstancesMap();
+
+ private OperationEvaluatorFactory() {
+ // no instances of this class
+ }
+
+ private static Map initialiseConstructorsMap() {
+ Map m = new HashMap(32);
+ add(m, ConcatPtg.class, ConcatEval.class);
+ add(m, FuncPtg.class, FuncVarEval.class);
+ add(m, FuncVarPtg.class, FuncVarEval.class);
+ return m;
+ }
+ private static Map initialiseInstancesMap() {
+ Map m = new HashMap(32);
+ add(m, EqualPtg.class, EqualEval.instance);
+ add(m, GreaterEqualPtg.class, GreaterEqualEval.instance);
+ add(m, GreaterThanPtg.class, GreaterThanEval.instance);
+ add(m, LessEqualPtg.class, LessEqualEval.instance);
+ add(m, LessThanPtg.class, LessThanEval.instance);
+ add(m, NotEqualPtg.class, NotEqualEval.instance);
+
+ add(m, AddPtg.class, AddEval.instance);
+ add(m, DividePtg.class, DivideEval.instance);
+ add(m, MultiplyPtg.class, MultiplyEval.instance);
+ add(m, PercentPtg.class, PercentEval.instance);
+ add(m, PowerPtg.class, PowerEval.instance);
+ add(m, SubtractPtg.class, SubtractEval.instance);
+ add(m, UnaryMinusPtg.class, UnaryMinusEval.instance);
+ add(m, UnaryPlusPtg.class, UnaryPlusEval.instance);
+ return m;
+ }
+
+ private static void add(Map m, Class ptgClass, OperationEval evalInstance) {
+ if(!Ptg.class.isAssignableFrom(ptgClass)) {
+ throw new IllegalArgumentException("Expected Ptg subclass");
+ }
+ m.put(ptgClass, evalInstance);
+ }
+
+ 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");
+ }
+ Object result;
+
+ Class ptgClass = ptg.getClass();
+
+ result = _instancesByPtgClass.get(ptgClass);
+ if (result != null) {
+ return (OperationEval) result;
+ }
+
+
+ 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[] 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;
+ }
+}
--- /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.ss.usermodel;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+
+/**
+ * Performance optimisation for {@link HSSFFormulaEvaluator}. This class stores previously
+ * calculated values of already visited cells, to avoid unnecessary re-calculation when the
+ * same cells are referenced multiple times
+ *
+ *
+ * @author Josh Micich
+ */
+final class EvaluationCache {
+ private static final class Key {
+
+ private final int _sheetIndex;
+ private final int _srcRowNum;
+ private final int _srcColNum;
+ private final int _hashCode;
+
+ public Key(int sheetIndex, int srcRowNum, int srcColNum) {
+ _sheetIndex = sheetIndex;
+ _srcRowNum = srcRowNum;
+ _srcColNum = srcColNum;
+ _hashCode = sheetIndex + srcRowNum + srcColNum;
+ }
+
+ public int hashCode() {
+ return _hashCode;
+ }
+
+ public boolean equals(Object obj) {
+ Key other = (Key) obj;
+ if (_hashCode != other._hashCode) {
+ return false;
+ }
+ if (_sheetIndex != other._sheetIndex) {
+ return false;
+ }
+ if (_srcRowNum != other._srcRowNum) {
+ return false;
+ }
+ if (_srcColNum != other._srcColNum) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ private final Map _valuesByKey;
+
+ /* package */EvaluationCache() {
+ _valuesByKey = new HashMap();
+ }
+
+ public ValueEval getValue(int sheetIndex, int srcRowNum, int srcColNum) {
+ Key key = new Key(sheetIndex, srcRowNum, srcColNum);
+ return (ValueEval) _valuesByKey.get(key);
+ }
+
+ public void setValue(int sheetIndex, int srcRowNum, int srcColNum, ValueEval value) {
+ Key key = new Key(sheetIndex, srcRowNum, srcColNum);
+ if (_valuesByKey.containsKey(key)) {
+ throw new RuntimeException("Already have cached value for this cell");
+ }
+ _valuesByKey.put(key, value);
+ }
+
+ /**
+ * Should be called whenever there are changes to input cells in the evaluated workbook.
+ */
+ public void clear() {
+ _valuesByKey.clear();
+ }
+}
import java.util.Iterator;
import java.util.Stack;
+import org.apache.poi.ss.util.AreaReference;
+import org.apache.poi.ss.util.CellReference;
+
import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.BoolPtg;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
+ * Evaluates formula cells.<p/>
+ *
+ * For performance reasons, this class keeps a cache of all previously calculated intermediate
+ * cell values. Be sure to call {@link #clearCache()} if any workbook cells are changed between
+ * calls to evaluate~ methods on this class.
+ *
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
+ * @author Josh Micich
*/
public class FormulaEvaluator {
+ /**
+ * used to track the number of evaluations
+ */
+ private static final class Counter {
+ public int value;
+ public Counter() {
+ value = 0;
+ }
+ }
protected Sheet _sheet;
protected Workbook _workbook;
+ private final EvaluationCache _cache;
+
+ private Counter _evaluationCounter;
+
public FormulaEvaluator(Sheet sheet, Workbook workbook) {
+ this(sheet, workbook, new EvaluationCache(), new Counter());
+ }
+
+ private FormulaEvaluator(Sheet sheet, Workbook workbook, EvaluationCache cache, Counter evaluationCounter) {
_sheet = sheet;
_workbook = workbook;
+ _cache = cache;
+ _evaluationCounter = evaluationCounter;
+ }
+
+ /**
+ * for debug use. Used in toString methods
+ */
+ public String getSheetName(Sheet sheet) {
+ return _workbook.getSheetName(_workbook.getSheetIndex(sheet));
+ }
+ /**
+ * for debug/test use
+ */
+ public int getEvaluationCount() {
+ return _evaluationCounter.value;
+ }
+
+ private static boolean isDebugLogEnabled() {
+ return false;
+ }
+ private static void logDebug(String s) {
+ if (isDebugLogEnabled()) {
+ System.out.println(s);
+ }
}
/**
*/
public void setCurrentRow(Row row) {
// do nothing
+ if (false) {
+ row.getClass(); // suppress unused parameter compiler warning
+ }
+ }
+
+ /**
+ * Should be called whenever there are changes to input cells in the evaluated workbook.
+ * Failure to call this method after changing cell values will cause incorrect behaviour
+ * of the evaluate~ methods of this class
+ */
+ public void clearCache() {
+ _cache.clear();
}
retval.setErrorValue(cell.getErrorCellValue());
break;
case Cell.CELL_TYPE_FORMULA:
- retval = getCellValueForEval(internalEvaluate(cell, _sheet, _workbook), _workbook.getCreationHelper());
+ retval = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper());
break;
case Cell.CELL_TYPE_NUMERIC:
retval = new CellValue(Cell.CELL_TYPE_NUMERIC, _workbook.getCreationHelper());
if (cell != null) {
switch (cell.getCellType()) {
case Cell.CELL_TYPE_FORMULA:
- CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet, _workbook), _workbook.getCreationHelper());
+ CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper());
switch (cv.getCellType()) {
case Cell.CELL_TYPE_BOOLEAN:
cell.setCellValue(cv.getBooleanValue());
if (cell != null) {
switch (cell.getCellType()) {
case Cell.CELL_TYPE_FORMULA:
- CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet, _workbook), _workbook.getCreationHelper());
+ CellValue cv = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper());
switch (cv.getCellType()) {
case Cell.CELL_TYPE_BOOLEAN:
cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
* else a runtime exception will be thrown somewhere inside the method.
* (Hence this is a private method.)
*/
- private static ValueEval internalEvaluate(Cell srcCell, Sheet sheet, Workbook workbook) {
+ private ValueEval internalEvaluate(Cell srcCell, Sheet sheet) {
int srcRowNum = srcCell.getRowIndex();
- short srcColNum = srcCell.getCellNum();
+ int srcColNum = srcCell.getCellNum();
+ ValueEval result;
+
+ int sheetIndex = _workbook.getSheetIndex(sheet);
+ result = _cache.getValue(sheetIndex, srcRowNum, srcColNum);
+ if (result != null) {
+ return result;
+ }
+ _evaluationCounter.value++;
EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
- if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) {
+ if(!tracker.startEvaluate(_workbook, sheet, srcRowNum, srcColNum)) {
return ErrorEval.CIRCULAR_REF_ERROR;
}
try {
- return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula());
+ result = evaluateCell(srcRowNum, (short)srcColNum, srcCell.getCellFormula());
} finally {
- tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
+ tracker.endEvaluate(_workbook, sheet, srcRowNum, srcColNum);
+ _cache.setValue(sheetIndex, srcRowNum, srcColNum, result);
}
+ if (isDebugLogEnabled()) {
+ String sheetName = _workbook.getSheetName(sheetIndex);
+ CellReference cr = new CellReference(srcRowNum, srcColNum);
+ logDebug("Evaluated " + sheetName + "!" + cr.formatAsString() + " to " + result.toString());
+ }
+ return result;
}
- private static ValueEval evaluateCell(Workbook workbook, Sheet sheet,
- int srcRowNum, short srcColNum, String cellFormulaText) {
+ private ValueEval evaluateCell(int srcRowNum, short srcColNum, String cellFormulaText) {
- Ptg[] ptgs = FormulaParser.parse(cellFormulaText, workbook);
+ Ptg[] ptgs = FormulaParser.parse(cellFormulaText, _workbook);
Stack stack = new Stack();
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
continue;
}
if (ptg instanceof MemErrPtg) { continue; }
- if (ptg instanceof MissingArgPtg) { continue; }
- if (ptg instanceof NamePtg) {
- // named ranges, macro functions
- NamePtg namePtg = (NamePtg) ptg;
- stack.push(new NameEval(namePtg.getIndex()));
- continue;
+ if (ptg instanceof MissingArgPtg) {
+ // TODO - might need to push BlankEval or MissingArgEval
+ continue;
}
- if (ptg instanceof NameXPtg) {
- NameXPtg nameXPtg = (NameXPtg) ptg;
- stack.push(new NameXEval(nameXPtg.getSheetRefIndex(), nameXPtg.getNameIndex()));
- continue;
- }
- if (ptg instanceof UnknownPtg) { continue; }
Eval opResult;
if (ptg instanceof OperationPtg) {
OperationPtg optg = (OperationPtg) ptg;
Eval p = (Eval) stack.pop();
ops[j] = p;
}
- opResult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
+ logDebug("invoke " + operation + " (nAgs=" + numops + ")");
+ opResult = invokeOperation(operation, ops, srcRowNum, srcColNum, _workbook, _sheet);
} else {
- opResult = getEvalForPtg(ptg, sheet, workbook);
+ opResult = getEvalForPtg(ptg, _sheet);
+ }
+ if (opResult == null) {
+ throw new RuntimeException("Evaluation result must not be null");
}
+ logDebug("push " + opResult);
stack.push(opResult);
}
return operation.evaluate(ops, srcRowNum, srcColNum);
}
+ private Sheet getOtherSheet(int externSheetIndex) {
+ return _workbook.getSheetAt(_workbook.getSheetIndexFromExternSheetIndex(externSheetIndex));
+ }
+ private FormulaEvaluator createEvaluatorForAnotherSheet(Sheet sheet) {
+ return new FormulaEvaluator(sheet, _workbook, _cache, _evaluationCounter);
+ }
+
/**
* returns an appropriate Eval impl instance for the Ptg. The Ptg must be
* one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,
* StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
* passed here!
*/
- private static Eval getEvalForPtg(Ptg ptg, Sheet sheet, Workbook workbook) {
+ private Eval getEvalForPtg(Ptg ptg, Sheet sheet) {
+ if (ptg instanceof NamePtg) {
+ // named ranges, macro functions
+ NamePtg namePtg = (NamePtg) ptg;
+ int numberOfNames = _workbook.getNumberOfNames();
+ int nameIndex = namePtg.getIndex();
+ if(nameIndex < 0 || nameIndex >= numberOfNames) {
+ throw new RuntimeException("Bad name index (" + nameIndex
+ + "). Allowed range is (0.." + (numberOfNames-1) + ")");
+ }
+ if(_workbook instanceof org.apache.poi.hssf.usermodel.HSSFWorkbook) {
+ org.apache.poi.hssf.usermodel.HSSFWorkbook hssfWb =
+ (org.apache.poi.hssf.usermodel.HSSFWorkbook)_workbook;
+ NameRecord nameRecord = hssfWb.getNameRecord(nameIndex);
+ if (nameRecord.isFunctionName()) {
+ return new NameEval(nameRecord.getNameText());
+ }
+ if (nameRecord.hasFormula()) {
+ return evaluateNameFormula(nameRecord.getNameDefinition(), sheet);
+ }
+ throw new RuntimeException("Don't know how to evalate name '" + nameRecord.getNameText() + "'");
+ } else {
+ throw new RuntimeException("Don't know how to evaluate name records for XSSF");
+ }
+ }
+ if (ptg instanceof NameXPtg) {
+ NameXPtg nameXPtg = (NameXPtg) ptg;
+ return new NameXEval(nameXPtg.getSheetRefIndex(), nameXPtg.getNameIndex());
+ }
if (ptg instanceof RefPtg) {
- return new LazyRefEval(((RefPtg) ptg), sheet, workbook);
+ return new LazyRefEval(((RefPtg) ptg), sheet, this);
}
if (ptg instanceof Ref3DPtg) {
Ref3DPtg refPtg = (Ref3DPtg) ptg;
- Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex()));
- return new LazyRefEval(refPtg, xsheet, workbook);
+ Sheet xsheet = getOtherSheet(refPtg.getExternSheetIndex());
+ return new LazyRefEval(refPtg, xsheet, createEvaluatorForAnotherSheet(xsheet));
}
if (ptg instanceof AreaPtg) {
- return new LazyAreaEval(((AreaPtg) ptg), sheet, workbook);
+ return new LazyAreaEval(((AreaPtg) ptg), sheet, this);
}
if (ptg instanceof Area3DPtg) {
Area3DPtg a3dp = (Area3DPtg) ptg;
- Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex()));
- return new LazyAreaEval(a3dp, xsheet, workbook);
+ Sheet xsheet = getOtherSheet(a3dp.getExternSheetIndex());
+ return new LazyAreaEval(a3dp, xsheet, createEvaluatorForAnotherSheet(xsheet));
}
if (ptg instanceof IntPtg) {
if (ptg instanceof ErrPtg) {
return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
}
+ if (ptg instanceof UnknownPtg) {
+ // TODO - remove UnknownPtg
+ throw new RuntimeException("UnknownPtg not allowed");
+ }
throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")");
}
+ private Eval evaluateNameFormula(Ptg[] ptgs, Sheet sheet) {
+ if (ptgs.length > 1) {
+ throw new RuntimeException("Complex name formulas not supported yet");
+ }
+ return getEvalForPtg(ptgs[0], sheet);
+ }
+
/**
* Given a cell, find its type and from that create an appropriate ValueEval
* impl instance and return that. Since the cell could be an external
* @param sheet
* @param workbook
*/
- public static ValueEval getEvalForCell(Cell cell, Sheet sheet, Workbook workbook) {
+ public ValueEval getEvalForCell(Cell cell, Sheet sheet) {
if (cell == null) {
return BlankEval.INSTANCE;
case Cell.CELL_TYPE_STRING:
return new StringEval(cell.getRichStringCellValue().getString());
case Cell.CELL_TYPE_FORMULA:
- return internalEvaluate(cell, sheet, workbook);
+ return internalEvaluate(cell, sheet);
case Cell.CELL_TYPE_BOOLEAN:
return BoolEval.valueOf(cell.getBooleanCellValue());
case Cell.CELL_TYPE_BLANK:
*/
final class OperationEvaluatorFactory {
private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
-
+ // TODO - use singleton instances directly instead of reflection
private static final Map _constructorsByPtgClass = initialiseConstructorsMap();
+ private static final Map _instancesByPtgClass = initialiseInstancesMap();
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 Map initialiseInstancesMap() {
+ Map m = new HashMap(32);
+ add(m, EqualPtg.class, EqualEval.instance);
+ add(m, GreaterEqualPtg.class, GreaterEqualEval.instance);
+ add(m, GreaterThanPtg.class, GreaterThanEval.instance);
+ add(m, LessEqualPtg.class, LessEqualEval.instance);
+ add(m, LessThanPtg.class, LessThanEval.instance);
+ add(m, NotEqualPtg.class, NotEqualEval.instance);
+
+ add(m, AddPtg.class, AddEval.instance);
+ add(m, DividePtg.class, DivideEval.instance);
+ add(m, MultiplyPtg.class, MultiplyEval.instance);
+ add(m, PercentPtg.class, PercentEval.instance);
+ add(m, PowerPtg.class, PowerEval.instance);
+ add(m, SubtractPtg.class, SubtractEval.instance);
+ add(m, UnaryMinusPtg.class, UnaryMinusEval.instance);
+ add(m, UnaryPlusPtg.class, UnaryPlusEval.instance);
+ return m;
+ }
+
+ private static void add(Map m, Class ptgClass, OperationEval evalInstance) {
+ if(!Ptg.class.isAssignableFrom(ptgClass)) {
+ throw new IllegalArgumentException("Expected Ptg subclass");
+ }
+ m.put(ptgClass, evalInstance);
+ }
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");
}
}
m.put(ptgClass, constructor);
}
-
+
/**
* returns the OperationEval concrete impl instance corresponding
* to the supplied operationPtg
if(ptg == null) {
throw new IllegalArgumentException("ptg must not be null");
}
+ Object result;
Class ptgClass = ptg.getClass();
+ result = _instancesByPtgClass.get(ptgClass);
+ if (result != null) {
+ return (OperationEval) result;
+ }
+
+
Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass);
if(constructor == null) {
if(ptgClass == ExpPtg.class) {
throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")");
}
- Object result;
Object[] initargs = { ptg };
try {
result = constructor.newInstance(initargs);
}
String[] parts = separateAreaRefs(reference);
+ String part0 = parts[0];
+ if (parts.length == 1) {
+ // TODO - probably shouldn't initialize area ref when text is really a cell ref
+ // Need to fix some named range stuff to get rid of this
+ _firstCell = new CellReference(part0);
+
+ _lastCell = _firstCell;
+ _isSingleCell = true;
+ return;
+ }
+ if (parts.length != 2) {
+ throw new IllegalArgumentException("Bad area ref '" + reference + "'");
+ }
- // Special handling for whole-column references
- if(parts.length == 2 && parts[0].length() == 1 &&
- parts[1].length() == 1 &&
- parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' &&
- parts[1].charAt(0) >= 'A' && parts[1].charAt(0) <= 'Z') {
+ String part1 = parts[1];
+ if (isPlainColumn(part0)) {
+ if (!isPlainColumn(part1)) {
+ throw new RuntimeException("Bad area ref '" + reference + "'");
+ }
+ // Special handling for whole-column references
// Represented internally as x$1 to x$65536
// which is the maximum range of rows
- parts[0] = parts[0] + "$1";
- parts[1] = parts[1] + "$65536";
- }
-
- _firstCell = new CellReference(parts[0]);
-
- if(parts.length == 2) {
- _lastCell = new CellReference(parts[1]);
+
+ boolean firstIsAbs = CellReference.isPartAbsolute(part0);
+ boolean lastIsAbs = CellReference.isPartAbsolute(part1);
+
+ int col0 = CellReference.convertColStringToIndex(part0);
+ int col1 = CellReference.convertColStringToIndex(part1);
+
+ _firstCell = new CellReference(0, col0, true, firstIsAbs);
+ _lastCell = new CellReference(0xFFFF, col1, true, lastIsAbs);
_isSingleCell = false;
+ // TODO - whole row refs
} else {
- _lastCell = _firstCell;
- _isSingleCell = true;
+ _firstCell = new CellReference(part0);
+ _lastCell = new CellReference(part1);
+ _isSingleCell = part0.equals(part1);
+ }
+ }
+
+ private boolean isPlainColumn(String refPart) {
+ for(int i=refPart.length()-1; i>=0; i--) {
+ int ch = refPart.charAt(i);
+ if (ch == '$' && i==0) {
+ continue;
+ }
+ if (ch < 'A' || ch > 'Z') {
+ return false;
+ }
}
+ return true;
}
-
+
/**
* Creates an area ref from a pair of Cell References.
*/
if (_isColAbs) {
colRef=colRef.substring(1);
}
- _colIndex = convertColStringToNum(colRef);
+ _colIndex = convertColStringToIndex(colRef);
String rowRef=parts[2];
if (rowRef.length() < 1) {
}
public CellReference(int pRow, int pCol) {
- this(pRow, pCol, false, false);
+ this(pRow, pCol & 0xFFFF, false, false);
}
public CellReference(int pRow, short pCol) {
this(pRow, (int)pCol, false, false);
public String getSheetName(){
return _sheetName;
}
-
+
+ public static boolean isPartAbsolute(String part) {
+ return part.charAt(0) == ABSOLUTE_REFERENCE_MARKER;
+ }
+
/**
* takes in a column reference portion of a CellRef and converts it from
* ALPHA-26 number format to 0-based base 10.
+ * 'A' -> 0
+ * 'Z' -> 25
+ * 'AA' -> 26
+ * 'IV' -> 255
+ * @return zero based column index
*/
- private int convertColStringToNum(String ref) {
- int lastIx = ref.length()-1;
- int retval=0;
- int pos = 0;
-
- for (int k = lastIx; k > -1; k--) {
+ protected static int convertColStringToIndex(String ref) {
+ int pos = 0;
+ int retval=0;
+ for (int k = ref.length()-1; k >= 0; k--) {
char thechar = ref.charAt(k);
+ if (thechar == ABSOLUTE_REFERENCE_MARKER) {
+ if (k != 0) {
+ throw new IllegalArgumentException("Bad col ref format '" + ref + "'");
+ }
+ break;
+ }
// Character.getNumericValue() returns the values
// 10-35 for the letter A-Z
int shift = (int)Math.pow(26, pos);
* This will normally return an array of size 2 or 3
*/
public SlideListWithText[] getSlideListWithTexts() { return slwts; }
- /**
+
+ /**
* Returns the SlideListWithText that deals with the
* Master Slides
*/
public SlideListWithText getMasterSlideListWithText() {
- if(slwts.length > 0) { return slwts[0]; }
- return null; }
+ for (int i = 0; i < slwts.length; i++) {
+ if(slwts[i].getInstance() == SlideListWithText.MASTER) {
+ return slwts[i];
+ }
+ }
+ return null;
+ }
+
/**
* Returns the SlideListWithText that deals with the
* Slides, or null if there isn't one
*/
- public SlideListWithText getSlideSlideListWithText() {
- if(slwts.length > 1) { return slwts[1]; }
- return null; }
+ public SlideListWithText getSlideSlideListWithText() {
+ for (int i = 0; i < slwts.length; i++) {
+ if(slwts[i].getInstance() == SlideListWithText.SLIDES) {
+ return slwts[i];
+ }
+ }
+ return null;
+ }
/**
* Returns the SlideListWithText that deals with the
* notes, or null if there isn't one
*/
public SlideListWithText getNotesSlideListWithText() {
- if(slwts.length > 2) { return slwts[2]; }
- return null; }
+ for (int i = 0; i < slwts.length; i++) {
+ if(slwts[i].getInstance() == SlideListWithText.NOTES) {
+ return slwts[i];
+ }
+ }
+ return null;
+ }
/**
moveChildRecords(oldLoc, newLoc, number);
}
}
-
-
+
+ /**
+ * Set child records.
+ *
+ * @param records the new child records
+ */
+ public void setChildRecord(Record[] records) {
+ this._children = records;
+ }
+
/* ===============================================================
* External Serialisation Methods
* ===============================================================
// For now, pretend to be an atom
public class SlideListWithText extends RecordContainer
{
- private byte[] _header;
+
+ /**
+ * Instance filed of the record header indicates that this SlideListWithText stores
+ * references to slides
+ */
+ public static final int SLIDES = 0;
+ /**
+ * Instance filed of the record header indicates that this SlideListWithText stores
+ * references to master slides
+ */
+ public static final int MASTER = 1;
+ /**
+ * Instance filed of the record header indicates that this SlideListWithText stores
+ * references to notes
+ */
+ public static final int NOTES = 2;
+
+ private byte[] _header;
private static long _type = 4080;
private SlideAtomsSet[] slideAtomsSets;
public void addSlidePersistAtom(SlidePersistAtom spa) {
// Add the new SlidePersistAtom at the end
appendChildRecord(spa);
-
+
SlideAtomsSet newSAS = new SlideAtomsSet(spa, new Record[0]);
-
+
// Update our SlideAtomsSets with this
SlideAtomsSet[] sas = new SlideAtomsSet[slideAtomsSets.length+1];
System.arraycopy(slideAtomsSets, 0, sas, 0, slideAtomsSets.length);
slideAtomsSets = sas;
}
- /**
+ public int getInstance(){
+ return LittleEndian.getShort(_header, 0) >> 4;
+ }
+
+ public void setInstance(int inst){
+ LittleEndian.putShort(_header, (short)((inst << 4) | 0xF));
+ }
+
+ /**
* Get access to the SlideAtomsSets of the children of this record
*/
public SlideAtomsSet[] getSlideAtomsSets() { return slideAtomsSets; }
}
/**
- * Shifts a SlideAtomsSet to a new position.
- * Works by shifting the child records about, then updating
- * the SlideAtomSets array
- * @param toMove The SlideAtomsSet to move
- * @param newPosition The new (0 based) position for the SlideAtomsSet
- */
- public void repositionSlideAtomsSet(SlideAtomsSet toMove, int newPosition) {
- // Ensure it's one of ours
- int curPos = -1;
- for(int i=0; i<slideAtomsSets.length; i++) {
- if(slideAtomsSets[i] == toMove) { curPos = i; }
- }
- if(curPos == -1) {
- throw new IllegalArgumentException("The supplied SlideAtomsSet didn't belong to this SlideListWithText");
- }
-
- // Ensure the newPosision is valid
- if(newPosition < 0 || newPosition >= slideAtomsSets.length) {
- throw new IllegalArgumentException("The new position must be between 0, and the number of SlideAtomsSets");
- }
-
- // Build the new records list
- moveChildrenBefore(toMove.getSlidePersistAtom(), toMove.slideRecords.length, slideAtomsSets[newPosition].getSlidePersistAtom());
-
- // Build the new SlideAtomsSets list
- ArrayUtil.arrayMoveWithin(slideAtomsSets, curPos, newPosition, 1);
- }
-
- /**
* Inner class to wrap up a matching set of records that hold the
* text for a given sheet. Contains the leading SlidePersistAtom,
* and all of the records until the next SlidePersistAtom. This
/**
* Re-orders a slide, to a new position.
- * @param oldSlideNumer The old slide number (1 based)
+ * @param oldSlideNumber The old slide number (1 based)
* @param newSlideNumber The new slide number (1 based)
*/
- public void reorderSlide(int oldSlideNumer, int newSlideNumber) {
+ public void reorderSlide(int oldSlideNumber, int newSlideNumber) {
// Ensure these numbers are valid
- if(oldSlideNumer < 1 || newSlideNumber < 1) {
+ if(oldSlideNumber < 1 || newSlideNumber < 1) {
throw new IllegalArgumentException("Old and new slide numbers must be greater than 0");
}
- if(oldSlideNumer > _slides.length || newSlideNumber > _slides.length) {
+ if(oldSlideNumber > _slides.length || newSlideNumber > _slides.length) {
throw new IllegalArgumentException("Old and new slide numbers must not exceed the number of slides (" + _slides.length + ")");
}
-
- // Shift the SlideAtomsSet
- SlideListWithText slwt = _documentRecord.getSlideSlideListWithText();
- slwt.repositionSlideAtomsSet(
- slwt.getSlideAtomsSets()[(oldSlideNumer-1)],
- (newSlideNumber-1)
- );
-
- // Re-order the slides
- ArrayUtil.arrayMoveWithin(_slides, (oldSlideNumer-1), (newSlideNumber-1), 1);
-
- // Tell the appropriate slides their new numbers
- for(int i=0; i<_slides.length; i++) {
- _slides[i].setSlideNumber( (i+1) );
- }
+
+ // The order of slides is defined by the order of slide atom sets in the SlideListWithText container.
+ SlideListWithText slwt = _documentRecord.getSlideSlideListWithText();
+ SlideAtomsSet[] sas = slwt.getSlideAtomsSets();
+
+ SlideAtomsSet tmp = sas[oldSlideNumber-1];
+ sas[oldSlideNumber-1] = sas[newSlideNumber-1];
+ sas[newSlideNumber-1] = tmp;
+
+ ArrayList lst = new ArrayList();
+ for (int i = 0; i < sas.length; i++) {
+ lst.add(sas[i].getSlidePersistAtom());
+ Record[] r = sas[i].getSlideRecords();
+ for (int j = 0; j < r.length; j++) {
+ lst.add(r[j]);
+ }
+ _slides[i].setSlideNumber(i+1);
+ }
+ Record[] r = (Record[])lst.toArray(new Record[lst.size()]);
+ slwt.setChildRecord(r);
}
/* ===============================================================
if(slist == null) {
// Need to add a new one
slist = new SlideListWithText();
- _documentRecord.addSlideListWithText(slist);
+ slist.setInstance(SlideListWithText.SLIDES);
+ _documentRecord.addSlideListWithText(slist);
}
// Grab the SlidePersistAtom with the highest Slide Number.
ptr.addSlideLookup(sp.getRefID(), slideOffset);
logger.log(POILogger.INFO, "New slide ended up at " + slideOffset);
- // All done and added
+ slide.setMasterSheet(_masters[0]);
+ // All done and added
return slide;
}
-
/**
* Adds a picture to this presentation and returns the associated index.
*
assertEquals(3, ss_read.getSlides().length);
// And check it's as expected
- s1 = ss_read.getSlides()[0];
- s2 = ss_read.getSlides()[1];
- s3 = ss_read.getSlides()[2];
-
- assertEquals(257, s1._getSheetNumber());
- assertEquals(4, s1._getSheetRefId());
+ Slide _s1 = ss_read.getSlides()[0];
+ Slide _s2 = ss_read.getSlides()[1];
+ Slide _s3 = ss_read.getSlides()[2];
+
+ // 1 --> 3
+ assertEquals(s1._getSheetNumber(), _s3._getSheetNumber());
+ assertEquals(s1._getSheetRefId(), _s3._getSheetRefId());
assertEquals(1, s1.getSlideNumber());
- assertEquals(256, s2._getSheetNumber());
- assertEquals(3, s2._getSheetRefId());
+
+ // 2nd slide is not updated
+ assertEquals(s2._getSheetNumber(), _s2._getSheetNumber());
+ assertEquals(s2._getSheetRefId(), _s2._getSheetRefId());
assertEquals(2, s2.getSlideNumber());
- assertEquals(258, s3._getSheetNumber());
- assertEquals(5, s3._getSheetRefId());
+
+ // 3 --> 1
+ assertEquals(s3._getSheetNumber(), _s1._getSheetNumber());
+ assertEquals(s3._getSheetRefId(), _s1._getSheetRefId());
assertEquals(3, s3.getSlideNumber());
}
}
public void testEvaluate() {
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls");
HSSFSheet sheet = wb.getSheetAt(0);
- HSSFCell cell = sheet.getRow(0).getCell(0);
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
- CellValue evalResult = fe.evaluate(cell);
- evalResult.toString();
+ confirmCellEval(sheet, 0, 0, fe, "YEARFRAC(B1,C1)", 29.0/90.0);
+ confirmCellEval(sheet, 1, 0, fe, "YEARFRAC(B2,C2)", 0.0);
+ confirmCellEval(sheet, 2, 0, fe, "IF(ISEVEN(3),1.2,1.6)", 1.6);
+ confirmCellEval(sheet, 3, 0, fe, "IF(ISODD(3),1.2,1.6)", 1.2);
+ }
+
+ private static void confirmCellEval(HSSFSheet sheet, int rowIx, int colIx,
+ HSSFFormulaEvaluator fe, String expectedFormula, double expectedResult) {
+ HSSFCell cell = sheet.getRow(rowIx).getCell(colIx);
+ assertEquals(expectedFormula, cell.getCellFormula());
+ CellValue cv = fe.evaluate(cell);
+ assertEquals(expectedResult, cv.getNumberValue(), 0.0);
}
}
confirm(md(1999, 3, 31), md(1999, 4, 3), 1, 0.008219178);
confirm(md(1999, 4, 5), md(1999, 4, 8), 1, 0.008219178);
confirm(md(1999, 4, 4), md(1999, 4, 7), 1, 0.008219178);
+ confirm(md(2000, 2, 5), md(2000, 6, 1), 0, 0.322222222);
}
private void confirm(double startDate, double endDate, int basis, double expectedValue) {
TestSuite result = new TestSuite(AllFormulaEvalTests.class.getName());
result.addTestSuite(TestAreaEval.class);
result.addTestSuite(TestCircularReferences.class);
+ result.addTestSuite(TestDivideEval.class);
+ result.addTestSuite(TestEqualEval.class);
result.addTestSuite(TestExternalFunction.class);
result.addTestSuite(TestFormulaBugs.class);
result.addTestSuite(TestFormulasFromSpreadsheet.class);
HSSFCell testCell = row.createCell(0);
testCell.setCellFormula("A1");
- HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
CellValue cellValue = evaluateWithCycles(wb, sheet, testCell);
confirmCycleErrorCode(cellValue);
HSSFCell testCell = row.createCell(3);
testCell.setCellFormula("A1");
- HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
CellValue cellValue = evaluateWithCycles(wb, sheet, testCell);
confirmCycleErrorCode(cellValue);
--- /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.TestCase;
+
+import org.apache.poi.hssf.record.formula.functions.EvalFactory;
+import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker;
+
+/**
+ * Test for divide operator evaluator.
+ *
+ * @author Josh Micich
+ */
+public final class TestDivideEval extends TestCase {
+
+ private static void confirm(ValueEval arg0, ValueEval arg1, double expectedResult) {
+ Eval[] args = {
+ arg0, arg1,
+ };
+
+ double result = NumericFunctionInvoker.invoke(DivideEval.instance, args, 0, 0);
+
+ assertEquals(expectedResult, result, 0);
+ }
+
+ public void testBasic() {
+ confirm(new NumberEval(5), new NumberEval(2), 2.5);
+ confirm(new NumberEval(3), new NumberEval(16), 0.1875);
+ confirm(new NumberEval(-150), new NumberEval(-15), 10.0);
+ confirm(new StringEval("0.2"), new NumberEval(0.05), 4.0);
+ confirm(BoolEval.TRUE, new StringEval("-0.2"), -5.0);
+ }
+
+ public void test1x1Area() {
+ AreaEval ae0 = EvalFactory.createAreaEval("B2:B2", new ValueEval[] { new NumberEval(50), });
+ AreaEval ae1 = EvalFactory.createAreaEval("C2:C2", new ValueEval[] { new NumberEval(10), });
+ confirm(ae0, ae1, 5);
+ }
+ public void testDivZero() {
+ Eval[] args = {
+ new NumberEval(5), NumberEval.ZERO,
+ };
+ Eval result = DivideEval.instance.evaluate(args, 0, (short) 0);
+ assertEquals(ErrorEval.DIV_ZERO, result);
+ }
+}
--- /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
+\r
+package org.apache.poi.hssf.record.formula.eval;\r
+\r
+import junit.framework.AssertionFailedError;\r
+import junit.framework.TestCase;\r
+\r
+import org.apache.poi.hssf.record.formula.functions.EvalFactory;\r
+\r
+/**\r
+ * Test for unary plus operator evaluator.\r
+ *\r
+ * @author Josh Micich\r
+ */\r
+public final class TestEqualEval extends TestCase {\r
+\r
+ /**\r
+ * Test for bug observable at svn revision 692218 (Sep 2008)<br/>\r
+ * The value from a 1x1 area should be taken immediately, regardless of srcRow and srcCol\r
+ */\r
+ public void test1x1AreaOperand() {\r
+ \r
+ ValueEval[] values = { BoolEval.FALSE, };\r
+ Eval[] args = {\r
+ EvalFactory.createAreaEval("B1:B1", values),\r
+ BoolEval.FALSE,\r
+ };\r
+ Eval result = EqualEval.instance.evaluate(args, 10, (short)20);\r
+ if (result instanceof ErrorEval) {\r
+ if (result == ErrorEval.VALUE_INVALID) {\r
+ throw new AssertionFailedError("Identified bug in evaluation of 1x1 area");\r
+ }\r
+ }\r
+ assertEquals(BoolEval.class, result.getClass());\r
+ assertTrue(((BoolEval)result).getBooleanValue());\r
+ }\r
+ /**\r
+ * Empty string is equal to blank\r
+ */\r
+ public void testBlankEqualToEmptyString() {\r
+ \r
+ Eval[] args = {\r
+ new StringEval(""),\r
+ BlankEval.INSTANCE,\r
+ };\r
+ Eval result = EqualEval.instance.evaluate(args, 10, (short)20);\r
+ assertEquals(BoolEval.class, result.getClass());\r
+ BoolEval be = (BoolEval) result;\r
+ if (!be.getBooleanValue()) {\r
+ throw new AssertionFailedError("Identified bug blank/empty string equality");\r
+ }\r
+ assertTrue(be.getBooleanValue());\r
+ }\r
+}\r
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
-import org.apache.poi.hssf.record.formula.PercentPtg;
+import org.apache.poi.hssf.record.formula.functions.EvalFactory;
import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
arg,
};
- PercentEval opEval = new PercentEval(PercentPtg.instance);
- double result = NumericFunctionInvoker.invoke(opEval, args, -1, (short)-1);
+ OperationEval opEval = PercentEval.instance;
+ double result = NumericFunctionInvoker.invoke(opEval, args, 0, 0);
assertEquals(expectedResult, result, 0);
}
confirm(BoolEval.TRUE, 0.01);
}
+ public void test1x1Area() {
+ AreaEval ae = EvalFactory.createAreaEval("B2:B2", new ValueEval[] { new NumberEval(50), });
+ confirm(ae, 0.5);
+ }
public void testInSpreadSheet() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1");
-/*
-* 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.
-*/
-
+/* ====================================================================
+ 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.TestCase;
import org.apache.poi.hssf.record.formula.AreaPtg;
-import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
import org.apache.poi.hssf.record.formula.functions.EvalFactory;
import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker;
EvalFactory.createAreaEval(areaPtg, values),
};
- double result = NumericFunctionInvoker.invoke(new UnaryPlusEval(UnaryPlusPtg.instance), args, 10, (short)20);
+ double result = NumericFunctionInvoker.invoke(UnaryPlusEval.instance, args, 10, (short)20);
assertEquals(35, result, 0);
}
result.addTestSuite(TestIndex.class);
result.addTestSuite(TestIsBlank.class);
result.addTestSuite(TestLen.class);
+ result.addTestSuite(TestLookupFunctionsFromSpreadsheet.class);
result.addTestSuite(TestMid.class);
result.addTestSuite(TestMathX.class);
result.addTestSuite(TestMatch.class);
* @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
*/
public final class TestDate extends TestCase {
-
+
private HSSFCell cell11;
private HSSFFormulaEvaluator evaluator;
public void setUp() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
- cell11 = sheet.createRow(0).createCell(0);
+ cell11 = sheet.createRow(0).createCell(0);
cell11.setCellType(HSSFCell.CELL_TYPE_FORMULA);
evaluator = new HSSFFormulaEvaluator(sheet, wb);
}
-
- /**
- * Test disabled pending a fix in the formula evaluator
- * TODO - create MissingArgEval and modify the formula evaluator to handle this
- */
+
+ /**
+ * Test disabled pending a fix in the formula evaluator
+ * TODO - create MissingArgEval and modify the formula evaluator to handle this
+ */
public void DISABLEDtestSomeArgumentsMissing() {
confirm("DATE(, 1, 0)", 0.0);
confirm("DATE(, 1, 1)", 1.0);
}
-
+
public void testValid() {
-
+
confirm("DATE(1900, 1, 1)", 1);
confirm("DATE(1900, 1, 32)", 32);
confirm("DATE(1900, 222, 1)", 6727);
confirm("DATE(2000, 1, 222)", 36747.00);
confirm("DATE(2007, 1, 1)", 39083);
}
-
+
public void testBugDate() {
confirm("DATE(1900, 2, 29)", 60);
confirm("DATE(1900, 2, 30)", 61);
confirm("DATE(1900, 1, 2222)", 2222);
confirm("DATE(1900, 1, 22222)", 22222);
}
-
+
public void testPartYears() {
confirm("DATE(4, 1, 1)", 1462.00);
confirm("DATE(14, 1, 1)", 5115.00);
confirm("DATE(1004, 1, 1)", 366705.00);
}
- private void confirm(String formulaText, double expectedResult) {
+ private void confirm(String formulaText, double expectedResult) {
cell11.setCellFormula(formulaText);
+ evaluator.clearCache();
double actualValue = evaluator.evaluate(cell11).getNumberValue();
- assertEquals(expectedResult, actualValue, 0);
- }
+ assertEquals(expectedResult, actualValue, 0);
+ }
}
result.addTestSuite(TestHSSFConditionalFormatting.class);
result.addTestSuite(TestHSSFDataFormatter.class);
result.addTestSuite(TestHSSFDateUtil.class);
+ result.addTestSuite(TestHSSFFormulaEvaluator.class);
result.addTestSuite(TestHSSFHeaderFooter.class);
result.addTestSuite(TestHSSFHyperlink.class);
result.addTestSuite(TestHSSFOptimiser.class);
import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.util.TempFile;
/**
NameRecord r = w.getNameRecord(i);
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
- List nd = r.getNameDefinition();
- assertEquals(1, nd.size());
- assertTrue(nd.get(0) instanceof DeletedArea3DPtg);
+ Ptg[] nd = r.getNameDefinition();
+ assertEquals(1, nd.length);
+ assertTrue(nd[0] instanceof DeletedArea3DPtg);
}
NameRecord r = w.getNameRecord(i);
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
- List nd = r.getNameDefinition();
- assertEquals(1, nd.size());
- assertTrue(nd.get(0) instanceof DeletedArea3DPtg);
+ Ptg[] nd = r.getNameDefinition();
+ assertEquals(1, nd.length);
+ assertTrue(nd[0] instanceof DeletedArea3DPtg);
}
NameRecord r = w.getNameRecord(i);
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
- List nd = r.getNameDefinition();
- assertEquals(1, nd.size());
- assertTrue(nd.get(0) instanceof DeletedArea3DPtg);
+ Ptg[] nd = r.getNameDefinition();
+ assertEquals(1, nd.length);
+ assertTrue(nd[0] instanceof DeletedArea3DPtg);
}
}
import java.io.File;
import java.io.FileOutputStream;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
import java.util.Iterator;
import junit.framework.AssertionFailedError;
*/
public final class TestFormulaEvaluatorBugs extends TestCase {
+ private static final boolean OUTPUT_TEST_FILES = false;
private String tmpDirName;
protected void setUp() {
HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
assertEquals(4.2 * 25, row.getCell(3).getNumericCellValue(), 0.0001);
- // Save
- File existing = new File(tmpDirName, "44636-existing.xls");
- FileOutputStream out = new FileOutputStream(existing);
- wb.write(out);
- out.close();
- System.err.println("Existing file for bug #44636 written to " + existing.toString());
-
+ FileOutputStream out;
+ if (OUTPUT_TEST_FILES) {
+ // Save
+ File existing = new File(tmpDirName, "44636-existing.xls");
+ out = new FileOutputStream(existing);
+ wb.write(out);
+ out.close();
+ System.err.println("Existing file for bug #44636 written to " + existing.toString());
+ }
// Now, do a new file from scratch
wb = new HSSFWorkbook();
sheet = wb.createSheet();
HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
assertEquals(5.4, row.getCell(0).getNumericCellValue(), 0.0001);
- // Save
- File scratch = new File(tmpDirName, "44636-scratch.xls");
- out = new FileOutputStream(scratch);
- wb.write(out);
- out.close();
- System.err.println("New file for bug #44636 written to " + scratch.toString());
+ if (OUTPUT_TEST_FILES) {
+ // Save
+ File scratch = new File(tmpDirName, "44636-scratch.xls");
+ out = new FileOutputStream(scratch);
+ wb.write(out);
+ out.close();
+ System.err.println("New file for bug #44636 written to " + scratch.toString());
+ }
}
/**
}
/**
- * Apparently, each subsequent call takes longer, which is very
- * odd.
- * We think it's because the formulas are recursive and crazy
+ * The HSSFFormula evaluator performance benefits greatly from caching of intermediate cell values
*/
- public void DISABLEDtestSlowEvaluate45376() throws Exception {
- HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("45376.xls");
+ public void testSlowEvaluate45376() {
- final String SHEET_NAME = "Eingabe";
- final int row = 6;
- final HSSFSheet sheet = wb.getSheet(SHEET_NAME);
-
- int firstCol = 4;
- int lastCol = 14;
- long[] timings = new long[lastCol-firstCol+1];
- long[] rtimings = new long[lastCol-firstCol+1];
-
- long then, now;
-
- final HSSFRow excelRow = sheet.getRow(row);
- for(int i = firstCol; i <= lastCol; i++) {
- final HSSFCell excelCell = excelRow.getCell(i);
- final HSSFFormulaEvaluator evaluator = new
- HSSFFormulaEvaluator(sheet, wb);
-
- now = System.currentTimeMillis();
- evaluator.evaluate(excelCell);
- then = System.currentTimeMillis();
- timings[i-firstCol] = (then-now);
- System.err.println("Col " + i + " took " + (then-now) + "ms");
- }
- for(int i = lastCol; i >= firstCol; i--) {
- final HSSFCell excelCell = excelRow.getCell(i);
- final HSSFFormulaEvaluator evaluator = new
- HSSFFormulaEvaluator(sheet, wb);
-
- now = System.currentTimeMillis();
- evaluator.evaluate(excelCell);
- then = System.currentTimeMillis();
- rtimings[i-firstCol] = (then-now);
- System.err.println("Col " + i + " took " + (then-now) + "ms");
- }
-
- // The timings for each should be about the same
- long avg = 0;
- for(int i=0; i<timings.length; i++) {
- avg += timings[i];
- }
- avg = (long)( ((double)avg) / timings.length );
-
- // Warn if any took more then 1.5 the average
- // TODO - replace with assert or similar
- for(int i=0; i<timings.length; i++) {
- if(timings[i] > 1.5*avg) {
- System.err.println("Doing col " + (i+firstCol) +
- " took " + timings[i] + "ms, vs avg " +
- avg + "ms"
- );
- }
- }
+ // Firstly set up a sequence of formula cells where each depends on the previous multiple
+ // times. Without caching, each subsequent cell take about 4 times longer to evaluate.
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet("Sheet1");
+ HSSFRow row = sheet.createRow(0);
+ for(int i=1; i<10; i++) {
+ HSSFCell cell = row.createCell(i);
+ char prevCol = (char) ('A' + i-1);
+ String prevCell = prevCol + "1";
+ // this formula is inspired by the offending formula of the attachment for bug 45376
+ String formula = "IF(DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1)<=$D$3," +
+ "DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1),NA())";
+ cell.setCellFormula(formula);
+
+ }
+ Calendar cal = new GregorianCalendar(2000, 0, 1, 0, 0, 0);
+ row.createCell(0).setCellValue(cal);
+
+ // Choose cell A9, so that the failing test case doesn't take too long to execute.
+ HSSFCell cell = row.getCell(8);
+ HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
+ evaluator.evaluate(cell);
+ int evalCount = evaluator.getEvaluationCount();
+ // With caching, the evaluationCount is 8 which is a big improvement
+ if (evalCount > 10) {
+ // Without caching, evaluating cell 'A9' takes 21845 evaluations which consumes
+ // much time (~3 sec on Core 2 Duo 2.2GHz)
+ System.err.println("Cell A9 took " + evalCount + " intermediate evaluations");
+ throw new AssertionFailedError("Identifed bug 45376 - Formula evaluator should cache values");
+ }
}
}
\ No newline at end of file
--- /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 org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
+
+import junit.framework.TestCase;
+/**
+ *
+ * @author Josh Micich
+ */
+public final class TestHSSFFormulaEvaluator extends TestCase {
+
+ /**
+ * Test that the HSSFFormulaEvaluator can evaluate simple named ranges
+ * (single cells and rectangular areas)
+ */
+ public void testEvaluateSimple() {
+ HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("testNames.xls");
+ HSSFSheet sheet = wb.getSheetAt(0);
+ HSSFCell cell = sheet.getRow(8).getCell(0);
+ HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+ CellValue cv = fe.evaluate(cell);
+ assertEquals(HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
+ assertEquals(3.72, cv.getNumberValue(), 0.0);
+ }
+
+ public void testFullColumnRefs() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet("Sheet1");
+ HSSFRow row = sheet.createRow(0);
+ HSSFCell cell0 = row.createCell(0);
+ cell0.setCellFormula("sum(D:D)");
+ HSSFCell cell1 = row.createCell(1);
+ cell1.setCellFormula("sum(D:E)");
+
+ // some values in column D
+ setValue(sheet, 1, 3, 5.0);
+ setValue(sheet, 2, 3, 6.0);
+ setValue(sheet, 5, 3, 7.0);
+ setValue(sheet, 50, 3, 8.0);
+
+ // some values in column E
+ setValue(sheet, 1, 4, 9.0);
+ setValue(sheet, 2, 4, 10.0);
+ setValue(sheet, 30000, 4, 11.0);
+
+ // some other values
+ setValue(sheet, 1, 2, 100.0);
+ setValue(sheet, 2, 5, 100.0);
+ setValue(sheet, 3, 6, 100.0);
+
+
+ HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
+ assertEquals(26.0, fe.evaluate(cell0).getNumberValue(), 0.0);
+ assertEquals(56.0, fe.evaluate(cell1).getNumberValue(), 0.0);
+ }
+
+ private static void setValue(HSSFSheet sheet, int rowIndex, int colIndex, double value) {
+ HSSFRow row = sheet.getRow(rowIndex);
+ if (row == null) {
+ row = sheet.createRow(rowIndex);
+ }
+ row.createCell(colIndex).setCellValue(value);
+ }
+}
import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.model.Sheet;
-import org.apache.poi.hssf.record.HCenterRecord;
-import org.apache.poi.hssf.record.PasswordRecord;
-import org.apache.poi.hssf.record.ProtectRecord;
-import org.apache.poi.hssf.record.SCLRecord;
-import org.apache.poi.hssf.record.VCenterRecord;
-import org.apache.poi.hssf.record.WSBoolRecord;
-import org.apache.poi.hssf.record.WindowTwoRecord;
+import org.apache.poi.hssf.model.DrawingManager2;
+import org.apache.poi.hssf.record.*;
import org.apache.poi.ss.util.Region;
import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ddf.EscherDgRecord;
/**
* Tests HSSFSheet. This test case is very incomplete at the moment.
*/
public final class TestHSSFSheet extends TestCase {
- private static HSSFWorkbook openSample(String sampleFileName) {
- return HSSFTestDataSamples.openSampleWorkbook(sampleFileName);
- }
-
- /**
- * Test the gridset field gets set as expected.
- */
- public void testBackupRecord() {
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet s = wb.createSheet();
- Sheet sheet = s.getSheet();
-
- assertEquals(true, sheet.getGridsetRecord().getGridset());
- s.setGridsPrinted(true);
- assertEquals(false, sheet.getGridsetRecord().getGridset());
- }
-
- /**
- * Test vertically centered output.
- */
- public void testVerticallyCenter() {
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet s = wb.createSheet();
- Sheet sheet = s.getSheet();
- VCenterRecord record = sheet.getPageSettings().getVCenter();
-
- assertEquals(false, record.getVCenter());
- s.setVerticallyCenter(true);
- assertEquals(true, record.getVCenter());
-
- // wb.write(new FileOutputStream("c:\\test.xls"));
- }
-
- /**
- * Test horizontally centered output.
- */
- public void testHorizontallyCenter() {
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet s = wb.createSheet();
- Sheet sheet = s.getSheet();
- HCenterRecord record = sheet.getPageSettings().getHCenter();
-
- assertEquals(false, record.getHCenter());
- s.setHorizontallyCenter(true);
- assertEquals(true, record.getHCenter());
- }
-
-
- /**
- * Test WSBboolRecord fields get set in the user model.
- */
- public void testWSBool() {
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet s = wb.createSheet();
- Sheet sheet = s.getSheet();
- WSBoolRecord record =
- (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
-
- // Check defaults
- assertEquals(true, record.getAlternateExpression());
- assertEquals(true, record.getAlternateFormula());
- assertEquals(false, record.getAutobreaks());
- assertEquals(false, record.getDialog());
- assertEquals(false, record.getDisplayGuts());
- assertEquals(true, record.getFitToPage());
- assertEquals(false, record.getRowSumsBelow());
- assertEquals(false, record.getRowSumsRight());
-
- // Alter
- s.setAlternativeExpression(false);
- s.setAlternativeFormula(false);
- s.setAutobreaks(true);
- s.setDialog(true);
- s.setDisplayGuts(true);
- s.setFitToPage(false);
- s.setRowSumsBelow(true);
- s.setRowSumsRight(true);
-
- // Check
- assertEquals(false, record.getAlternateExpression());
- assertEquals(false, record.getAlternateFormula());
- assertEquals(true, record.getAutobreaks());
- assertEquals(true, record.getDialog());
- assertEquals(true, record.getDisplayGuts());
- assertEquals(false, record.getFitToPage());
- assertEquals(true, record.getRowSumsBelow());
- assertEquals(true, record.getRowSumsRight());
- assertEquals(false, s.getAlternateExpression());
- assertEquals(false, s.getAlternateFormula());
- assertEquals(true, s.getAutobreaks());
- assertEquals(true, s.getDialog());
- assertEquals(true, s.getDisplayGuts());
- assertEquals(false, s.getFitToPage());
- assertEquals(true, s.getRowSumsBelow());
- assertEquals(true, s.getRowSumsRight());
- }
-
- public void testReadBooleans() {
- HSSFWorkbook workbook = new HSSFWorkbook();
- HSSFSheet sheet = workbook.createSheet("Test boolean");
- HSSFRow row = sheet.createRow(2);
- HSSFCell cell = row.createCell(9);
- cell.setCellValue(true);
- cell = row.createCell(11);
- cell.setCellValue(true);
-
- workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
-
- sheet = workbook.getSheetAt(0);
- row = sheet.getRow(2);
- assertNotNull(row);
- assertEquals(2, row.getPhysicalNumberOfCells());
- }
-
- public void testRemoveRow() {
- HSSFWorkbook workbook = new HSSFWorkbook();
- HSSFSheet sheet = workbook.createSheet("Test boolean");
- HSSFRow row = sheet.createRow(2);
- sheet.removeRow(row);
- }
-
- public void testRemoveZeroRow() {
- HSSFWorkbook workbook = new HSSFWorkbook();
- HSSFSheet sheet = workbook.createSheet("Sheet1");
- HSSFRow row = sheet.createRow(0);
- try {
- sheet.removeRow(row);
- } catch (IllegalArgumentException e) {
- if (e.getMessage().equals("Invalid row number (-1) outside allowable range (0..65535)")) {
- throw new AssertionFailedError("Identified bug 45367");
- }
- throw e;
- }
- }
-
- public void testCloneSheet() {
- HSSFWorkbook workbook = new HSSFWorkbook();
- HSSFSheet sheet = workbook.createSheet("Test Clone");
- HSSFRow row = sheet.createRow(0);
- HSSFCell cell = row.createCell(0);
- HSSFCell cell2 = row.createCell(1);
- cell.setCellValue(new HSSFRichTextString("clone_test"));
- cell2.setCellFormula("sin(1)");
-
- HSSFSheet clonedSheet = workbook.cloneSheet(0);
- HSSFRow clonedRow = clonedSheet.getRow(0);
-
- //Check for a good clone
- assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test");
-
- //Check that the cells are not somehow linked
- cell.setCellValue(new HSSFRichTextString("Difference Check"));
- cell2.setCellFormula("cos(2)");
- if ("Difference Check".equals(clonedRow.getCell(0).getRichStringCellValue().getString())) {
- fail("string cell not properly cloned");
- }
- if ("COS(2)".equals(clonedRow.getCell(1).getCellFormula())) {
- fail("formula cell not properly cloned");
- }
- assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test");
- assertEquals(clonedRow.getCell(1).getCellFormula(), "SIN(1)");
- }
-
- /** tests that the sheet name for multiple clones of the same sheet is unique
- * BUG 37416
- */
- public void testCloneSheetMultipleTimes() {
- HSSFWorkbook workbook = new HSSFWorkbook();
- HSSFSheet sheet = workbook.createSheet("Test Clone");
- HSSFRow row = sheet.createRow(0);
- HSSFCell cell = row.createCell(0);
- cell.setCellValue(new HSSFRichTextString("clone_test"));
- //Clone the sheet multiple times
- workbook.cloneSheet(0);
- workbook.cloneSheet(0);
-
- assertNotNull(workbook.getSheet("Test Clone"));
- assertNotNull(workbook.getSheet("Test Clone(1)"));
- assertNotNull(workbook.getSheet("Test Clone(2)"));
- }
-
- /**
- * Setting landscape and portrait stuff on new sheets
- */
- public void testPrintSetupLandscapeNew() throws Exception {
- HSSFWorkbook workbook = new HSSFWorkbook();
- HSSFSheet sheetL = workbook.createSheet("LandscapeS");
- HSSFSheet sheetP = workbook.createSheet("LandscapeP");
-
- // Check two aspects of the print setup
- assertFalse(sheetL.getPrintSetup().getLandscape());
- assertFalse(sheetP.getPrintSetup().getLandscape());
- assertEquals(0, sheetL.getPrintSetup().getCopies());
- assertEquals(0, sheetP.getPrintSetup().getCopies());
-
- // Change one on each
- sheetL.getPrintSetup().setLandscape(true);
- sheetP.getPrintSetup().setCopies((short)3);
-
- // Check taken
- assertTrue(sheetL.getPrintSetup().getLandscape());
- assertFalse(sheetP.getPrintSetup().getLandscape());
- assertEquals(0, sheetL.getPrintSetup().getCopies());
- assertEquals(3, sheetP.getPrintSetup().getCopies());
-
- // Save and re-load, and check still there
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- workbook.write(baos);
- workbook = new HSSFWorkbook(new ByteArrayInputStream(baos.toByteArray()));
-
- assertTrue(sheetL.getPrintSetup().getLandscape());
- assertFalse(sheetP.getPrintSetup().getLandscape());
- assertEquals(0, sheetL.getPrintSetup().getCopies());
- assertEquals(3, sheetP.getPrintSetup().getCopies());
- }
-
- /**
- * Setting landscape and portrait stuff on existing sheets
- */
- public void testPrintSetupLandscapeExisting() {
- HSSFWorkbook workbook = openSample("SimpleWithPageBreaks.xls");
-
- assertEquals(3, workbook.getNumberOfSheets());
-
- HSSFSheet sheetL = workbook.getSheetAt(0);
- HSSFSheet sheetPM = workbook.getSheetAt(1);
- HSSFSheet sheetLS = workbook.getSheetAt(2);
-
- // Check two aspects of the print setup
- assertFalse(sheetL.getPrintSetup().getLandscape());
- assertTrue(sheetPM.getPrintSetup().getLandscape());
- assertTrue(sheetLS.getPrintSetup().getLandscape());
- assertEquals(1, sheetL.getPrintSetup().getCopies());
- assertEquals(1, sheetPM.getPrintSetup().getCopies());
- assertEquals(1, sheetLS.getPrintSetup().getCopies());
-
- // Change one on each
- sheetL.getPrintSetup().setLandscape(true);
- sheetPM.getPrintSetup().setLandscape(false);
- sheetPM.getPrintSetup().setCopies((short)3);
-
- // Check taken
- assertTrue(sheetL.getPrintSetup().getLandscape());
- assertFalse(sheetPM.getPrintSetup().getLandscape());
- assertTrue(sheetLS.getPrintSetup().getLandscape());
- assertEquals(1, sheetL.getPrintSetup().getCopies());
- assertEquals(3, sheetPM.getPrintSetup().getCopies());
- assertEquals(1, sheetLS.getPrintSetup().getCopies());
-
- // Save and re-load, and check still there
- workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
-
- assertTrue(sheetL.getPrintSetup().getLandscape());
- assertFalse(sheetPM.getPrintSetup().getLandscape());
- assertTrue(sheetLS.getPrintSetup().getLandscape());
- assertEquals(1, sheetL.getPrintSetup().getCopies());
- assertEquals(3, sheetPM.getPrintSetup().getCopies());
- assertEquals(1, sheetLS.getPrintSetup().getCopies());
- }
-
- public void testGroupRows() {
- HSSFWorkbook workbook = new HSSFWorkbook();
- HSSFSheet s = workbook.createSheet();
- HSSFRow r1 = s.createRow(0);
- HSSFRow r2 = s.createRow(1);
- HSSFRow r3 = s.createRow(2);
- HSSFRow r4 = s.createRow(3);
- HSSFRow r5 = s.createRow(4);
-
- assertEquals(0, r1.getOutlineLevel());
- assertEquals(0, r2.getOutlineLevel());
- assertEquals(0, r3.getOutlineLevel());
- assertEquals(0, r4.getOutlineLevel());
- assertEquals(0, r5.getOutlineLevel());
-
- s.groupRow(2,3);
-
- assertEquals(0, r1.getOutlineLevel());
- assertEquals(0, r2.getOutlineLevel());
- assertEquals(1, r3.getOutlineLevel());
- assertEquals(1, r4.getOutlineLevel());
- assertEquals(0, r5.getOutlineLevel());
-
- // Save and re-open
- workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
-
- s = workbook.getSheetAt(0);
- r1 = s.getRow(0);
- r2 = s.getRow(1);
- r3 = s.getRow(2);
- r4 = s.getRow(3);
- r5 = s.getRow(4);
-
- assertEquals(0, r1.getOutlineLevel());
- assertEquals(0, r2.getOutlineLevel());
- assertEquals(1, r3.getOutlineLevel());
- assertEquals(1, r4.getOutlineLevel());
- assertEquals(0, r5.getOutlineLevel());
- }
-
- public void testGroupRowsExisting() {
- HSSFWorkbook workbook = openSample("NoGutsRecords.xls");
-
- HSSFSheet s = workbook.getSheetAt(0);
- HSSFRow r1 = s.getRow(0);
- HSSFRow r2 = s.getRow(1);
- HSSFRow r3 = s.getRow(2);
- HSSFRow r4 = s.getRow(3);
- HSSFRow r5 = s.getRow(4);
- HSSFRow r6 = s.getRow(5);
-
- assertEquals(0, r1.getOutlineLevel());
- assertEquals(0, r2.getOutlineLevel());
- assertEquals(0, r3.getOutlineLevel());
- assertEquals(0, r4.getOutlineLevel());
- assertEquals(0, r5.getOutlineLevel());
- assertEquals(0, r6.getOutlineLevel());
-
- // This used to complain about lacking guts records
- s.groupRow(2, 4);
-
- assertEquals(0, r1.getOutlineLevel());
- assertEquals(0, r2.getOutlineLevel());
- assertEquals(1, r3.getOutlineLevel());
- assertEquals(1, r4.getOutlineLevel());
- assertEquals(1, r5.getOutlineLevel());
- assertEquals(0, r6.getOutlineLevel());
-
- // Save and re-open
- try {
- workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
- } catch (OutOfMemoryError e) {
- throw new AssertionFailedError("Identified bug 39903");
- }
-
- s = workbook.getSheetAt(0);
- r1 = s.getRow(0);
- r2 = s.getRow(1);
- r3 = s.getRow(2);
- r4 = s.getRow(3);
- r5 = s.getRow(4);
- r6 = s.getRow(5);
-
- assertEquals(0, r1.getOutlineLevel());
- assertEquals(0, r2.getOutlineLevel());
- assertEquals(1, r3.getOutlineLevel());
- assertEquals(1, r4.getOutlineLevel());
- assertEquals(1, r5.getOutlineLevel());
- assertEquals(0, r6.getOutlineLevel());
- }
-
- public void testGetDrawings() {
- HSSFWorkbook wb1c = openSample("WithChart.xls");
- HSSFWorkbook wb2c = openSample("WithTwoCharts.xls");
-
- // 1 chart sheet -> data on 1st, chart on 2nd
- assertNotNull(wb1c.getSheetAt(0).getDrawingPatriarch());
- assertNotNull(wb1c.getSheetAt(1).getDrawingPatriarch());
- assertFalse(wb1c.getSheetAt(0).getDrawingPatriarch().containsChart());
- assertTrue(wb1c.getSheetAt(1).getDrawingPatriarch().containsChart());
-
- // 2 chart sheet -> data on 1st, chart on 2nd+3rd
- assertNotNull(wb2c.getSheetAt(0).getDrawingPatriarch());
- assertNotNull(wb2c.getSheetAt(1).getDrawingPatriarch());
- assertNotNull(wb2c.getSheetAt(2).getDrawingPatriarch());
- assertFalse(wb2c.getSheetAt(0).getDrawingPatriarch().containsChart());
- assertTrue(wb2c.getSheetAt(1).getDrawingPatriarch().containsChart());
- assertTrue(wb2c.getSheetAt(2).getDrawingPatriarch().containsChart());
- }
-
- /**
- * Test that the ProtectRecord is included when creating or cloning a sheet
- */
- public void testProtect() {
- HSSFWorkbook workbook = new HSSFWorkbook();
- HSSFSheet hssfSheet = workbook.createSheet();
- Sheet sheet = hssfSheet.getSheet();
- ProtectRecord protect = sheet.getProtect();
-
- assertFalse(protect.getProtect());
-
- // This will tell us that cloneSheet, and by extension,
- // the list forms of createSheet leave us with an accessible
- // ProtectRecord.
- hssfSheet.setProtect(true);
- Sheet cloned = sheet.cloneSheet();
- assertNotNull(cloned.getProtect());
- assertTrue(hssfSheet.getProtect());
- }
-
- public void testProtectSheet() {
- short expected = (short)0xfef1;
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet s = wb.createSheet();
- s.protectSheet("abcdefghij");
- Sheet sheet = s.getSheet();
- ProtectRecord protect = sheet.getProtect();
- PasswordRecord pass = sheet.getPassword();
- assertTrue("protection should be on",protect.getProtect());
- assertTrue("object protection should be on",sheet.isProtected()[1]);
- assertTrue("scenario protection should be on",sheet.isProtected()[2]);
- assertEquals("well known value for top secret hash should be "+Integer.toHexString(expected).substring(4),expected,pass.getPassword());
- }
-
-
- public void testZoom() {
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet sheet = wb.createSheet();
- assertEquals(-1, sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid));
- sheet.setZoom(3,4);
- assertTrue(sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid) > 0);
- SCLRecord sclRecord = (SCLRecord) sheet.getSheet().findFirstRecordBySid(SCLRecord.sid);
- assertEquals(3, sclRecord.getNumerator());
- assertEquals(4, sclRecord.getDenominator());
-
- int sclLoc = sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid);
- int window2Loc = sheet.getSheet().findFirstRecordLocBySid(WindowTwoRecord.sid);
- assertTrue(sclLoc == window2Loc + 1);
- }
-
-
- /**
- * When removing one merged region, it would break
- *
- */
- public void testRemoveMerged() {
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet sheet = wb.createSheet();
- CellRangeAddress region = new CellRangeAddress(0, 1, 0, 1);
- sheet.addMergedRegion(region);
- region = new CellRangeAddress(1, 2, 0, 1);
- sheet.addMergedRegion(region);
-
- sheet.removeMergedRegion(0);
-
- region = sheet.getMergedRegion(0);
- assertEquals("Left over region should be starting at row 1", 1, region.getFirstRow());
-
- sheet.removeMergedRegion(0);
-
- assertEquals("there should be no merged regions left!", 0, sheet.getNumMergedRegions());
-
- //an, add, remove, get(0) would null pointer
- sheet.addMergedRegion(region);
- assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
- sheet.removeMergedRegion(0);
- assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions());
- //add it again!
- region.setLastRow(4);
-
- sheet.addMergedRegion(region);
- assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
-
- //should exist now!
- assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions());
- region = sheet.getMergedRegion(0);
- assertEquals("the merged row to doesnt match the one we put in ", 4, region.getLastRow());
- }
-
- public void testShiftMerged() {
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet sheet = wb.createSheet();
- HSSFRow row = sheet.createRow(0);
- HSSFCell cell = row.createCell(0);
- cell.setCellValue(new HSSFRichTextString("first row, first cell"));
-
- row = sheet.createRow(1);
- cell = row.createCell(1);
- cell.setCellValue(new HSSFRichTextString("second row, second cell"));
-
- CellRangeAddress region = new CellRangeAddress(1, 1, 0, 1);
- sheet.addMergedRegion(region);
-
- sheet.shiftRows(1, 1, 1);
-
- region = sheet.getMergedRegion(0);
- assertEquals("Merged region not moved over to row 2", 2, region.getFirstRow());
- }
-
- /**
- * Tests the display of gridlines, formulas, and rowcolheadings.
- * @author Shawn Laubach (slaubach at apache dot org)
- */
- public void testDisplayOptions() {
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet sheet = wb.createSheet();
-
- wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
- sheet = wb.getSheetAt(0);
-
- assertEquals(sheet.isDisplayGridlines(), true);
- assertEquals(sheet.isDisplayRowColHeadings(), true);
- assertEquals(sheet.isDisplayFormulas(), false);
-
- sheet.setDisplayGridlines(false);
- sheet.setDisplayRowColHeadings(false);
- sheet.setDisplayFormulas(true);
-
- wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
- sheet = wb.getSheetAt(0);
-
- assertEquals(sheet.isDisplayGridlines(), false);
- assertEquals(sheet.isDisplayRowColHeadings(), false);
- assertEquals(sheet.isDisplayFormulas(), true);
- }
-
-
- /**
- * Make sure the excel file loads work
- *
- */
- public void testPageBreakFiles() {
- HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls");
-
- HSSFSheet sheet = wb.getSheetAt(0);
- assertNotNull(sheet);
-
- assertEquals("1 row page break", 1, sheet.getRowBreaks().length);
- assertEquals("1 column page break", 1, sheet.getColumnBreaks().length);
-
- assertTrue("No row page break", sheet.isRowBroken(22));
- assertTrue("No column page break", sheet.isColumnBroken((short)4));
-
- sheet.setRowBreak(10);
- sheet.setColumnBreak((short)13);
-
- assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
- assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
-
- wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
- sheet = wb.getSheetAt(0);
-
- assertTrue("No row page break", sheet.isRowBroken(22));
- assertTrue("No column page break", sheet.isColumnBroken((short)4));
-
- assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
- assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
- }
-
- public void testDBCSName () {
- HSSFWorkbook wb = openSample("DBCSSheetName.xls");
- wb.getSheetAt(1);
- assertEquals ("DBCS Sheet Name 2", wb.getSheetName(1),"\u090f\u0915" );
- assertEquals("DBCS Sheet Name 1", wb.getSheetName(0),"\u091c\u093e");
- }
-
- /**
- * Testing newly added method that exposes the WINDOW2.toprow
- * parameter to allow setting the toprow in the visible view
- * of the sheet when it is first opened.
- */
- public void testTopRow() {
- HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls");
-
- HSSFSheet sheet = wb.getSheetAt(0);
- assertNotNull(sheet);
-
- short toprow = (short) 100;
- short leftcol = (short) 50;
- sheet.showInPane(toprow,leftcol);
- assertEquals("HSSFSheet.getTopRow()", toprow, sheet.getTopRow());
- assertEquals("HSSFSheet.getLeftCol()", leftcol, sheet.getLeftCol());
- }
-
- /** cell with formula becomes null on cloning a sheet*/
- public void test35084() {
-
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFSheet s = wb.createSheet("Sheet1");
- HSSFRow r = s.createRow(0);
- r.createCell(0).setCellValue(1);
- r.createCell(1).setCellFormula("A1*2");
- HSSFSheet s1 = wb.cloneSheet(0);
- r = s1.getRow(0);
- assertEquals("double", r.getCell(0).getNumericCellValue(), 1, 0); // sanity check
- assertNotNull(r.getCell(1));
- assertEquals("formula", r.getCell(1).getCellFormula(), "A1*2");
- }
-
- /** test that new default column styles get applied */
- public void testDefaultColumnStyle() {
- HSSFWorkbook wb = new HSSFWorkbook();
- HSSFCellStyle style = wb.createCellStyle();
- HSSFSheet s = wb.createSheet();
- s.setDefaultColumnStyle((short) 0, style);
- HSSFRow r = s.createRow(0);
- HSSFCell c = r.createCell(0);
- assertEquals("style should match", style.getIndex(), c.getCellStyle().getIndex());
- }
-
-
- /**
- *
- */
- public void testAddEmptyRow() {
- //try to add 5 empty rows to a new sheet
- HSSFWorkbook workbook = new HSSFWorkbook();
- HSSFSheet sheet = workbook.createSheet();
- for (int i = 0; i < 5; i++) {
- sheet.createRow(i);
- }
-
- workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
-
- //try adding empty rows in an existing worksheet
- workbook = openSample("Simple.xls");
-
- sheet = workbook.getSheetAt(0);
- for (int i = 3; i < 10; i++) sheet.createRow(i);
-
- workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
- }
-
- public void testAutoSizeColumn() {
- HSSFWorkbook wb = openSample("43902.xls");
- String sheetName = "my sheet";
- HSSFSheet sheet = wb.getSheet(sheetName);
-
- // Can't use literal numbers for column sizes, as
- // will come out with different values on different
- // machines based on the fonts available.
- // So, we use ranges, which are pretty large, but
- // thankfully don't overlap!
- int minWithRow1And2 = 6400;
- int maxWithRow1And2 = 7800;
- int minWithRow1Only = 2750;
- int maxWithRow1Only = 3300;
-
- // autoSize the first column and check its size before the merged region (1,0,1,1) is set:
- // it has to be based on the 2nd row width
- sheet.autoSizeColumn((short)0);
- assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) >= minWithRow1And2);
- assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) <= maxWithRow1And2);
-
- //create a region over the 2nd row and auto size the first column
- sheet.addMergedRegion(new CellRangeAddress(1,1,0,1));
- sheet.autoSizeColumn((short)0);
- HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb);
-
- // check that the autoSized column width has ignored the 2nd row
- // because it is included in a merged region (Excel like behavior)
- HSSFSheet sheet2 = wb2.getSheet(sheetName);
- assertTrue(sheet2.getColumnWidth((short)0) >= minWithRow1Only);
- assertTrue(sheet2.getColumnWidth((short)0) <= maxWithRow1Only);
-
- // remove the 2nd row merged region and check that the 2nd row value is used to the autoSizeColumn width
- sheet2.removeMergedRegion(1);
- sheet2.autoSizeColumn((short)0);
- HSSFWorkbook wb3 = HSSFTestDataSamples.writeOutAndReadBack(wb2);
- HSSFSheet sheet3 = wb3.getSheet(sheetName);
- assertTrue(sheet3.getColumnWidth((short)0) >= minWithRow1And2);
- assertTrue(sheet3.getColumnWidth((short)0) <= maxWithRow1And2);
- }
-
- /**
- * Setting ForceFormulaRecalculation on sheets
- */
- public void testForceRecalculation() throws Exception {
- HSSFWorkbook workbook = openSample("UncalcedRecord.xls");
-
- HSSFSheet sheet = workbook.getSheetAt(0);
- HSSFSheet sheet2 = workbook.getSheetAt(0);
- HSSFRow row = sheet.getRow(0);
- row.createCell(0).setCellValue(5);
- row.createCell(1).setCellValue(8);
- assertFalse(sheet.getForceFormulaRecalculation());
- assertFalse(sheet2.getForceFormulaRecalculation());
-
- // Save and manually verify that on column C we have 0, value in template
- File tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_err.xls" );
- tempFile.delete();
- FileOutputStream fout = new FileOutputStream( tempFile );
- workbook.write( fout );
- fout.close();
- sheet.setForceFormulaRecalculation(true);
- assertTrue(sheet.getForceFormulaRecalculation());
-
- // Save and manually verify that on column C we have now 13, calculated value
- tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_succ.xls" );
- tempFile.delete();
- fout = new FileOutputStream( tempFile );
- workbook.write( fout );
- fout.close();
-
- // Try it can be opened
- HSSFWorkbook wb2 = new HSSFWorkbook(new FileInputStream(tempFile));
-
- // And check correct sheet settings found
- sheet = wb2.getSheetAt(0);
- sheet2 = wb2.getSheetAt(1);
- assertTrue(sheet.getForceFormulaRecalculation());
- assertFalse(sheet2.getForceFormulaRecalculation());
-
- // Now turn if back off again
- sheet.setForceFormulaRecalculation(false);
-
- fout = new FileOutputStream( tempFile );
- wb2.write( fout );
- fout.close();
- wb2 = new HSSFWorkbook(new FileInputStream(tempFile));
-
- assertFalse(wb2.getSheetAt(0).getForceFormulaRecalculation());
- assertFalse(wb2.getSheetAt(1).getForceFormulaRecalculation());
- assertFalse(wb2.getSheetAt(2).getForceFormulaRecalculation());
-
- // Now add a new sheet, and check things work
- // with old ones unset, new one set
- HSSFSheet s4 = wb2.createSheet();
- s4.setForceFormulaRecalculation(true);
-
- assertFalse(sheet.getForceFormulaRecalculation());
- assertFalse(sheet2.getForceFormulaRecalculation());
- assertTrue(s4.getForceFormulaRecalculation());
-
- fout = new FileOutputStream( tempFile );
- wb2.write( fout );
- fout.close();
-
- HSSFWorkbook wb3 = new HSSFWorkbook(new FileInputStream(tempFile));
- assertFalse(wb3.getSheetAt(0).getForceFormulaRecalculation());
- assertFalse(wb3.getSheetAt(1).getForceFormulaRecalculation());
- assertFalse(wb3.getSheetAt(2).getForceFormulaRecalculation());
- assertTrue(wb3.getSheetAt(3).getForceFormulaRecalculation());
- }
-
- public void testColumnWidth() {
- //check we can correctly read column widths from a reference workbook
- HSSFWorkbook wb = openSample("colwidth.xls");
-
- //reference values
- int[] ref = {365, 548, 731, 914, 1097, 1280, 1462, 1645, 1828, 2011, 2194, 2377, 2560, 2742, 2925, 3108, 3291, 3474, 3657};
-
- HSSFSheet sh = wb.getSheetAt(0);
- for (char i = 'A'; i <= 'S'; i++) {
- int idx = i - 'A';
- int w = sh.getColumnWidth((short)idx);
- assertEquals(ref[idx], w);
- }
-
- //the second sheet doesn't have overridden column widths
- sh = wb.getSheetAt(1);
- int def_width = sh.getDefaultColumnWidth();
- for (char i = 'A'; i <= 'S'; i++) {
- int idx = i - 'A';
- int w = sh.getColumnWidth((short)idx);
- //getDefaultColumnWidth returns width measued in characters
- //getColumnWidth returns width measued in 1/256th units
- assertEquals(def_width*256, w);
- }
-
- //test new workbook
- wb = new HSSFWorkbook();
- sh = wb.createSheet();
- sh.setDefaultColumnWidth((short)10);
- assertEquals(10, sh.getDefaultColumnWidth());
- assertEquals(256*10, sh.getColumnWidth((short)0));
- assertEquals(256*10, sh.getColumnWidth((short)1));
- assertEquals(256*10, sh.getColumnWidth((short)2));
- for (char i = 'D'; i <= 'F'; i++) {
- short w = (short)(256*12);
- sh.setColumnWidth((short)i, w);
- assertEquals(w, sh.getColumnWidth((short)i));
- }
-
- //serialize and read again
- wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
-
- sh = wb.getSheetAt(0);
- assertEquals(10, sh.getDefaultColumnWidth());
- //columns A-C have default width
- assertEquals(256*10, sh.getColumnWidth((short)0));
- assertEquals(256*10, sh.getColumnWidth((short)1));
- assertEquals(256*10, sh.getColumnWidth((short)2));
- //columns D-F have custom width
- for (char i = 'D'; i <= 'F'; i++) {
- short w = (short)(256*12);
- assertEquals(w, sh.getColumnWidth((short)i));
- }
- }
-
- /**
- * Some utilities write Excel files without the ROW records.
- * Excel, ooo, and google docs are OK with this.
- * Now POI is too.
- */
- public void testMissingRowRecords_bug41187() {
- HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex41187-19267.xls");
-
- HSSFSheet sheet = wb.getSheetAt(0);
- HSSFRow row = sheet.getRow(0);
- if(row == null) {
- throw new AssertionFailedError("Identified bug 41187 a");
- }
- if (row.getHeight() == 0) {
- throw new AssertionFailedError("Identified bug 41187 b");
- }
- assertEquals("Hi Excel!", row.getCell(0).getRichStringCellValue().getString());
- // check row height for 'default' flag
- assertEquals((short)0xFF, row.getHeight());
-
- HSSFTestDataSamples.writeOutAndReadBack(wb);
- }
+ private static HSSFWorkbook openSample(String sampleFileName) {
+ return HSSFTestDataSamples.openSampleWorkbook(sampleFileName);
+ }
+
+ /**
+ * Test the gridset field gets set as expected.
+ */
+ public void testBackupRecord() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet s = wb.createSheet();
+ Sheet sheet = s.getSheet();
+
+ assertEquals(true, sheet.getGridsetRecord().getGridset());
+ s.setGridsPrinted(true);
+ assertEquals(false, sheet.getGridsetRecord().getGridset());
+ }
+
+ /**
+ * Test vertically centered output.
+ */
+ public void testVerticallyCenter() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet s = wb.createSheet();
+ Sheet sheet = s.getSheet();
+ VCenterRecord record = sheet.getPageSettings().getVCenter();
+
+ assertEquals(false, record.getVCenter());
+ s.setVerticallyCenter(true);
+ assertEquals(true, record.getVCenter());
+
+ // wb.write(new FileOutputStream("c:\\test.xls"));
+ }
+
+ /**
+ * Test horizontally centered output.
+ */
+ public void testHorizontallyCenter() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet s = wb.createSheet();
+ Sheet sheet = s.getSheet();
+ HCenterRecord record = sheet.getPageSettings().getHCenter();
+
+ assertEquals(false, record.getHCenter());
+ s.setHorizontallyCenter(true);
+ assertEquals(true, record.getHCenter());
+ }
+
+
+ /**
+ * Test WSBboolRecord fields get set in the user model.
+ */
+ public void testWSBool() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet s = wb.createSheet();
+ Sheet sheet = s.getSheet();
+ WSBoolRecord record =
+ (WSBoolRecord) sheet.findFirstRecordBySid(WSBoolRecord.sid);
+
+ // Check defaults
+ assertEquals(true, record.getAlternateExpression());
+ assertEquals(true, record.getAlternateFormula());
+ assertEquals(false, record.getAutobreaks());
+ assertEquals(false, record.getDialog());
+ assertEquals(false, record.getDisplayGuts());
+ assertEquals(true, record.getFitToPage());
+ assertEquals(false, record.getRowSumsBelow());
+ assertEquals(false, record.getRowSumsRight());
+
+ // Alter
+ s.setAlternativeExpression(false);
+ s.setAlternativeFormula(false);
+ s.setAutobreaks(true);
+ s.setDialog(true);
+ s.setDisplayGuts(true);
+ s.setFitToPage(false);
+ s.setRowSumsBelow(true);
+ s.setRowSumsRight(true);
+
+ // Check
+ assertEquals(false, record.getAlternateExpression());
+ assertEquals(false, record.getAlternateFormula());
+ assertEquals(true, record.getAutobreaks());
+ assertEquals(true, record.getDialog());
+ assertEquals(true, record.getDisplayGuts());
+ assertEquals(false, record.getFitToPage());
+ assertEquals(true, record.getRowSumsBelow());
+ assertEquals(true, record.getRowSumsRight());
+ assertEquals(false, s.getAlternateExpression());
+ assertEquals(false, s.getAlternateFormula());
+ assertEquals(true, s.getAutobreaks());
+ assertEquals(true, s.getDialog());
+ assertEquals(true, s.getDisplayGuts());
+ assertEquals(false, s.getFitToPage());
+ assertEquals(true, s.getRowSumsBelow());
+ assertEquals(true, s.getRowSumsRight());
+ }
+
+ public void testReadBooleans() {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("Test boolean");
+ HSSFRow row = sheet.createRow(2);
+ HSSFCell cell = row.createCell(9);
+ cell.setCellValue(true);
+ cell = row.createCell(11);
+ cell.setCellValue(true);
+
+ workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+
+ sheet = workbook.getSheetAt(0);
+ row = sheet.getRow(2);
+ assertNotNull(row);
+ assertEquals(2, row.getPhysicalNumberOfCells());
+ }
+
+ public void testRemoveRow() {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("Test boolean");
+ HSSFRow row = sheet.createRow(2);
+ sheet.removeRow(row);
+ }
+
+ public void testRemoveZeroRow() {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("Sheet1");
+ HSSFRow row = sheet.createRow(0);
+ try {
+ sheet.removeRow(row);
+ } catch (IllegalArgumentException e) {
+ if (e.getMessage().equals("Invalid row number (-1) outside allowable range (0..65535)")) {
+ throw new AssertionFailedError("Identified bug 45367");
+ }
+ throw e;
+ }
+ }
+
+ public void testCloneSheet() {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("Test Clone");
+ HSSFRow row = sheet.createRow(0);
+ HSSFCell cell = row.createCell(0);
+ HSSFCell cell2 = row.createCell(1);
+ cell.setCellValue(new HSSFRichTextString("clone_test"));
+ cell2.setCellFormula("sin(1)");
+
+ HSSFSheet clonedSheet = workbook.cloneSheet(0);
+ HSSFRow clonedRow = clonedSheet.getRow(0);
+
+ //Check for a good clone
+ assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test");
+
+ //Check that the cells are not somehow linked
+ cell.setCellValue(new HSSFRichTextString("Difference Check"));
+ cell2.setCellFormula("cos(2)");
+ if ("Difference Check".equals(clonedRow.getCell(0).getRichStringCellValue().getString())) {
+ fail("string cell not properly cloned");
+ }
+ if ("COS(2)".equals(clonedRow.getCell(1).getCellFormula())) {
+ fail("formula cell not properly cloned");
+ }
+ assertEquals(clonedRow.getCell(0).getRichStringCellValue().getString(), "clone_test");
+ assertEquals(clonedRow.getCell(1).getCellFormula(), "SIN(1)");
+ }
+
+ /** tests that the sheet name for multiple clones of the same sheet is unique
+ * BUG 37416
+ */
+ public void testCloneSheetMultipleTimes() {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet("Test Clone");
+ HSSFRow row = sheet.createRow(0);
+ HSSFCell cell = row.createCell(0);
+ cell.setCellValue(new HSSFRichTextString("clone_test"));
+ //Clone the sheet multiple times
+ workbook.cloneSheet(0);
+ workbook.cloneSheet(0);
+
+ assertNotNull(workbook.getSheet("Test Clone"));
+ assertNotNull(workbook.getSheet("Test Clone (2)"));
+ assertEquals("Test Clone (3)", workbook.getSheetName(2));
+ assertNotNull(workbook.getSheet("Test Clone (3)"));
+
+ workbook.removeSheetAt(0);
+ workbook.removeSheetAt(0);
+ workbook.removeSheetAt(0);
+ workbook.createSheet("abc ( 123)");
+ workbook.cloneSheet(0);
+ assertEquals("abc (124)", workbook.getSheetName(1));
+ }
+
+ /**
+ * Setting landscape and portrait stuff on new sheets
+ */
+ public void testPrintSetupLandscapeNew() throws Exception {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheetL = workbook.createSheet("LandscapeS");
+ HSSFSheet sheetP = workbook.createSheet("LandscapeP");
+
+ // Check two aspects of the print setup
+ assertFalse(sheetL.getPrintSetup().getLandscape());
+ assertFalse(sheetP.getPrintSetup().getLandscape());
+ assertEquals(0, sheetL.getPrintSetup().getCopies());
+ assertEquals(0, sheetP.getPrintSetup().getCopies());
+
+ // Change one on each
+ sheetL.getPrintSetup().setLandscape(true);
+ sheetP.getPrintSetup().setCopies((short)3);
+
+ // Check taken
+ assertTrue(sheetL.getPrintSetup().getLandscape());
+ assertFalse(sheetP.getPrintSetup().getLandscape());
+ assertEquals(0, sheetL.getPrintSetup().getCopies());
+ assertEquals(3, sheetP.getPrintSetup().getCopies());
+
+ // Save and re-load, and check still there
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ workbook.write(baos);
+ workbook = new HSSFWorkbook(new ByteArrayInputStream(baos.toByteArray()));
+
+ assertTrue(sheetL.getPrintSetup().getLandscape());
+ assertFalse(sheetP.getPrintSetup().getLandscape());
+ assertEquals(0, sheetL.getPrintSetup().getCopies());
+ assertEquals(3, sheetP.getPrintSetup().getCopies());
+ }
+
+ /**
+ * Setting landscape and portrait stuff on existing sheets
+ */
+ public void testPrintSetupLandscapeExisting() {
+ HSSFWorkbook workbook = openSample("SimpleWithPageBreaks.xls");
+
+ assertEquals(3, workbook.getNumberOfSheets());
+
+ HSSFSheet sheetL = workbook.getSheetAt(0);
+ HSSFSheet sheetPM = workbook.getSheetAt(1);
+ HSSFSheet sheetLS = workbook.getSheetAt(2);
+
+ // Check two aspects of the print setup
+ assertFalse(sheetL.getPrintSetup().getLandscape());
+ assertTrue(sheetPM.getPrintSetup().getLandscape());
+ assertTrue(sheetLS.getPrintSetup().getLandscape());
+ assertEquals(1, sheetL.getPrintSetup().getCopies());
+ assertEquals(1, sheetPM.getPrintSetup().getCopies());
+ assertEquals(1, sheetLS.getPrintSetup().getCopies());
+
+ // Change one on each
+ sheetL.getPrintSetup().setLandscape(true);
+ sheetPM.getPrintSetup().setLandscape(false);
+ sheetPM.getPrintSetup().setCopies((short)3);
+
+ // Check taken
+ assertTrue(sheetL.getPrintSetup().getLandscape());
+ assertFalse(sheetPM.getPrintSetup().getLandscape());
+ assertTrue(sheetLS.getPrintSetup().getLandscape());
+ assertEquals(1, sheetL.getPrintSetup().getCopies());
+ assertEquals(3, sheetPM.getPrintSetup().getCopies());
+ assertEquals(1, sheetLS.getPrintSetup().getCopies());
+
+ // Save and re-load, and check still there
+ workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+
+ assertTrue(sheetL.getPrintSetup().getLandscape());
+ assertFalse(sheetPM.getPrintSetup().getLandscape());
+ assertTrue(sheetLS.getPrintSetup().getLandscape());
+ assertEquals(1, sheetL.getPrintSetup().getCopies());
+ assertEquals(3, sheetPM.getPrintSetup().getCopies());
+ assertEquals(1, sheetLS.getPrintSetup().getCopies());
+ }
+
+ public void testGroupRows() {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet s = workbook.createSheet();
+ HSSFRow r1 = s.createRow(0);
+ HSSFRow r2 = s.createRow(1);
+ HSSFRow r3 = s.createRow(2);
+ HSSFRow r4 = s.createRow(3);
+ HSSFRow r5 = s.createRow(4);
+
+ assertEquals(0, r1.getOutlineLevel());
+ assertEquals(0, r2.getOutlineLevel());
+ assertEquals(0, r3.getOutlineLevel());
+ assertEquals(0, r4.getOutlineLevel());
+ assertEquals(0, r5.getOutlineLevel());
+
+ s.groupRow(2,3);
+
+ assertEquals(0, r1.getOutlineLevel());
+ assertEquals(0, r2.getOutlineLevel());
+ assertEquals(1, r3.getOutlineLevel());
+ assertEquals(1, r4.getOutlineLevel());
+ assertEquals(0, r5.getOutlineLevel());
+
+ // Save and re-open
+ workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+
+ s = workbook.getSheetAt(0);
+ r1 = s.getRow(0);
+ r2 = s.getRow(1);
+ r3 = s.getRow(2);
+ r4 = s.getRow(3);
+ r5 = s.getRow(4);
+
+ assertEquals(0, r1.getOutlineLevel());
+ assertEquals(0, r2.getOutlineLevel());
+ assertEquals(1, r3.getOutlineLevel());
+ assertEquals(1, r4.getOutlineLevel());
+ assertEquals(0, r5.getOutlineLevel());
+ }
+
+ public void testGroupRowsExisting() {
+ HSSFWorkbook workbook = openSample("NoGutsRecords.xls");
+
+ HSSFSheet s = workbook.getSheetAt(0);
+ HSSFRow r1 = s.getRow(0);
+ HSSFRow r2 = s.getRow(1);
+ HSSFRow r3 = s.getRow(2);
+ HSSFRow r4 = s.getRow(3);
+ HSSFRow r5 = s.getRow(4);
+ HSSFRow r6 = s.getRow(5);
+
+ assertEquals(0, r1.getOutlineLevel());
+ assertEquals(0, r2.getOutlineLevel());
+ assertEquals(0, r3.getOutlineLevel());
+ assertEquals(0, r4.getOutlineLevel());
+ assertEquals(0, r5.getOutlineLevel());
+ assertEquals(0, r6.getOutlineLevel());
+
+ // This used to complain about lacking guts records
+ s.groupRow(2, 4);
+
+ assertEquals(0, r1.getOutlineLevel());
+ assertEquals(0, r2.getOutlineLevel());
+ assertEquals(1, r3.getOutlineLevel());
+ assertEquals(1, r4.getOutlineLevel());
+ assertEquals(1, r5.getOutlineLevel());
+ assertEquals(0, r6.getOutlineLevel());
+
+ // Save and re-open
+ try {
+ workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+ } catch (OutOfMemoryError e) {
+ throw new AssertionFailedError("Identified bug 39903");
+ }
+
+ s = workbook.getSheetAt(0);
+ r1 = s.getRow(0);
+ r2 = s.getRow(1);
+ r3 = s.getRow(2);
+ r4 = s.getRow(3);
+ r5 = s.getRow(4);
+ r6 = s.getRow(5);
+
+ assertEquals(0, r1.getOutlineLevel());
+ assertEquals(0, r2.getOutlineLevel());
+ assertEquals(1, r3.getOutlineLevel());
+ assertEquals(1, r4.getOutlineLevel());
+ assertEquals(1, r5.getOutlineLevel());
+ assertEquals(0, r6.getOutlineLevel());
+ }
+
+ public void testGetDrawings() {
+ HSSFWorkbook wb1c = openSample("WithChart.xls");
+ HSSFWorkbook wb2c = openSample("WithTwoCharts.xls");
+
+ // 1 chart sheet -> data on 1st, chart on 2nd
+ assertNotNull(wb1c.getSheetAt(0).getDrawingPatriarch());
+ assertNotNull(wb1c.getSheetAt(1).getDrawingPatriarch());
+ assertFalse(wb1c.getSheetAt(0).getDrawingPatriarch().containsChart());
+ assertTrue(wb1c.getSheetAt(1).getDrawingPatriarch().containsChart());
+
+ // 2 chart sheet -> data on 1st, chart on 2nd+3rd
+ assertNotNull(wb2c.getSheetAt(0).getDrawingPatriarch());
+ assertNotNull(wb2c.getSheetAt(1).getDrawingPatriarch());
+ assertNotNull(wb2c.getSheetAt(2).getDrawingPatriarch());
+ assertFalse(wb2c.getSheetAt(0).getDrawingPatriarch().containsChart());
+ assertTrue(wb2c.getSheetAt(1).getDrawingPatriarch().containsChart());
+ assertTrue(wb2c.getSheetAt(2).getDrawingPatriarch().containsChart());
+ }
+
+ /**
+ * Test that the ProtectRecord is included when creating or cloning a sheet
+ */
+ public void testProtect() {
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet hssfSheet = workbook.createSheet();
+ Sheet sheet = hssfSheet.getSheet();
+ ProtectRecord protect = sheet.getProtect();
+
+ assertFalse(protect.getProtect());
+
+ // This will tell us that cloneSheet, and by extension,
+ // the list forms of createSheet leave us with an accessible
+ // ProtectRecord.
+ hssfSheet.setProtect(true);
+ Sheet cloned = sheet.cloneSheet();
+ assertNotNull(cloned.getProtect());
+ assertTrue(hssfSheet.getProtect());
+ }
+
+ public void testProtectSheet() {
+ short expected = (short)0xfef1;
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet s = wb.createSheet();
+ s.protectSheet("abcdefghij");
+ Sheet sheet = s.getSheet();
+ ProtectRecord protect = sheet.getProtect();
+ PasswordRecord pass = sheet.getPassword();
+ assertTrue("protection should be on",protect.getProtect());
+ assertTrue("object protection should be on",sheet.isProtected()[1]);
+ assertTrue("scenario protection should be on",sheet.isProtected()[2]);
+ assertEquals("well known value for top secret hash should be "+Integer.toHexString(expected).substring(4),expected,pass.getPassword());
+ }
+
+
+ public void testZoom() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet();
+ assertEquals(-1, sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid));
+ sheet.setZoom(3,4);
+ assertTrue(sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid) > 0);
+ SCLRecord sclRecord = (SCLRecord) sheet.getSheet().findFirstRecordBySid(SCLRecord.sid);
+ assertEquals(3, sclRecord.getNumerator());
+ assertEquals(4, sclRecord.getDenominator());
+
+ int sclLoc = sheet.getSheet().findFirstRecordLocBySid(SCLRecord.sid);
+ int window2Loc = sheet.getSheet().findFirstRecordLocBySid(WindowTwoRecord.sid);
+ assertTrue(sclLoc == window2Loc + 1);
+ }
+
+
+ /**
+ * When removing one merged region, it would break
+ *
+ */
+ public void testRemoveMerged() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet();
+ CellRangeAddress region = new CellRangeAddress(0, 1, 0, 1);
+ sheet.addMergedRegion(region);
+ region = new CellRangeAddress(1, 2, 0, 1);
+ sheet.addMergedRegion(region);
+
+ sheet.removeMergedRegion(0);
+
+ region = sheet.getMergedRegion(0);
+ assertEquals("Left over region should be starting at row 1", 1, region.getFirstRow());
+
+ sheet.removeMergedRegion(0);
+
+ assertEquals("there should be no merged regions left!", 0, sheet.getNumMergedRegions());
+
+ //an, add, remove, get(0) would null pointer
+ sheet.addMergedRegion(region);
+ assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
+ sheet.removeMergedRegion(0);
+ assertEquals("there should now be zero merged regions!", 0, sheet.getNumMergedRegions());
+ //add it again!
+ region.setLastRow(4);
+
+ sheet.addMergedRegion(region);
+ assertEquals("there should now be one merged region!", 1, sheet.getNumMergedRegions());
+
+ //should exist now!
+ assertTrue("there isn't more than one merged region in there", 1 <= sheet.getNumMergedRegions());
+ region = sheet.getMergedRegion(0);
+ assertEquals("the merged row to doesnt match the one we put in ", 4, region.getLastRow());
+ }
+
+ public void testShiftMerged() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet();
+ HSSFRow row = sheet.createRow(0);
+ HSSFCell cell = row.createCell(0);
+ cell.setCellValue(new HSSFRichTextString("first row, first cell"));
+
+ row = sheet.createRow(1);
+ cell = row.createCell(1);
+ cell.setCellValue(new HSSFRichTextString("second row, second cell"));
+
+ CellRangeAddress region = new CellRangeAddress(1, 1, 0, 1);
+ sheet.addMergedRegion(region);
+
+ sheet.shiftRows(1, 1, 1);
+
+ region = sheet.getMergedRegion(0);
+ assertEquals("Merged region not moved over to row 2", 2, region.getFirstRow());
+ }
+
+ /**
+ * Tests the display of gridlines, formulas, and rowcolheadings.
+ * @author Shawn Laubach (slaubach at apache dot org)
+ */
+ public void testDisplayOptions() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet sheet = wb.createSheet();
+
+ wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+ sheet = wb.getSheetAt(0);
+
+ assertEquals(sheet.isDisplayGridlines(), true);
+ assertEquals(sheet.isDisplayRowColHeadings(), true);
+ assertEquals(sheet.isDisplayFormulas(), false);
+
+ sheet.setDisplayGridlines(false);
+ sheet.setDisplayRowColHeadings(false);
+ sheet.setDisplayFormulas(true);
+
+ wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+ sheet = wb.getSheetAt(0);
+
+ assertEquals(sheet.isDisplayGridlines(), false);
+ assertEquals(sheet.isDisplayRowColHeadings(), false);
+ assertEquals(sheet.isDisplayFormulas(), true);
+ }
+
+
+ /**
+ * Make sure the excel file loads work
+ *
+ */
+ public void testPageBreakFiles() {
+ HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls");
+
+ HSSFSheet sheet = wb.getSheetAt(0);
+ assertNotNull(sheet);
+
+ assertEquals("1 row page break", 1, sheet.getRowBreaks().length);
+ assertEquals("1 column page break", 1, sheet.getColumnBreaks().length);
+
+ assertTrue("No row page break", sheet.isRowBroken(22));
+ assertTrue("No column page break", sheet.isColumnBroken((short)4));
+
+ sheet.setRowBreak(10);
+ sheet.setColumnBreak((short)13);
+
+ assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
+ assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
+
+ wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+ sheet = wb.getSheetAt(0);
+
+ assertTrue("No row page break", sheet.isRowBroken(22));
+ assertTrue("No column page break", sheet.isColumnBroken((short)4));
+
+ assertEquals("row breaks number", 2, sheet.getRowBreaks().length);
+ assertEquals("column breaks number", 2, sheet.getColumnBreaks().length);
+ }
+
+ public void testDBCSName () {
+ HSSFWorkbook wb = openSample("DBCSSheetName.xls");
+ wb.getSheetAt(1);
+ assertEquals ("DBCS Sheet Name 2", wb.getSheetName(1),"\u090f\u0915" );
+ assertEquals("DBCS Sheet Name 1", wb.getSheetName(0),"\u091c\u093e");
+ }
+
+ /**
+ * Testing newly added method that exposes the WINDOW2.toprow
+ * parameter to allow setting the toprow in the visible view
+ * of the sheet when it is first opened.
+ */
+ public void testTopRow() {
+ HSSFWorkbook wb = openSample("SimpleWithPageBreaks.xls");
+
+ HSSFSheet sheet = wb.getSheetAt(0);
+ assertNotNull(sheet);
+
+ short toprow = (short) 100;
+ short leftcol = (short) 50;
+ sheet.showInPane(toprow,leftcol);
+ assertEquals("HSSFSheet.getTopRow()", toprow, sheet.getTopRow());
+ assertEquals("HSSFSheet.getLeftCol()", leftcol, sheet.getLeftCol());
+ }
+
+ /** cell with formula becomes null on cloning a sheet*/
+ public void test35084() {
+
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFSheet s = wb.createSheet("Sheet1");
+ HSSFRow r = s.createRow(0);
+ r.createCell(0).setCellValue(1);
+ r.createCell(1).setCellFormula("A1*2");
+ HSSFSheet s1 = wb.cloneSheet(0);
+ r = s1.getRow(0);
+ assertEquals("double", r.getCell(0).getNumericCellValue(), 1, 0); // sanity check
+ assertNotNull(r.getCell(1));
+ assertEquals("formula", r.getCell(1).getCellFormula(), "A1*2");
+ }
+
+ /** test that new default column styles get applied */
+ public void testDefaultColumnStyle() {
+ HSSFWorkbook wb = new HSSFWorkbook();
+ HSSFCellStyle style = wb.createCellStyle();
+ HSSFSheet s = wb.createSheet();
+ s.setDefaultColumnStyle((short) 0, style);
+ HSSFRow r = s.createRow(0);
+ HSSFCell c = r.createCell(0);
+ assertEquals("style should match", style.getIndex(), c.getCellStyle().getIndex());
+ }
+
+
+ /**
+ *
+ */
+ public void testAddEmptyRow() {
+ //try to add 5 empty rows to a new sheet
+ HSSFWorkbook workbook = new HSSFWorkbook();
+ HSSFSheet sheet = workbook.createSheet();
+ for (int i = 0; i < 5; i++) {
+ sheet.createRow(i);
+ }
+
+ workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+
+ //try adding empty rows in an existing worksheet
+ workbook = openSample("Simple.xls");
+
+ sheet = workbook.getSheetAt(0);
+ for (int i = 3; i < 10; i++) sheet.createRow(i);
+
+ workbook = HSSFTestDataSamples.writeOutAndReadBack(workbook);
+ }
+
+ public void testAutoSizeColumn() {
+ HSSFWorkbook wb = openSample("43902.xls");
+ String sheetName = "my sheet";
+ HSSFSheet sheet = wb.getSheet(sheetName);
+
+ // Can't use literal numbers for column sizes, as
+ // will come out with different values on different
+ // machines based on the fonts available.
+ // So, we use ranges, which are pretty large, but
+ // thankfully don't overlap!
+ int minWithRow1And2 = 6400;
+ int maxWithRow1And2 = 7800;
+ int minWithRow1Only = 2750;
+ int maxWithRow1Only = 3300;
+
+ // autoSize the first column and check its size before the merged region (1,0,1,1) is set:
+ // it has to be based on the 2nd row width
+ sheet.autoSizeColumn((short)0);
+ assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) >= minWithRow1And2);
+ assertTrue("Column autosized with only one row: wrong width", sheet.getColumnWidth((short)0) <= maxWithRow1And2);
+
+ //create a region over the 2nd row and auto size the first column
+ sheet.addMergedRegion(new CellRangeAddress(1,1,0,1));
+ sheet.autoSizeColumn((short)0);
+ HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb);
+
+ // check that the autoSized column width has ignored the 2nd row
+ // because it is included in a merged region (Excel like behavior)
+ HSSFSheet sheet2 = wb2.getSheet(sheetName);
+ assertTrue(sheet2.getColumnWidth((short)0) >= minWithRow1Only);
+ assertTrue(sheet2.getColumnWidth((short)0) <= maxWithRow1Only);
+
+ // remove the 2nd row merged region and check that the 2nd row value is used to the autoSizeColumn width
+ sheet2.removeMergedRegion(1);
+ sheet2.autoSizeColumn((short)0);
+ HSSFWorkbook wb3 = HSSFTestDataSamples.writeOutAndReadBack(wb2);
+ HSSFSheet sheet3 = wb3.getSheet(sheetName);
+ assertTrue(sheet3.getColumnWidth((short)0) >= minWithRow1And2);
+ assertTrue(sheet3.getColumnWidth((short)0) <= maxWithRow1And2);
+ }
+
+ /**
+ * Setting ForceFormulaRecalculation on sheets
+ */
+ public void testForceRecalculation() throws Exception {
+ HSSFWorkbook workbook = openSample("UncalcedRecord.xls");
+
+ HSSFSheet sheet = workbook.getSheetAt(0);
+ HSSFSheet sheet2 = workbook.getSheetAt(0);
+ HSSFRow row = sheet.getRow(0);
+ row.createCell(0).setCellValue(5);
+ row.createCell(1).setCellValue(8);
+ assertFalse(sheet.getForceFormulaRecalculation());
+ assertFalse(sheet2.getForceFormulaRecalculation());
+
+ // Save and manually verify that on column C we have 0, value in template
+ File tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_err.xls" );
+ tempFile.delete();
+ FileOutputStream fout = new FileOutputStream( tempFile );
+ workbook.write( fout );
+ fout.close();
+ sheet.setForceFormulaRecalculation(true);
+ assertTrue(sheet.getForceFormulaRecalculation());
+
+ // Save and manually verify that on column C we have now 13, calculated value
+ tempFile = new File(System.getProperty("java.io.tmpdir")+"/uncalced_succ.xls" );
+ tempFile.delete();
+ fout = new FileOutputStream( tempFile );
+ workbook.write( fout );
+ fout.close();
+
+ // Try it can be opened
+ HSSFWorkbook wb2 = new HSSFWorkbook(new FileInputStream(tempFile));
+
+ // And check correct sheet settings found
+ sheet = wb2.getSheetAt(0);
+ sheet2 = wb2.getSheetAt(1);
+ assertTrue(sheet.getForceFormulaRecalculation());
+ assertFalse(sheet2.getForceFormulaRecalculation());
+
+ // Now turn if back off again
+ sheet.setForceFormulaRecalculation(false);
+
+ fout = new FileOutputStream( tempFile );
+ wb2.write( fout );
+ fout.close();
+ wb2 = new HSSFWorkbook(new FileInputStream(tempFile));
+
+ assertFalse(wb2.getSheetAt(0).getForceFormulaRecalculation());
+ assertFalse(wb2.getSheetAt(1).getForceFormulaRecalculation());
+ assertFalse(wb2.getSheetAt(2).getForceFormulaRecalculation());
+
+ // Now add a new sheet, and check things work
+ // with old ones unset, new one set
+ HSSFSheet s4 = wb2.createSheet();
+ s4.setForceFormulaRecalculation(true);
+
+ assertFalse(sheet.getForceFormulaRecalculation());
+ assertFalse(sheet2.getForceFormulaRecalculation());
+ assertTrue(s4.getForceFormulaRecalculation());
+
+ fout = new FileOutputStream( tempFile );
+ wb2.write( fout );
+ fout.close();
+
+ HSSFWorkbook wb3 = new HSSFWorkbook(new FileInputStream(tempFile));
+ assertFalse(wb3.getSheetAt(0).getForceFormulaRecalculation());
+ assertFalse(wb3.getSheetAt(1).getForceFormulaRecalculation());
+ assertFalse(wb3.getSheetAt(2).getForceFormulaRecalculation());
+ assertTrue(wb3.getSheetAt(3).getForceFormulaRecalculation());
+ }
+
+ public void testColumnWidth() {
+ //check we can correctly read column widths from a reference workbook
+ HSSFWorkbook wb = openSample("colwidth.xls");
+
+ //reference values
+ int[] ref = {365, 548, 731, 914, 1097, 1280, 1462, 1645, 1828, 2011, 2194, 2377, 2560, 2742, 2925, 3108, 3291, 3474, 3657};
+
+ HSSFSheet sh = wb.getSheetAt(0);
+ for (char i = 'A'; i <= 'S'; i++) {
+ int idx = i - 'A';
+ int w = sh.getColumnWidth((short)idx);
+ assertEquals(ref[idx], w);
+ }
+
+ //the second sheet doesn't have overridden column widths
+ sh = wb.getSheetAt(1);
+ int def_width = sh.getDefaultColumnWidth();
+ for (char i = 'A'; i <= 'S'; i++) {
+ int idx = i - 'A';
+ int w = sh.getColumnWidth((short)idx);
+ //getDefaultColumnWidth returns width measued in characters
+ //getColumnWidth returns width measued in 1/256th units
+ assertEquals(def_width*256, w);
+ }
+
+ //test new workbook
+ wb = new HSSFWorkbook();
+ sh = wb.createSheet();
+ sh.setDefaultColumnWidth((short)10);
+ assertEquals(10, sh.getDefaultColumnWidth());
+ assertEquals(256*10, sh.getColumnWidth((short)0));
+ assertEquals(256*10, sh.getColumnWidth((short)1));
+ assertEquals(256*10, sh.getColumnWidth((short)2));
+ for (char i = 'D'; i <= 'F'; i++) {
+ short w = (short)(256*12);
+ sh.setColumnWidth((short)i, w);
+ assertEquals(w, sh.getColumnWidth((short)i));
+ }
+
+ //serialize and read again
+ wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+
+ sh = wb.getSheetAt(0);
+ assertEquals(10, sh.getDefaultColumnWidth());
+ //columns A-C have default width
+ assertEquals(256*10, sh.getColumnWidth((short)0));
+ assertEquals(256*10, sh.getColumnWidth((short)1));
+ assertEquals(256*10, sh.getColumnWidth((short)2));
+ //columns D-F have custom width
+ for (char i = 'D'; i <= 'F'; i++) {
+ short w = (short)(256*12);
+ assertEquals(w, sh.getColumnWidth((short)i));
+ }
+ }
+
+ /**
+ * Some utilities write Excel files without the ROW records.
+ * Excel, ooo, and google docs are OK with this.
+ * Now POI is too.
+ */
+ public void testMissingRowRecords_bug41187() {
+ HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex41187-19267.xls");
+
+ HSSFSheet sheet = wb.getSheetAt(0);
+ HSSFRow row = sheet.getRow(0);
+ if(row == null) {
+ throw new AssertionFailedError("Identified bug 41187 a");
+ }
+ if (row.getHeight() == 0) {
+ throw new AssertionFailedError("Identified bug 41187 b");
+ }
+ assertEquals("Hi Excel!", row.getCell(0).getRichStringCellValue().getString());
+ // check row height for 'default' flag
+ assertEquals((short)0xFF, row.getHeight());
+
+ HSSFTestDataSamples.writeOutAndReadBack(wb);
+ }
+
+ /**
+ * If we clone a sheet containing drawings,
+ * we must allocate a new ID of the drawing group and re-create shape IDs
+ *
+ * See bug #45720.
+ */
+ public void testCloneSheetWithDrawings() {
+ HSSFWorkbook wb1 = HSSFTestDataSamples.openSampleWorkbook("45720.xls");
+
+ HSSFSheet sheet1 = wb1.getSheetAt(0);
+
+ wb1.getWorkbook().findDrawingGroup();
+ DrawingManager2 dm1 = wb1.getWorkbook().getDrawingManager();
+
+ wb1.cloneSheet(0);
+
+ HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb1);
+ wb2.getWorkbook().findDrawingGroup();
+ DrawingManager2 dm2 = wb2.getWorkbook().getDrawingManager();
+
+ //check EscherDggRecord - a workbook-level registry of drawing objects
+ assertEquals(dm1.getDgg().getMaxDrawingGroupId() + 1, dm2.getDgg().getMaxDrawingGroupId());
+
+ HSSFSheet sheet2 = wb2.getSheetAt(1);
+
+ //check that id of the drawing group was updated
+ EscherDgRecord dg1 = (EscherDgRecord)sheet1.getDrawingEscherAggregate().findFirstWithId(EscherDgRecord.RECORD_ID);
+ EscherDgRecord dg2 = (EscherDgRecord)sheet2.getDrawingEscherAggregate().findFirstWithId(EscherDgRecord.RECORD_ID);
+ int dg_id_1 = dg1.getOptions() >> 4;
+ int dg_id_2 = dg2.getOptions() >> 4;
+ assertEquals(dg_id_1 + 1, dg_id_2);
+
+ //TODO: check shapeId in the cloned sheet
+ }
}
assertEquals("On2", nr.getNameText());
assertEquals(0, nr.getSheetNumber());
assertEquals(1, nr.getExternSheetNumber());
- assertEquals(1, nr.getNameDefinition().size());
+ assertEquals(1, nr.getNameDefinition().length);
- ptg = (Area3DPtg)nr.getNameDefinition().get(0);
+ ptg = (Area3DPtg)nr.getNameDefinition()[0];
assertEquals(1, ptg.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn());
assertEquals(0, ptg.getFirstRow());
assertEquals("OnOne", nr.getNameText());
assertEquals(0, nr.getSheetNumber());
assertEquals(0, nr.getExternSheetNumber());
- assertEquals(1, nr.getNameDefinition().size());
+ assertEquals(1, nr.getNameDefinition().length);
- ptg = (Area3DPtg)nr.getNameDefinition().get(0);
+ ptg = (Area3DPtg)nr.getNameDefinition()[0];
assertEquals(0, ptg.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn());
assertEquals(2, ptg.getFirstRow());
assertEquals("OnSheet3", nr.getNameText());
assertEquals(0, nr.getSheetNumber());
assertEquals(2, nr.getExternSheetNumber());
- assertEquals(1, nr.getNameDefinition().size());
+ assertEquals(1, nr.getNameDefinition().length);
- ptg = (Area3DPtg)nr.getNameDefinition().get(0);
+ ptg = (Area3DPtg)nr.getNameDefinition()[0];
assertEquals(2, ptg.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn());
assertEquals(0, ptg.getFirstRow());
import java.io.IOException;
import java.io.InputStream;
-import java.util.List;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.MemFuncPtg;
+import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.UnionPtg;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFName;
public void testReferenceWithSheet() {
AreaReference ar;
- ar = new AreaReference("Tabelle1!B5");
+ ar = new AreaReference("Tabelle1!B5:B5");
assertTrue(ar.isSingleCell());
TestCellReference.confirmCell(ar.getFirstCell(), "Tabelle1", 4, 1, false, false, "Tabelle1!B5");
}
}
- public void testContiguousReferences() throws Exception {
- String refSimple = "$C$10";
+ public void testContiguousReferences() {
+ String refSimple = "$C$10:$C$10";
String ref2D = "$C$10:$D$11";
- String refDCSimple = "$C$10,$D$12,$E$14";
- String refDC2D = "$C$10:$C$11,$D$12,$E$14:$E$20";
+ String refDCSimple = "$C$10:$C$10,$D$12:$D$12,$E$14:$E$14";
+ String refDC2D = "$C$10:$C$11,$D$12:$D$12,$E$14:$E$20";
String refDC3D = "Tabelle1!$C$10:$C$14,Tabelle1!$D$10:$D$12";
// Check that we detect as contiguous properly
assertNotNull(nr);
assertEquals("test", nr.getNameText());
- List def =nr.getNameDefinition();
- assertEquals(4, def.size());
+ Ptg[] def =nr.getNameDefinition();
+ assertEquals(4, def.length);
- MemFuncPtg ptgA = (MemFuncPtg)def.get(0);
- Area3DPtg ptgB = (Area3DPtg)def.get(1);
- Area3DPtg ptgC = (Area3DPtg)def.get(2);
- UnionPtg ptgD = (UnionPtg)def.get(3);
+ MemFuncPtg ptgA = (MemFuncPtg)def[0];
+ Area3DPtg ptgB = (Area3DPtg)def[1];
+ Area3DPtg ptgC = (Area3DPtg)def[2];
+ UnionPtg ptgD = (UnionPtg)def[3];
assertEquals("", ptgA.toFormulaString(wb));
assertEquals(refA, ptgB.toFormulaString(wb));
assertEquals(refB, ptgC.toFormulaString(wb));
private static void confirmResolveCellRef(HSSFWorkbook wb, CellReference cref) {
HSSFSheet s = wb.getSheet(cref.getSheetName());
HSSFRow r = s.getRow(cref.getRow());
- HSSFCell c = r.getCell(cref.getCol());
+ HSSFCell c = r.getCell((int)cref.getCol());
assertNotNull(c);
}
public void testSpecialSheetNames() {
AreaReference ar;
- ar = new AreaReference("'Sheet A'!A1");
+ ar = new AreaReference("'Sheet A'!A1:A1");
confirmAreaSheetName(ar, "Sheet A", "'Sheet A'!A1");
- ar = new AreaReference("'Hey! Look Here!'!A1");
+ ar = new AreaReference("'Hey! Look Here!'!A1:A1");
confirmAreaSheetName(ar, "Hey! Look Here!", "'Hey! Look Here!'!A1");
ar = new AreaReference("'O''Toole'!A1:B2");
assertEquals(expectedFullText, ar.formatAsString());
}
- public static void main(String[] args) {
- junit.textui.TestRunner.run(TestAreaReference.class);
- }
+ public void testWholeColumnRefs() {
+ confirmWholeColumnRef("A:A", 0, 0, false, false);
+ confirmWholeColumnRef("$C:D", 2, 3, true, false);
+ confirmWholeColumnRef("AD:$AE", 29, 30, false, true);
+
+ }
+
+ private static void confirmWholeColumnRef(String ref, int firstCol, int lastCol, boolean firstIsAbs, boolean lastIsAbs) {
+ AreaReference ar = new AreaReference(ref);
+ confirmCell(ar.getFirstCell(), 0, firstCol, true, firstIsAbs);
+ confirmCell(ar.getLastCell(), 0xFFFF, lastCol, true, lastIsAbs);
+ }
+
+ private static void confirmCell(CellReference cell, int row, int col, boolean isRowAbs,
+ boolean isColAbs) {
+ assertEquals(row, cell.getRow());
+ assertEquals(col, cell.getCol());
+ assertEquals(isRowAbs, cell.isRowAbsolute());
+ assertEquals(isColAbs, cell.isColAbsolute());
+ }
}