https://svn.apache.org/repos/asf/poi/trunk ........ r691533 | yegor | 2008-09-03 09:04:07 +0100 (Wed, 03 Sep 2008) | 1 line fixed bug #45728: SlideShow.reorderSlide didn't work properly ........ r691687 | josh | 2008-09-03 18:03:02 +0100 (Wed, 03 Sep 2008) | 1 line Fixed ArrayPtg.toString to not crash when partially initialised ........ r691740 | josh | 2008-09-03 20:22:53 +0100 (Wed, 03 Sep 2008) | 1 line Initial work on bug 45720 - copy 'FilterDatabase' named record when cloning sheets. Some clean-up in NameRecord. ........ r692239 | josh | 2008-09-04 21:58:37 +0100 (Thu, 04 Sep 2008) | 1 line Fixed 2 small bugs in RelationalOperationEval (added junits). Refactored hierarchy. ........ r692241 | josh | 2008-09-04 22:01:48 +0100 (Thu, 04 Sep 2008) | 1 line Fix unused import (correction to r692239) ........ r692243 | josh | 2008-09-04 22:05:50 +0100 (Thu, 04 Sep 2008) | 1 line Fixed compiler warnings, linked junit test to suite ........ r692255 | josh | 2008-09-04 22:32:17 +0100 (Thu, 04 Sep 2008) | 1 line Made HSSFFormulaEvaluator capable of handling simple named ranges ........ r692300 | josh | 2008-09-05 00:16:15 +0100 (Fri, 05 Sep 2008) | 1 line Fix for bug 45376 - added caching to HSSFFormulaEvaluator ........ r692506 | josh | 2008-09-05 19:22:30 +0100 (Fri, 05 Sep 2008) | 1 line Minor fixes for numeric operators - junit added. Some refactoring. ........ r692538 | josh | 2008-09-05 21:38:51 +0100 (Fri, 05 Sep 2008) | 1 line Modified formula evaluator to handle whole column refs ........ r692541 | josh | 2008-09-05 21:43:37 +0100 (Fri, 05 Sep 2008) | 1 line reverted changes accidentally submitted with r692538 ........ r692612 | josh | 2008-09-06 06:30:31 +0100 (Sat, 06 Sep 2008) | 1 line Fixes for special cases of lookup functions (test cases added) ........ r692614 | josh | 2008-09-06 07:04:01 +0100 (Sat, 06 Sep 2008) | 1 line Minor fixes to YEARFRAC(). Added ISEVEN() and ISODD(). Added test cases. ........ r692893 | yegor | 2008-09-07 17:30:35 +0100 (Sun, 07 Sep 2008) | 1 line fixed bug #45720: cloneSheet breaks autofilters. ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@692932 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_5_BETA3
@@ -65,6 +65,8 @@ | |||
<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> |
@@ -62,6 +62,8 @@ | |||
<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> |
@@ -235,4 +235,21 @@ public class EscherContainerRecord extends EscherRecord | |||
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); | |||
} | |||
} | |||
} | |||
} |
@@ -67,6 +67,17 @@ public class DrawingManager2 | |||
* @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 ); | |||
@@ -78,7 +89,6 @@ public class DrawingManager2 | |||
{ | |||
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()) | |||
@@ -90,7 +100,6 @@ public class DrawingManager2 | |||
// 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 ); | |||
@@ -98,7 +107,6 @@ public class DrawingManager2 | |||
dgg.setShapeIdMax( result + 1 ); | |||
return result; | |||
} | |||
//////////// Non-public methods ///////////// | |||
/** |
@@ -22,57 +22,8 @@ import java.util.Iterator; | |||
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; | |||
@@ -2206,17 +2157,17 @@ public final class Workbook implements Model { | |||
// 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(); | |||
@@ -2224,7 +2175,7 @@ public final class Workbook implements Model { | |||
dgg = (EscherDggRecord)er; | |||
} | |||
} | |||
if(dgg != null) { | |||
drawingManager = new DrawingManager2(dgg); | |||
return; | |||
@@ -2234,7 +2185,7 @@ public final class Workbook implements Model { | |||
// Look for the DrawingGroup record | |||
int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid); | |||
// If there is one, does it have a EscherDggRecord? | |||
if(dgLoc != -1) { | |||
DrawingGroupRecord dg = | |||
@@ -2246,7 +2197,7 @@ public final class Workbook implements Model { | |||
dgg = (EscherDggRecord)er; | |||
} | |||
} | |||
if(dgg != null) { | |||
drawingManager = new DrawingManager2(dgg); | |||
} | |||
@@ -2455,4 +2406,54 @@ public final class Workbook implements Model { | |||
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); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -17,9 +17,8 @@ | |||
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; | |||
@@ -44,51 +43,33 @@ import org.apache.poi.util.StringUtil; | |||
*/ | |||
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; | |||
@@ -98,22 +79,16 @@ public final class NameRecord extends Record { | |||
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; | |||
@@ -122,13 +97,13 @@ public final class NameRecord extends Record { | |||
/** 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 = ""; | |||
} | |||
/** | |||
@@ -146,19 +121,10 @@ public final class NameRecord extends Record { | |||
*/ | |||
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 | |||
@@ -176,34 +142,9 @@ public final class NameRecord extends Record { | |||
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() | |||
{ | |||
@@ -226,49 +167,12 @@ public final class NameRecord extends Record { | |||
} | |||
/** 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 | |||
@@ -313,72 +217,15 @@ public final class NameRecord extends Record { | |||
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(); | |||
} | |||
@@ -388,13 +235,26 @@ public final class NameRecord extends Record { | |||
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 | |||
@@ -419,7 +279,7 @@ public final class NameRecord extends Record { | |||
*/ | |||
public boolean isBuiltInName() | |||
{ | |||
return ((this.field_1_option_flag & Option.OPT_BUILTIN) != 0); | |||
return ((field_1_option_flag & Option.OPT_BUILTIN) != 0); | |||
} | |||
@@ -428,7 +288,7 @@ public final class NameRecord extends Record { | |||
*/ | |||
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 | |||
@@ -436,19 +296,19 @@ public final class NameRecord extends Record { | |||
*/ | |||
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 | |||
@@ -490,7 +350,8 @@ public final class NameRecord extends Record { | |||
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 | |||
@@ -498,109 +359,107 @@ public final class NameRecord extends Record { | |||
* @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 | |||
@@ -609,11 +468,13 @@ public final class NameRecord extends Record { | |||
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){ | |||
@@ -625,11 +486,8 @@ public final class NameRecord extends Record { | |||
} | |||
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) | |||
@@ -646,16 +504,14 @@ public final class NameRecord extends Record { | |||
//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){ | |||
@@ -667,29 +523,27 @@ public final class NameRecord extends Record { | |||
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; | |||
} | |||
/** | |||
@@ -699,40 +553,36 @@ public final class NameRecord extends Record { | |||
* @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); | |||
} | |||
/** | |||
@@ -742,113 +592,90 @@ public final class NameRecord extends Record { | |||
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) | |||
{ | |||
@@ -859,14 +686,15 @@ public final class NameRecord extends Record { | |||
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"; | |||
} | |||
} |
@@ -94,10 +94,14 @@ public final class ArrayPtg extends Ptg { | |||
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(); |
@@ -41,7 +41,7 @@ public final class Ref3DPtg extends OperandPtg { | |||
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 | |||
@@ -93,10 +93,10 @@ public final class Ref3DPtg extends OperandPtg { | |||
} | |||
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; | |||
} | |||
@@ -109,8 +109,8 @@ public final class AnalysisToolPak { | |||
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); |
@@ -0,0 +1,74 @@ | |||
/* ==================================================================== | |||
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); | |||
} | |||
} |
@@ -21,6 +21,7 @@ import java.util.Calendar; | |||
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; | |||
@@ -96,6 +97,9 @@ final class YearFrac implements FreeRefFunction { | |||
Calendar date = parseDate(strVal); | |||
return DateUtil.getExcelDate(date, false); | |||
} | |||
if (ve instanceof BlankEval) { | |||
return 0.0; | |||
} | |||
return OperandResolver.coerceValueToDouble(ve); | |||
} | |||
@@ -120,7 +124,7 @@ final class YearFrac implements FreeRefFunction { | |||
} 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); | |||
} | |||
@@ -150,6 +154,7 @@ final class YearFrac implements FreeRefFunction { | |||
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; | |||
} | |||
@@ -1,27 +1,22 @@ | |||
/* | |||
* 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 > | |||
@@ -37,58 +32,14 @@ import org.apache.poi.hssf.record.formula.Ptg; | |||
* <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; | |||
} | |||
} |
@@ -1,95 +1,36 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -1,67 +1,34 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -42,7 +42,7 @@ final class ExternalFunction implements FreeRefFunction { | |||
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 { | |||
@@ -65,7 +65,7 @@ final class ExternalFunction implements FreeRefFunction { | |||
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) { | |||
@@ -74,24 +74,12 @@ final class ExternalFunction implements FreeRefFunction { | |||
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); | |||
} |
@@ -1,67 +1,34 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -1,67 +1,34 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -1,19 +1,19 @@ | |||
/* | |||
* 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; | |||
@@ -24,20 +24,21 @@ import org.apache.poi.ss.usermodel.FormulaEvaluator; | |||
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) { | |||
@@ -53,13 +54,27 @@ public final class LazyAreaEval extends AreaEvalBase { | |||
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(); | |||
} | |||
} |
@@ -1,30 +1,51 @@ | |||
/* ==================================================================== | |||
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.AreaI; | |||
import org.apache.poi.hssf.record.formula.AreaI.OffsetArea; | |||
import org.apache.poi.hssf.record.formula.Ref3DPtg; | |||
import org.apache.poi.hssf.record.formula.RefPtg; | |||
import org.apache.poi.hssf.record.formula.AreaI.OffsetArea; | |||
import org.apache.poi.ss.usermodel.Cell; | |||
import org.apache.poi.ss.usermodel.FormulaEvaluator; | |||
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 Josh Micich | |||
*/ | |||
public final class LazyRefEval extends RefEvalBase { | |||
private final Sheet _sheet; | |||
private final Workbook _workbook; | |||
private final FormulaEvaluator _evaluator; | |||
public LazyRefEval(RefPtg ptg, Sheet sheet, Workbook workbook) { | |||
public LazyRefEval(RefPtg ptg, Sheet sheet, FormulaEvaluator evaluator) { | |||
super(ptg.getRow(), ptg.getColumn()); | |||
_sheet = sheet; | |||
_workbook = workbook; | |||
_evaluator = evaluator; | |||
} | |||
public LazyRefEval(Ref3DPtg ptg, Sheet sheet, Workbook workbook) { | |||
public LazyRefEval(Ref3DPtg ptg, Sheet sheet, FormulaEvaluator evaluator) { | |||
super(ptg.getRow(), ptg.getColumn()); | |||
_sheet = sheet; | |||
_workbook = workbook; | |||
_evaluator = evaluator; | |||
} | |||
public ValueEval getInnerValueEval() { | |||
@@ -39,7 +60,7 @@ public final class LazyRefEval extends RefEvalBase { | |||
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) { | |||
@@ -47,6 +68,18 @@ public final class LazyRefEval extends RefEvalBase { | |||
AreaI area = new OffsetArea(getRow(), getColumn(), | |||
relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); | |||
return new LazyAreaEval(area, _sheet, _workbook); | |||
return new LazyAreaEval(area, _sheet, _evaluator); | |||
} | |||
public String toString() { | |||
CellReference cr = new CellReference(getRow(), getColumn()); | |||
StringBuffer sb = new StringBuffer(); | |||
sb.append(getClass().getName()).append("["); | |||
String sheetName = _evaluator.getSheetName(_sheet); | |||
sb.append(sheetName); | |||
sb.append('!'); | |||
sb.append(cr.formatAsString()); | |||
sb.append("]"); | |||
return sb.toString(); | |||
} | |||
} |
@@ -1,67 +1,34 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -1,68 +1,34 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -1,89 +1,33 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -22,26 +22,24 @@ package org.apache.poi.hssf.record.formula.eval; | |||
*/ | |||
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(); | |||
} |
@@ -1,68 +1,34 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -1,66 +0,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 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; | |||
} | |||
} |
@@ -17,55 +17,40 @@ | |||
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"); | |||
} | |||
} |
@@ -1,90 +1,33 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -1,216 +1,145 @@ | |||
/* | |||
* 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"); | |||
} | |||
} |
@@ -1,93 +1,33 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
/* ==================================================================== | |||
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; | |||
} | |||
} |
@@ -1,75 +1,56 @@ | |||
/* | |||
* 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(); | |||
} | |||
} |
@@ -17,8 +17,6 @@ | |||
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 > | |||
@@ -26,13 +24,9 @@ import org.apache.poi.hssf.record.formula.UnaryPlusPtg; | |||
*/ | |||
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) { | |||
@@ -59,10 +53,10 @@ public final class UnaryPlusEval implements OperationEval { | |||
} | |||
public int getNumberOfOperands() { | |||
return delegate.getNumberOfOperands(); | |||
return 1; | |||
} | |||
public int getType() { | |||
return delegate.getType(); | |||
throw new RuntimeException("obsolete code should not be called"); | |||
} | |||
} |
@@ -55,49 +55,40 @@ public final class ValueEvalToNumericXlator { | |||
* @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()); | |||
} | |||
/** |
@@ -60,8 +60,7 @@ public final class Hlookup implements Function { | |||
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) { | |||
@@ -73,15 +72,14 @@ public final class Hlookup implements Function { | |||
/** | |||
* 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); | |||
} | |||
} |
@@ -322,30 +322,45 @@ final class LookupUtils { | |||
* <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 | |||
} | |||
@@ -583,11 +598,13 @@ final class LookupUtils { | |||
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); |
@@ -60,8 +60,7 @@ public final class Vlookup implements Function { | |||
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) { | |||
@@ -73,12 +72,11 @@ public final class Vlookup implements Function { | |||
/** | |||
* 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(); | |||
} |
@@ -23,15 +23,15 @@ import org.apache.poi.hssf.util.RangeAddress; | |||
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. | |||
* | |||
@@ -43,58 +43,57 @@ public class HSSFName implements Name { | |||
_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) | |||
@@ -102,11 +101,11 @@ public class HSSFName implements Name { | |||
_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); | |||
@@ -117,7 +116,7 @@ public class HSSFName implements Name { | |||
setSheetName(sheetName); | |||
} | |||
//allow the poi utilities to parse it out | |||
//allow the poi utilities to parse it out | |||
_definedNameRec.setAreaReference(ref); | |||
} | |||
@@ -131,7 +130,7 @@ public class HSSFName implements Name { | |||
return "#REF!".endsWith(ref); | |||
} | |||
public boolean isFunctionName() { | |||
return _definedNameRec.isFunctionName(); | |||
return _definedNameRec.isFunctionName(); | |||
} | |||
public String toString() { | |||
StringBuffer sb = new StringBuffer(64); |
@@ -27,7 +27,6 @@ import java.util.ArrayList; | |||
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; | |||
@@ -36,6 +35,7 @@ import org.apache.poi.ddf.EscherBlipRecord; | |||
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; | |||
@@ -55,6 +55,8 @@ import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor; | |||
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; | |||
@@ -667,33 +669,81 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
* @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; | |||
} | |||
/** | |||
@@ -907,7 +957,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
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); | |||
@@ -925,29 +975,27 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
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) | |||
@@ -963,13 +1011,13 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
} | |||
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) { | |||
@@ -985,10 +1033,6 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
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 | |||
@@ -1288,6 +1332,9 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm | |||
return result; | |||
} | |||
public NameRecord getNameRecord(int nameIndex) { | |||
return getWorkbook().getNameRecord(nameIndex); | |||
} | |||
/** gets the named range name | |||
* @param index the named range index (0 based) |
@@ -0,0 +1,184 @@ | |||
/* ==================================================================== | |||
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; | |||
} | |||
} |
@@ -0,0 +1,95 @@ | |||
/* ==================================================================== | |||
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(); | |||
} | |||
} |
@@ -20,7 +20,11 @@ package org.apache.poi.ss.usermodel; | |||
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; | |||
@@ -56,17 +60,64 @@ import org.apache.poi.hssf.record.formula.eval.StringEval; | |||
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); | |||
} | |||
} | |||
/** | |||
@@ -75,6 +126,18 @@ public class FormulaEvaluator { | |||
*/ | |||
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(); | |||
} | |||
@@ -102,7 +165,7 @@ public class FormulaEvaluator { | |||
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()); | |||
@@ -140,7 +203,7 @@ public class FormulaEvaluator { | |||
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()); | |||
@@ -185,7 +248,7 @@ public class FormulaEvaluator { | |||
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); | |||
@@ -283,26 +346,40 @@ public class FormulaEvaluator { | |||
* 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++) { | |||
@@ -314,19 +391,10 @@ public class FormulaEvaluator { | |||
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; | |||
@@ -343,10 +411,15 @@ public class FormulaEvaluator { | |||
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); | |||
} | |||
@@ -403,28 +476,63 @@ public class FormulaEvaluator { | |||
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) { | |||
@@ -442,8 +550,19 @@ public class FormulaEvaluator { | |||
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 | |||
@@ -453,7 +572,7 @@ public class FormulaEvaluator { | |||
* @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; | |||
@@ -464,7 +583,7 @@ public class FormulaEvaluator { | |||
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: |
@@ -69,8 +69,9 @@ import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval; | |||
*/ | |||
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 | |||
@@ -78,32 +79,44 @@ final class OperationEvaluatorFactory { | |||
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"); | |||
} | |||
@@ -125,7 +138,7 @@ final class OperationEvaluatorFactory { | |||
} | |||
m.put(ptgClass, constructor); | |||
} | |||
/** | |||
* returns the OperationEval concrete impl instance corresponding | |||
* to the supplied operationPtg | |||
@@ -134,9 +147,16 @@ final class OperationEvaluatorFactory { | |||
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) { | |||
@@ -147,7 +167,6 @@ final class OperationEvaluatorFactory { | |||
throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")"); | |||
} | |||
Object result; | |||
Object[] initargs = { ptg }; | |||
try { | |||
result = constructor.newInstance(initargs); |
@@ -46,29 +46,59 @@ public class AreaReference { | |||
} | |||
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. | |||
*/ |
@@ -80,7 +80,7 @@ public class CellReference { | |||
if (_isColAbs) { | |||
colRef=colRef.substring(1); | |||
} | |||
_colIndex = convertColStringToNum(colRef); | |||
_colIndex = convertColStringToIndex(colRef); | |||
String rowRef=parts[2]; | |||
if (rowRef.length() < 1) { | |||
@@ -94,7 +94,7 @@ public class CellReference { | |||
} | |||
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); | |||
@@ -130,18 +130,31 @@ public class CellReference { | |||
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); |
@@ -71,27 +71,44 @@ public class Document extends PositionDependentRecordContainer | |||
* 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; | |||
} | |||
/** |
@@ -243,8 +243,16 @@ public abstract class RecordContainer extends Record | |||
moveChildRecords(oldLoc, newLoc, number); | |||
} | |||
} | |||
/** | |||
* Set child records. | |||
* | |||
* @param records the new child records | |||
*/ | |||
public void setChildRecord(Record[] records) { | |||
this._children = records; | |||
} | |||
/* =============================================================== | |||
* External Serialisation Methods | |||
* =============================================================== |
@@ -50,7 +50,24 @@ import java.util.Vector; | |||
// 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; | |||
@@ -123,9 +140,9 @@ public class SlideListWithText extends RecordContainer | |||
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); | |||
@@ -133,7 +150,15 @@ public class SlideListWithText extends RecordContainer | |||
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; } | |||
@@ -152,35 +177,6 @@ public class SlideListWithText extends RecordContainer | |||
} | |||
/** | |||
* 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 |
@@ -536,32 +536,37 @@ public final class SlideShow { | |||
/** | |||
* 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); | |||
} | |||
/* =============================================================== | |||
@@ -585,7 +590,8 @@ public final class SlideShow { | |||
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. | |||
@@ -678,11 +684,11 @@ public final class SlideShow { | |||
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. | |||
* |
@@ -279,18 +279,23 @@ public class TestReOrderingSlides extends TestCase { | |||
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()); | |||
} | |||
} |
@@ -74,9 +74,18 @@ public final class TestExternalFunctionFormulas extends TestCase { | |||
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); | |||
} | |||
} |
@@ -38,6 +38,7 @@ public final class TestYearFracCalculator extends TestCase { | |||
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) { |
@@ -31,6 +31,8 @@ public class AllFormulaEvalTests { | |||
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); |
@@ -93,7 +93,6 @@ public final class TestCircularReferences extends TestCase { | |||
HSSFCell testCell = row.createCell(0); | |||
testCell.setCellFormula("A1"); | |||
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); | |||
CellValue cellValue = evaluateWithCycles(wb, sheet, testCell); | |||
confirmCycleErrorCode(cellValue); | |||
@@ -114,7 +113,6 @@ public final class TestCircularReferences extends TestCase { | |||
HSSFCell testCell = row.createCell(3); | |||
testCell.setCellFormula("A1"); | |||
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb); | |||
CellValue cellValue = evaluateWithCycles(wb, sheet, testCell); | |||
confirmCycleErrorCode(cellValue); |
@@ -0,0 +1,62 @@ | |||
/* ==================================================================== | |||
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); | |||
} | |||
} |
@@ -0,0 +1,69 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hssf.record.formula.eval; | |||
import junit.framework.AssertionFailedError; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.hssf.record.formula.functions.EvalFactory; | |||
/** | |||
* Test for unary plus operator evaluator. | |||
* | |||
* @author Josh Micich | |||
*/ | |||
public final class TestEqualEval extends TestCase { | |||
/** | |||
* Test for bug observable at svn revision 692218 (Sep 2008)<br/> | |||
* The value from a 1x1 area should be taken immediately, regardless of srcRow and srcCol | |||
*/ | |||
public void test1x1AreaOperand() { | |||
ValueEval[] values = { BoolEval.FALSE, }; | |||
Eval[] args = { | |||
EvalFactory.createAreaEval("B1:B1", values), | |||
BoolEval.FALSE, | |||
}; | |||
Eval result = EqualEval.instance.evaluate(args, 10, (short)20); | |||
if (result instanceof ErrorEval) { | |||
if (result == ErrorEval.VALUE_INVALID) { | |||
throw new AssertionFailedError("Identified bug in evaluation of 1x1 area"); | |||
} | |||
} | |||
assertEquals(BoolEval.class, result.getClass()); | |||
assertTrue(((BoolEval)result).getBooleanValue()); | |||
} | |||
/** | |||
* Empty string is equal to blank | |||
*/ | |||
public void testBlankEqualToEmptyString() { | |||
Eval[] args = { | |||
new StringEval(""), | |||
BlankEval.INSTANCE, | |||
}; | |||
Eval result = EqualEval.instance.evaluate(args, 10, (short)20); | |||
assertEquals(BoolEval.class, result.getClass()); | |||
BoolEval be = (BoolEval) result; | |||
if (!be.getBooleanValue()) { | |||
throw new AssertionFailedError("Identified bug blank/empty string equality"); | |||
} | |||
assertTrue(be.getBooleanValue()); | |||
} | |||
} |
@@ -20,7 +20,7 @@ package org.apache.poi.hssf.record.formula.eval; | |||
import junit.framework.AssertionFailedError; | |||
import junit.framework.TestCase; | |||
import org.apache.poi.hssf.record.formula.PercentPtg; | |||
import org.apache.poi.hssf.record.formula.functions.EvalFactory; | |||
import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker; | |||
import org.apache.poi.hssf.usermodel.HSSFCell; | |||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; | |||
@@ -41,8 +41,8 @@ public final class TestPercentEval extends TestCase { | |||
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); | |||
} | |||
@@ -55,6 +55,10 @@ public final class TestPercentEval extends TestCase { | |||
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"); |
@@ -1,27 +1,25 @@ | |||
/* | |||
* 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; | |||
@@ -53,7 +51,7 @@ public final class TestUnaryPlusEval extends TestCase { | |||
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); | |||
} |
@@ -36,6 +36,7 @@ public final class AllIndividualFunctionEvaluationTests { | |||
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); |
@@ -28,29 +28,29 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
* @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); | |||
@@ -58,7 +58,7 @@ public final class TestDate extends TestCase { | |||
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); | |||
@@ -66,7 +66,7 @@ public final class TestDate extends TestCase { | |||
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); | |||
@@ -74,10 +74,11 @@ public final class TestDate extends TestCase { | |||
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); | |||
} | |||
} | |||
@@ -46,6 +46,7 @@ public class AllUserModelTests { | |||
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); |
@@ -37,6 +37,7 @@ import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord; | |||
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; | |||
/** | |||
@@ -1020,9 +1021,9 @@ public final class TestBugs extends TestCase { | |||
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); | |||
} | |||
@@ -1038,9 +1039,9 @@ public final class TestBugs extends TestCase { | |||
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); | |||
} | |||
@@ -1055,9 +1056,9 @@ public final class TestBugs extends TestCase { | |||
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); | |||
} | |||
} | |||
@@ -19,6 +19,8 @@ package org.apache.poi.hssf.usermodel; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.util.Calendar; | |||
import java.util.GregorianCalendar; | |||
import java.util.Iterator; | |||
import junit.framework.AssertionFailedError; | |||
@@ -35,6 +37,7 @@ import org.apache.poi.hssf.record.formula.Ptg; | |||
*/ | |||
public final class TestFormulaEvaluatorBugs extends TestCase { | |||
private static final boolean OUTPUT_TEST_FILES = false; | |||
private String tmpDirName; | |||
protected void setUp() { | |||
@@ -65,13 +68,15 @@ public final class TestFormulaEvaluatorBugs extends TestCase { | |||
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(); | |||
@@ -86,12 +91,14 @@ public final class TestFormulaEvaluatorBugs extends TestCase { | |||
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()); | |||
} | |||
} | |||
/** | |||
@@ -281,64 +288,39 @@ public final class TestFormulaEvaluatorBugs extends TestCase { | |||
} | |||
/** | |||
* 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"); | |||
} | |||
} | |||
} |
@@ -0,0 +1,82 @@ | |||
/* ==================================================================== | |||
Licensed to the Apache Software Foundation (ASF) under one or more | |||
contributor license agreements. See the NOTICE file distributed with | |||
this work for additional information regarding copyright ownership. | |||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||
(the "License"); you may not use this file except in compliance with | |||
the License. You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hssf.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); | |||
} | |||
} |
@@ -429,9 +429,9 @@ public final class TestHSSFWorkbook extends TestCase { | |||
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()); | |||
@@ -452,9 +452,9 @@ public final class TestHSSFWorkbook extends TestCase { | |||
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()); | |||
@@ -475,9 +475,9 @@ public final class TestHSSFWorkbook extends TestCase { | |||
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()); |
@@ -19,7 +19,6 @@ package org.apache.poi.hssf.util; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.List; | |||
import junit.framework.TestCase; | |||
@@ -28,6 +27,7 @@ import org.apache.poi.hssf.model.Workbook; | |||
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; | |||
@@ -85,7 +85,7 @@ public final class TestAreaReference extends TestCase { | |||
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"); | |||
@@ -115,11 +115,11 @@ public final class TestAreaReference extends TestCase { | |||
} | |||
} | |||
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 | |||
@@ -206,13 +206,13 @@ public final class TestAreaReference extends TestCase { | |||
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)); | |||
@@ -245,16 +245,16 @@ public final class TestAreaReference extends TestCase { | |||
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"); | |||
@@ -270,7 +270,24 @@ public final class TestAreaReference extends TestCase { | |||
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()); | |||
} | |||
} |