Merged revisions 638786-638802,638805-638811,638813-638814,638816-639230,639233-639241,639243-639253,639255-639486,639488-639601,639603-639835,639837-639917,639919-640056,640058-640710,640712-641156,641158-641184,641186-641795,641797-641798,641800-641933,641935-641963,641965-641966,641968-641995,641997-642230,642232-642562,642564-642565,642568-642570,642572-642573,642576-642736,642739-642877,642879,642881-642890,642892-642903,642905-642945,642947-643624,643626-643653,643655-643669,643671,643673-643830,643832-643833,643835-644342,644344-644472,644474-644508,644510-645347,645349-645351,645353-645559,645561-645565,645568-645951,645953-646193,646195-646311,646313-646404,646406-646665,646667-646853,646855-646869,646871-647151,647153-647185,647187-647277,647279-647566,647568-647573,647575,647578-647711,647714-647737,647739-647823,647825-648155,648157-648202,648204-648273,648275,648277-648302,648304-648333,648335-648588,648590-648622,648625-648673,648675-649141,649144,649146-649556,649558-649795,649799,649801-649910,649912-649913,649915-650128,650131-650132,650134-650137,650140-650914,650916-651991,651993-652284,652286-652287,652289,652291,652293-652297,652299-652328,652330-652425,652427-652445,652447-652560,652562-652933,652935,652937-652993,652995-653116,653118-653124,653126-653483,653487-653519,653522-653550,653552-653607,653609-653667,653669-653674,653676-653814,653817-653830,653832-653891,653893-653944,653946-654055,654057-654355,654357-654365,654367-654648,654651-655215,655217-655277,655279-655281,655283-655911,655913-656212,656214,656216-656251,656253-656698,656700-656756,656758-656892,656894-657135,657137-657165,657168-657179,657181-657354,657356-657357,657359-657701,657703-657874,657876-658032,658034-658284,658286,658288-658301,658303-658307,658309-658321,658323-658335,658337-658348,658351,658353-658832,658834-658983,658985,658987-659066,659068-659402,659404-659428,659430-659451,659453-659454,659456-659461,659463-659477,659479-659524,659526-659571,659574,659576-660255,660257-660262,660264-660279,660281-660343,660345-660473,660475-660827,660829-660833,660835-660888,660890-663321,663323-663435,663437-663764,663766-663854,663856-664219,664221-664489,664494-664514,664516-668013,668015-668142,668144-668152,668154,668156-668256,668258,668260-669139,669141-669455,669457-669657,669659-669808,669810-670189,670191-671321,671323-672229,672231-672549,672551-672552,672554-672561,672563-672566,672568,672571-673049,673051-673852,673854-673862,673864-673986,673988-673996,673998-674347,674349-674890,674892-674910,674912-674936,674938-674952,674954-675078,675080-675085,675087-675217,675219-675660,675662-675670,675672-675716,675718-675726,675728-675733,675735-675775,675777-675782,675784,675786-675791,675794-675852,675854-676200,676202,676204,676206-676220,676222-676309,676311-676456,676458-676994,676996-677027,677030-677040,677042-677056,677058-677375,677377-677968,677970-677971,677973,677975-677994,677996-678286,678288-678538,678540-680393,680395-680469,680471-680529,680531-680852,680854-681529,681531-681571,681573-682224,682226,682228,682231-682281,682283-682335,682337-682507,682509,682512-682517,682519-682532,682534-682619,682622-682777,682779-682998,683000-683019,683021-683022,683024-683080,683082-683092,683094-683095,683097-683127,683129-683131,683133-683166,683168-683698,683700-683705,683707-683757,683759-683787,683789-683870,683872-683879,683881-683900,683902-684066,684068-684074,684076-684222,684224-684254,684257-684281,684283-684286,684288-684292,684294-684298,684300-684301,684303-684308,684310-684317,684320,684323-684335,684337-684348,684350-684354,684356-684361,684363-684369,684371-684453,684455-684883,684885-684937,684940-684958,684960-684970,684972-684985,684987-685053,685055-685063,685065-685259,685261-685262,685264-685266,685268-685282,685285-686035,686037-686045,686047-686052,686054-686206,686208-686215,686217-686277,686279-686289,686291-686620,686622-686623,686626-686627,686629-686639,686641-686843,686845-686976,686978-687402,687404-687422,687424-687428,687430-687442,687444-688425,688427-688641,688643-688649,688651-688654,688656-688824,688826-688909,688911-689543,689545-689558,689560-689635,689637-689703,689705-689715,689717-689718,689720,689722-689972,689974-690090,690092-690093,690095-690111,690113-690258,690260-690261,690263-690403,690405-690410,690412-690460,690462-690516,690518-690533,690535,690537-690625,690627-690635,690637-690720,690722-690725,690727-690728,690730-690738,690740-690760,690762-690771,690773-690824,690826-690834,690838-691016,691018-691179,691181,691183-692908 via svnmerge from

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-ffa450edef68
This commit is contained in:
Nick Burch 2008-09-07 20:11:32 +00:00
parent 2a02163d66
commit e6ec8e2601
72 changed files with 3105 additions and 2924 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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_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;
/**Included for completeness sake, not implemented
*/
public final static byte BUILTIN_AUTO_CLOSE = (byte)3;
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_DATABASE = (byte)4;
/**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;
/**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_FILTER_DB = 13;
public final static byte BUILTIN_AUTO_ACTIVATE = (byte)10;
/**Included for completeness sake, not implemented
*/
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();
public int serialize( int offset, byte[] data ) {
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();
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();
StringBuffer sb = 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");
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";
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
@ -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 {
public final class AddEval extends TwoOperandNumericOperation {
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 static final OperationEval instance = new AddEval();
public AddEval(Ptg ptg) {
delegate = (AddPtg) ptg;
}
private AddEval() {
}
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 int getNumberOfOperands() {
return delegate.getNumberOfOperands();
}
public int getType() {
return delegate.getType();
}
protected double evaluate(double d0, double d1) {
return d0 + d1;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class DivideEval extends NumericOperationEval {
public final class DivideEval extends TwoOperandNumericOperation {
private DividePtg delegate;
public static final OperationEval instance = new DivideEval();
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
));
private DivideEval() {
}
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;
}
}
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 int getNumberOfOperands() {
return delegate.getNumberOfOperands();
}
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;
}
}

View File

@ -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
import org.apache.poi.hssf.record.formula.EqualPtg;
import org.apache.poi.hssf.record.formula.Ptg;
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 Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class EqualEval extends RelationalOperationEval {
public final class EqualEval extends RelationalOperationEval {
private EqualPtg delegate;
public static final OperationEval instance = new EqualEval();
private EqualEval() {
}
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 int getNumberOfOperands() {
return delegate.getNumberOfOperands();
}
public int getType() {
return delegate.getType();
}
protected boolean convertComparisonResult(int cmpResult) {
return cmpResult == 0;
}
}

View File

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

View File

@ -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
import org.apache.poi.hssf.record.formula.GreaterEqualPtg;
import org.apache.poi.hssf.record.formula.Ptg;
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 Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class GreaterEqualEval extends RelationalOperationEval {
public final 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 int getType() {
return delegate.getType();
}
public static final OperationEval instance = new GreaterEqualEval();
private GreaterEqualEval() {
}
protected boolean convertComparisonResult(int cmpResult) {
return cmpResult >= 0;
}
}

View File

@ -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
import org.apache.poi.hssf.record.formula.GreaterThanPtg;
import org.apache.poi.hssf.record.formula.Ptg;
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 Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class GreaterThanEval extends RelationalOperationEval {
public final 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 int getType() {
return delegate.getType();
}
public static final OperationEval instance = new GreaterThanEval();
private GreaterThanEval() {
}
protected boolean convertComparisonResult(int cmpResult) {
return cmpResult > 0;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*
* @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();
}
}

View File

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

View File

@ -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
import org.apache.poi.hssf.record.formula.LessEqualPtg;
import org.apache.poi.hssf.record.formula.Ptg;
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 Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class LessEqualEval extends RelationalOperationEval {
public final 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 int getType() {
return delegate.getType();
}
public static final OperationEval instance = new LessEqualEval();
private LessEqualEval() {
}
protected boolean convertComparisonResult(int cmpResult) {
return cmpResult <= 0;
}
}

View File

@ -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
import org.apache.poi.hssf.record.formula.LessThanPtg;
import org.apache.poi.hssf.record.formula.Ptg;
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 Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class LessThanEval extends RelationalOperationEval {
public final 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 int getType() {
return delegate.getType();
}
public static final OperationEval instance = new LessThanEval();
private LessThanEval() {
}
protected boolean convertComparisonResult(int cmpResult) {
return cmpResult < 0;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class MultiplyEval extends NumericOperationEval {
public final class MultiplyEval extends TwoOperandNumericOperation {
private MultiplyPtg delegate;
public static final OperationEval instance = new MultiplyEval();
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
));
private MultiplyEval() {
}
public MultiplyEval(Ptg ptg) {
delegate = (MultiplyPtg) 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;
}
if (Double.isNaN(d0) || Double.isNaN(d1)) {
return ErrorEval.NUM_ERROR;
}
return new NumberEval(d0 * d1);
}
public int getNumberOfOperands() {
return delegate.getNumberOfOperands();
}
public int getType() {
return delegate.getType();
}
protected double evaluate(double d0, double d1) {
return d0 * d1;
}
}

View File

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

View File

@ -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
import org.apache.poi.hssf.record.formula.NotEqualPtg;
import org.apache.poi.hssf.record.formula.Ptg;
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 Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class NotEqualEval extends RelationalOperationEval {
public final 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 int getType() {
return delegate.getType();
}
public static final OperationEval instance = new NotEqualEval();
private NotEqualEval() {
}
protected boolean convertComparisonResult(int cmpResult) {
return cmpResult != 0;
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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;
}
}

View File

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

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class PowerEval extends NumericOperationEval {
public final class PowerEval extends TwoOperandNumericOperation {
private PowerPtg delegate;
public static final OperationEval instance = new PowerEval();
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
));
private PowerEval() {
}
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;
}
double p = Math.pow(d0, d1);
if (Double.isNaN(p)) {
return ErrorEval.VALUE_INVALID;
}
return new NumberEval(p);
}
public int getNumberOfOperands() {
return delegate.getNumberOfOperands();
}
public int getType() {
return delegate.getType();
}
protected double evaluate(double d0, double d1) {
return Math.pow(d0, d1);
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
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.
*
* 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;
}
/**
* 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;
}
// 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;
}
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);
}
// 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;
}
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() + ")");
}
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() + ")");
}
public final int getNumberOfOperands() {
return 2;
}
public final int getType() {
// TODO - get rid of this method
throw new RuntimeException("Obsolete code - should not be called");
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class SubtractEval extends NumericOperationEval {
public final class SubtractEval extends TwoOperandNumericOperation {
private SubtractPtg delegate;
public static final OperationEval instance = new SubtractEval();
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
));
private SubtractEval() {
}
public SubtractEval(Ptg ptg) {
delegate = (SubtractPtg) 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;
}
}
if (retval == null) {
retval = (Double.isNaN(d0) || Double.isNaN(d1))
? (ValueEval) ErrorEval.VALUE_INVALID
: new NumberEval(d0 - d1);
}
return retval;
}
public int getNumberOfOperands() {
return delegate.getNumberOfOperands();
}
public int getType() {
return delegate.getType();
}
protected double evaluate(double d0, double d1) {
return d0 - d1;
}
}

View File

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

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class UnaryMinusEval extends NumericOperationEval {
public final class UnaryMinusEval implements OperationEval {
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 static final OperationEval instance = new UnaryMinusEval();
private UnaryMinusEval() {
}
public UnaryMinusEval(Ptg ptg) {
this.delegate = (UnaryMinusPtg) ptg;
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");
}
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 int getNumberOfOperands() {
return delegate.getNumberOfOperands();
}
public int getType() {
return delegate.getType();
}
}

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
@ -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");
}
}

View File

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

View File

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

View File

@ -322,30 +322,45 @@ final class LookupUtils {
* <tr><td>&lt;blank&gt;</td><td>&nbsp;</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");
}
ValueEval veRowColIndexArg;
try {
veRowColIndexArg = OperandResolver.getSingleValue(rowColIndexArg, srcCellRow, (short)srcCellCol);
} catch (EvaluationException e) {
// All errors get translated to #REF!
throw EvaluationException.invalidRef();
}
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
}
// 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();
}
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.
}
// else - numeric value parses OK
}
// 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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &lt; amolweb at ya hoo dot com &gt;
*
* @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:

View File

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

View File

@ -46,29 +46,59 @@ public class AreaReference {
}
String[] parts = separateAreaRefs(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') {
// 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]);
_isSingleCell = false;
} else {
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 + "'");
}
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
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 {
_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.
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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