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 4cfed67468
commit 8a7e7f103b
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> <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>
<release version="3.1.1-alpha1" date="2008-??-??"> <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="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="fix">45358 - signed/unsigned error when parsing 3-d area refs, performance problem evaluating area refs, and ClassCastExcecption in IF()</action>
<action dev="POI-DEVELOPERS" type="add">Support for HPBF Publisher hyperlinks, including during text extraction</action> <action dev="POI-DEVELOPERS" type="add">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> <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>
<release version="3.1.1-alpha1" date="2008-??-??"> <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="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="fix">45358 - signed/unsigned error when parsing 3-d area refs, performance problem evaluating area refs, and ClassCastExcecption in IF()</action>
<action dev="POI-DEVELOPERS" type="add">Support for HPBF Publisher hyperlinks, including during text extraction</action> <action dev="POI-DEVELOPERS" type="add">Support for HPBF Publisher hyperlinks, including during text extraction</action>

View File

@ -235,4 +235,21 @@ public class EscherContainerRecord extends EscherRecord
return null; 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. * @return a new shape id.
*/ */
public int allocateShapeId(short drawingGroupId) 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 ); dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 );
@ -78,7 +89,6 @@ public class DrawingManager2
{ {
int result = c.getNumShapeIdsUsed() + (1024 * (i+1)); int result = c.getNumShapeIdsUsed() + (1024 * (i+1));
c.incrementShapeId(); c.incrementShapeId();
EscherDgRecord dg = getDrawingGroup(drawingGroupId);
dg.setNumShapes( dg.getNumShapes() + 1 ); dg.setNumShapes( dg.getNumShapes() + 1 );
dg.setLastMSOSPID( result ); dg.setLastMSOSPID( result );
if (result >= dgg.getShapeIdMax()) if (result >= dgg.getShapeIdMax())
@ -90,7 +100,6 @@ public class DrawingManager2
// Create new cluster // Create new cluster
dgg.addCluster( drawingGroupId, 0 ); dgg.addCluster( drawingGroupId, 0 );
dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId(); dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId();
EscherDgRecord dg = getDrawingGroup(drawingGroupId);
dg.setNumShapes( dg.getNumShapes() + 1 ); dg.setNumShapes( dg.getNumShapes() + 1 );
int result = (1024 * dgg.getFileIdClusters().length); int result = (1024 * dgg.getFileIdClusters().length);
dg.setLastMSOSPID( result ); dg.setLastMSOSPID( result );
@ -98,7 +107,6 @@ public class DrawingManager2
dgg.setShapeIdMax( result + 1 ); dgg.setShapeIdMax( result + 1 );
return result; return result;
} }
//////////// Non-public methods ///////////// //////////// Non-public methods /////////////
/** /**

View File

@ -22,57 +22,8 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import org.apache.poi.ddf.EscherBSERecord; import org.apache.poi.ddf.*;
import org.apache.poi.ddf.EscherBoolProperty; import org.apache.poi.hssf.record.*;
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.hssf.record.formula.NameXPtg; import org.apache.poi.hssf.record.formula.NameXPtg;
import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.hssf.util.SheetReferences; import org.apache.poi.hssf.util.SheetReferences;
@ -2206,17 +2157,17 @@ public final class Workbook implements Model {
// contains a EscherDggRecord // contains a EscherDggRecord
for(Iterator rit = records.iterator(); rit.hasNext();) { for(Iterator rit = records.iterator(); rit.hasNext();) {
Record r = (Record)rit.next(); Record r = (Record)rit.next();
if(r instanceof DrawingGroupRecord) { if(r instanceof DrawingGroupRecord) {
DrawingGroupRecord dg = (DrawingGroupRecord)r; DrawingGroupRecord dg = (DrawingGroupRecord)r;
dg.processChildRecords(); dg.processChildRecords();
EscherContainerRecord cr = EscherContainerRecord cr =
dg.getEscherContainer(); dg.getEscherContainer();
if(cr == null) { if(cr == null) {
continue; continue;
} }
EscherDggRecord dgg = null; EscherDggRecord dgg = null;
for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) { for(Iterator it = cr.getChildRecords().iterator(); it.hasNext();) {
Object er = it.next(); Object er = it.next();
@ -2224,7 +2175,7 @@ public final class Workbook implements Model {
dgg = (EscherDggRecord)er; dgg = (EscherDggRecord)er;
} }
} }
if(dgg != null) { if(dgg != null) {
drawingManager = new DrawingManager2(dgg); drawingManager = new DrawingManager2(dgg);
return; return;
@ -2234,7 +2185,7 @@ public final class Workbook implements Model {
// Look for the DrawingGroup record // Look for the DrawingGroup record
int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid); int dgLoc = findFirstRecordLocBySid(DrawingGroupRecord.sid);
// If there is one, does it have a EscherDggRecord? // If there is one, does it have a EscherDggRecord?
if(dgLoc != -1) { if(dgLoc != -1) {
DrawingGroupRecord dg = DrawingGroupRecord dg =
@ -2246,7 +2197,7 @@ public final class Workbook implements Model {
dgg = (EscherDggRecord)er; dgg = (EscherDggRecord)er;
} }
} }
if(dgg != null) { if(dgg != null) {
drawingManager = new DrawingManager2(dgg); drawingManager = new DrawingManager2(dgg);
} }
@ -2455,4 +2406,54 @@ public final class Workbook implements Model {
public NameXPtg getNameXPtg(String name) { public NameXPtg getNameXPtg(String name) {
return getOrCreateLinkTable().getNameXPtg(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; package org.apache.poi.hssf.record;
import java.util.Iterator; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Stack;
import org.apache.poi.hssf.model.FormulaParser; import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.record.formula.Area3DPtg; 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 class NameRecord extends Record {
public final static short sid = 0x0018; public final static short sid = 0x0018;
/**Included for completeness sake, not implemented /**Included for completeness sake, not implemented */
*/ public final static byte BUILTIN_CONSOLIDATE_AREA = 1;
public final static byte BUILTIN_CONSOLIDATE_AREA = (byte)1; /**Included for completeness sake, not implemented */
public final static byte BUILTIN_AUTO_OPEN = 2;
/**Included for completeness sake, not implemented /**Included for completeness sake, not implemented */
*/ public final static byte BUILTIN_AUTO_CLOSE = 3;
public final static byte BUILTIN_AUTO_OPEN = (byte)2; /**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_PRINT_AREA = 6;
*/ public final static byte BUILTIN_PRINT_TITLE = 7;
public final static byte BUILTIN_AUTO_CLOSE = (byte)3;
/**Included for completeness sake, not implemented /**Included for completeness sake, not implemented */
*/ public final static byte BUILTIN_RECORDER = 8;
public final static byte BUILTIN_DATABASE = (byte)4; /**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_FILTER_DB = 13;
*/
public final static byte BUILTIN_CRITERIA = (byte)5;
public final static byte BUILTIN_PRINT_AREA = (byte)6;
public final static byte BUILTIN_PRINT_TITLE = (byte)7;
/**Included for completeness sake, not implemented
*/
public final static byte BUILTIN_RECORDER = (byte)8;
/**Included for completeness sake, not implemented
*/
public final static byte BUILTIN_DATA_FORM = (byte)9;
/**Included for completeness sake, not implemented
*/
public final static byte BUILTIN_AUTO_ACTIVATE = (byte)10;
/**Included for completeness sake, not implemented
*/
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 { private static final class Option {
public static final int OPT_HIDDEN_NAME = 0x0001; public static final int OPT_HIDDEN_NAME = 0x0001;
public static final int OPT_FUNCTION_NAME = 0x0002; 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_BUILTIN = 0x0020;
public static final int OPT_BINDATA = 0x1000; public static final int OPT_BINDATA = 0x1000;
} }
private short field_1_option_flag; private short field_1_option_flag;
private byte field_2_keyboard_shortcut; 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 private short field_5_index_to_sheet; // unused: see field_6
/** the one based sheet number. Zero if this is a global name */ /** the one based sheet number. Zero if this is a global name */
private int field_6_sheetNumber; private int field_6_sheetNumber;
private byte field_7_length_custom_menu; private boolean field_11_nameIsMultibyte;
private byte field_8_length_description_text; private byte field_12_built_in_code;
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 String field_12_name_text; 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_14_custom_menu_text;
private String field_15_description_text; private String field_15_description_text;
private String field_16_help_topic_text; private String field_16_help_topic_text;
@ -122,13 +97,13 @@ public final class NameRecord extends Record {
/** Creates new NameRecord */ /** Creates new NameRecord */
public NameRecord() { public NameRecord() {
field_13_name_definition = new Stack(); field_13_name_definition = Ptg.EMPTY_PTG_ARRAY;
field_12_name_text = new String(); field_12_name_text = "";
field_14_custom_menu_text = new String(); field_14_custom_menu_text = "";
field_15_description_text = new String(); field_15_description_text = "";
field_16_help_topic_text = new String(); field_16_help_topic_text = "";
field_17_status_bar_text = new String(); field_17_status_bar_text = "";
} }
/** /**
@ -146,19 +121,10 @@ public final class NameRecord extends Record {
*/ */
public NameRecord(byte builtin, int sheetNumber) public NameRecord(byte builtin, int sheetNumber)
{ {
this(); this();
this.field_12_builtIn_name = builtin; field_12_built_in_code = builtin;
this.setOptionFlag((short)(this.field_1_option_flag | Option.OPT_BUILTIN)); setOptionFlag((short)(field_1_option_flag | Option.OPT_BUILTIN));
this.setNameTextLength((byte)1);
field_6_sheetNumber = sheetNumber; //the extern sheets are set through references 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 /** sets the option flag for the named range
@ -176,34 +142,9 @@ public final class NameRecord extends Record {
field_2_keyboard_shortcut = shortcut; 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 * 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() 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 /** sets the name of the named range
* @param name named range name * @param name named range name
*/ */
public void setNameText(String name){ public void setNameText(String name){
field_12_name_text = name; field_12_name_text = name;
setCompressedUnicodeFlag( field_11_nameIsMultibyte = StringUtil.hasMultibyte(name);
StringUtil.hasMultibyte(name) ? (byte)1 : (byte)0
);
} }
/** sets the custom menu text /** sets the custom menu text
@ -313,72 +217,15 @@ public final class NameRecord extends Record {
return field_2_keyboard_shortcut ; return field_2_keyboard_shortcut ;
} }
/** /**
* gets the name length, in characters * gets the name length, in characters
* @return name length * @return name length
*/ */
public byte getNameTextLength(){ private int getNameTextLength(){
return field_3_length_name_text; if (isBuiltInName()) {
} return 1;
/**
* 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);
} }
return field_3_length_name_text; return field_12_name_text.length();
}
/** 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;
} }
@ -388,13 +235,26 @@ public final class NameRecord extends Record {
public boolean isHiddenName() { public boolean isHiddenName() {
return (field_1_option_flag & Option.OPT_HIDDEN_NAME) != 0; 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() { public boolean isFunctionName() {
return (field_1_option_flag & Option.OPT_FUNCTION_NAME) != 0; 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 * @return true if name is a command
@ -419,7 +279,7 @@ public final class NameRecord extends Record {
*/ */
public boolean isBuiltInName() 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(){ 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 /** Gets the Built In Name
@ -436,19 +296,19 @@ public final class NameRecord extends Record {
*/ */
public byte getBuiltInName() public byte getBuiltInName()
{ {
return this.field_12_builtIn_name; return field_12_built_in_code;
} }
/** gets the definition, reference (Formula) /** 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() { public Ptg[] getNameDefinition() {
return field_13_name_definition; return (Ptg[]) field_13_name_definition.clone();
} }
public void setNameDefinition(Stack nameDefinition) { public void setNameDefinition(Ptg[] ptgs) {
field_13_name_definition = nameDefinition; field_13_name_definition = (Ptg[]) ptgs.clone();
} }
/** get the custom menu text /** get the custom menu text
@ -490,7 +350,8 @@ public final class NameRecord extends Record {
throw new RecordFormatException("NOT A valid Name RECORD"); throw new RecordFormatException("NOT A valid Name RECORD");
} }
} }
/** /**
* called by the class that is responsible for writing this sucker. * called by the class that is responsible for writing this sucker.
* Subclasses should implement this so that their data is passed back in a * 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 * @param data byte array containing instance data
* @return number of bytes written * @return number of bytes written
*/ */
public int serialize( int offset, byte[] data ) public int serialize( int offset, byte[] data ) {
{
LittleEndian.putShort( data, 0 + offset, sid );
short size = (short)( 15 + getTextsLength() + getNameDefinitionSize());
LittleEndian.putShort( data, 2 + offset, size );
// size defined below
LittleEndian.putShort( data, 4 + offset, getOptionFlag() );
data[6 + offset] = getKeyboardShortcut();
data[7 + offset] = getNameTextLength();
LittleEndian.putShort( data, 8 + offset, getDefinitionLength() );
LittleEndian.putShort( data, 10 + offset, getUnused() );
LittleEndian.putUShort( data, 12 + offset, field_6_sheetNumber);
data[14 + offset] = getCustomMenuLength();
data[15 + offset] = getDescriptionTextLength();
data[16 + offset] = getHelpTopicLength();
data[17 + offset] = getStatusBarLength();
data[18 + offset] = getCompressedUnicodeFlag();
int start_of_name_definition = 19 + field_3_length_name_text; int field_7_length_custom_menu = field_14_custom_menu_text.length();
int field_8_length_description_text = field_15_description_text.length();
if (this.isBuiltInName()) { int field_9_length_help_topic_text = field_16_help_topic_text.length();
//can send the builtin name directly in int field_10_length_status_bar_text = field_17_status_bar_text.length();
data [19 + offset] = this.getBuiltInName(); int rawNameSize = getNameRawSize();
} 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;
for (int k = 0; k < list.size(); k++) int formulaTotalSize = Ptg.getEncodedSize(field_13_name_definition);
{ int dataSize = 15 // 4 shorts + 7 bytes
Ptg ptg = ( Ptg ) list.get(k); + rawNameSize
+ field_7_length_custom_menu
result += ptg.getSize(); + 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 /** returns the record size
*/ */
public int getRecordSize(){ public int getRecordSize(){
int result; return 4 // sid + size
+ 15 // 4 shorts + 7 bytes
result = 19 + getTextsLength() + getNameDefinitionSize(); + getNameRawSize()
+ field_14_custom_menu_text.length()
+ field_15_description_text.length()
return result; + field_16_help_topic_text.length()
+ field_17_status_bar_text.length()
+ Ptg.getEncodedSize(field_13_name_definition);
} }
/** gets the extern sheet number /** gets the extern sheet number
* @return extern sheet index * @return extern sheet index
*/ */
public short getExternSheetNumber(){ public short getExternSheetNumber(){
if (field_13_name_definition == null || field_13_name_definition.isEmpty()) return 0; if (field_13_name_definition.length < 1) {
Ptg ptg = (Ptg) field_13_name_definition.peek(); return 0;
short result = 0; }
Ptg ptg = field_13_name_definition[0];
if (ptg.getClass() == Area3DPtg.class){ if (ptg.getClass() == Area3DPtg.class){
result = ((Area3DPtg) ptg).getExternSheetIndex(); return ((Area3DPtg) ptg).getExternSheetIndex();
} else if (ptg.getClass() == Ref3DPtg.class){
result = ((Ref3DPtg) ptg).getExternSheetIndex();
} }
if (ptg.getClass() == Ref3DPtg.class){
return result; return ((Ref3DPtg) ptg).getExternSheetIndex();
}
return 0;
} }
/** sets the extern sheet number /** sets the extern sheet number
@ -609,11 +468,13 @@ public final class NameRecord extends Record {
public void setExternSheetNumber(short externSheetNumber){ public void setExternSheetNumber(short externSheetNumber){
Ptg ptg; Ptg ptg;
if (field_13_name_definition == null || field_13_name_definition.isEmpty()){ if (field_13_name_definition.length < 1){
field_13_name_definition = new Stack();
ptg = createNewPtg(); ptg = createNewPtg();
field_13_name_definition = new Ptg[] {
ptg,
};
} else { } else {
ptg = (Ptg) field_13_name_definition.peek(); ptg = field_13_name_definition[0];
} }
if (ptg.getClass() == Area3DPtg.class){ if (ptg.getClass() == Area3DPtg.class){
@ -625,11 +486,8 @@ public final class NameRecord extends Record {
} }
private Ptg createNewPtg(){ private static Ptg createNewPtg(){
Ptg ptg = new Area3DPtg("A1", 0); // TODO - change to not be partially initialised return new Area3DPtg("A1:A1", 0); // TODO - change to not be partially initialised
field_13_name_definition.push(ptg);
return ptg;
} }
/** gets the reference , the area only (range) /** 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 //Trying to find if what ptg do we need
RangeAddress ra = new RangeAddress(ref); RangeAddress ra = new RangeAddress(ref);
Ptg oldPtg; Ptg oldPtg;
Ptg ptg;
if (field_13_name_definition==null ||field_13_name_definition.isEmpty()){ if (field_13_name_definition.length < 1){
field_13_name_definition = new Stack();
oldPtg = createNewPtg(); oldPtg = createNewPtg();
} else { } else {
//Trying to find extern sheet index //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; short externSheetIndex = 0;
if (oldPtg.getClass() == Area3DPtg.class){ if (oldPtg.getClass() == Area3DPtg.class){
@ -667,29 +523,27 @@ public final class NameRecord extends Record {
if (ra.hasRange()) { if (ra.hasRange()) {
// Is it contiguous or not? // Is it contiguous or not?
AreaReference[] refs = AreaReference[] refs = AreaReference.generateContiguous(ref);
AreaReference.generateContiguous(ref);
this.setDefinitionTextLength((short)0);
// Add the area reference(s) // Add the area reference(s)
for(int i=0; i<refs.length; i++) { for(int i=0; i<refs.length; i++) {
ptg = new Area3DPtg(refs[i].formatAsString(), externSheetIndex); Ptg ptg = new Area3DPtg(refs[i].formatAsString(), externSheetIndex);
field_13_name_definition.push(ptg); temp.add(ptg);
this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) );
} }
// And then a union if we had more than one area // And then a union if we had more than one area
if(refs.length > 1) { if(refs.length > 1) {
ptg = UnionPtg.instance; Ptg ptg = UnionPtg.instance;
field_13_name_definition.push(ptg); temp.add(ptg);
this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) );
} }
} else { } else {
ptg = new Ref3DPtg(); Ptg ptg = new Ref3DPtg();
((Ref3DPtg) ptg).setExternSheetIndex(externSheetIndex); ((Ref3DPtg) ptg).setExternSheetIndex(externSheetIndex);
((Ref3DPtg) ptg).setArea(ref); ((Ref3DPtg) ptg).setArea(ref);
field_13_name_definition.push(ptg); temp.add(ptg);
this.setDefinitionTextLength((short)ptg.getSize());
} }
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 * @param in the RecordInputstream to read the record from
*/ */
protected void fillFields(RecordInputStream in) { protected void fillFields(RecordInputStream in) {
field_1_option_flag = in.readShort(); field_1_option_flag = in.readShort();
field_2_keyboard_shortcut = in.readByte(); field_2_keyboard_shortcut = in.readByte();
field_3_length_name_text = in.readByte(); int field_3_length_name_text = in.readByte();
field_4_length_name_definition = in.readShort(); int field_4_length_name_definition = in.readShort();
field_5_index_to_sheet = in.readShort(); field_5_index_to_sheet = in.readShort();
field_6_sheetNumber = in.readUShort(); field_6_sheetNumber = in.readUShort();
field_7_length_custom_menu = in.readByte(); int field_7_length_custom_menu = in.readUByte();
field_8_length_description_text = in.readByte(); int field_8_length_description_text = in.readUByte();
field_9_length_help_topic_text = in.readByte(); int field_9_length_help_topic_text = in.readUByte();
field_10_length_status_bar_text = in.readByte(); int field_10_length_status_bar_text = in.readUByte();
//store the name in byte form if it's a builtin name //store the name in byte form if it's a built-in name
field_11_compressed_unicode_flag= in.readByte(); field_11_nameIsMultibyte = (in.readByte() != 0);
if (this.isBuiltInName()) { if (isBuiltInName()) {
field_12_builtIn_name = in.readByte(); field_12_built_in_code = in.readByte();
} else { } else {
if (field_11_compressed_unicode_flag == 1) { if (field_11_nameIsMultibyte) {
field_12_name_text = in.readUnicodeLEString(field_3_length_name_text); field_12_name_text = in.readUnicodeLEString(field_3_length_name_text);
} else { } else {
field_12_name_text = in.readCompressedUnicode(field_3_length_name_text); 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??? //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_14_custom_menu_text = in.readCompressedUnicode(field_7_length_custom_menu);
field_15_description_text = in.readCompressedUnicode(field_8_length_description_text);
field_15_description_text = in.readCompressedUnicode(LittleEndian.ubyteToInt(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);
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));
/*} */
} }
/** /**
@ -742,113 +592,90 @@ public final class NameRecord extends Record {
return sid; return sid;
} }
/* /*
20 00 20 00
00 00
01 01
1A 00 // sz = 0x1A = 26 1A 00 // sz = 0x1A = 26
00 00 00 00
01 00 01 00
00 00
00 00
00 00
00 00
00 // unicode flag 00 // unicode flag
07 // name 07 // name
29 17 00 3B 00 00 00 00 FF FF 00 00 02 00 3B 00 //{ 26 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 // } 00 07 00 07 00 00 00 FF 00 10 // }
20 00 20 00
00 00
01 01
0B 00 // sz = 0xB = 11 0B 00 // sz = 0xB = 11
00 00 00 00
01 00 01 00
00 00
00 00
00 00
00 00
00 // unicode flag 00 // unicode flag
07 // name 07 // name
3B 00 00 07 00 07 00 00 00 FF 00 // { 11 } 3B 00 00 07 00 07 00 00 00 FF 00 // { 11 }
*/ */
/* /*
18, 00, 18, 00,
1B, 00, 1B, 00,
20, 00, 20, 00,
00, 00,
01, 01,
0B, 00, 0B, 00,
00, 00,
00, 00,
00, 00,
00, 00,
00, 00,
07, 07,
3B 00 00 07 00 07 00 00 00 FF 00 ] 3B 00 00 07 00 07 00 00 00 FF 00 ]
*/ */
/**
* @see Object#toString()
*/
public String toString() { public String toString() {
StringBuffer buffer = new StringBuffer(); StringBuffer sb = new StringBuffer();
buffer.append("[NAME]\n"); sb.append("[NAME]\n");
buffer.append(" .option flags = ").append( HexDump.toHex( field_1_option_flag ) ) sb.append(" .option flags = ").append(HexDump.shortToHex(field_1_option_flag)).append("\n");
.append("\n"); sb.append(" .keyboard shortcut = ").append(HexDump.byteToHex(field_2_keyboard_shortcut)).append("\n");
buffer.append(" .keyboard shortcut = ").append( HexDump.toHex( field_2_keyboard_shortcut ) ) sb.append(" .length of the name = ").append(getNameTextLength()).append("\n");
.append("\n"); sb.append(" .unused = ").append( field_5_index_to_sheet ).append("\n");
buffer.append(" .length of the name = ").append( field_3_length_name_text ) sb.append(" .index to sheet (1-based, 0=Global) = ").append( field_6_sheetNumber ).append("\n");
.append("\n"); sb.append(" .Menu text length = ").append(field_14_custom_menu_text.length()).append("\n");
buffer.append(" .size of the formula data = ").append( field_4_length_name_definition ) sb.append(" .Description text length= ").append(field_15_description_text.length()).append("\n");
.append("\n"); sb.append(" .Help topic text length = ").append(field_16_help_topic_text.length()).append("\n");
buffer.append(" .unused = ").append( field_5_index_to_sheet ) sb.append(" .Status bar text length = ").append(field_17_status_bar_text.length()).append("\n");
.append("\n"); sb.append(" .NameIsMultibyte = ").append(field_11_nameIsMultibyte).append("\n");
buffer.append(" .index to sheet (1-based, 0=Global) = ").append( field_6_sheetNumber ) sb.append(" .Name (Unicode text) = ").append( getNameText() ).append("\n");
.append("\n"); sb.append(" .Formula (nTokens=").append(field_13_name_definition.length).append("):") .append("\n");
buffer.append(" .Length of menu text (character count) = ").append( field_7_length_custom_menu ) for (int i = 0; i < field_13_name_definition.length; i++) {
.append("\n"); Ptg ptg = field_13_name_definition[i];
buffer.append(" .Length of description text (character count) = ").append( field_8_length_description_text ) sb.append(" " + ptg.toString()).append(ptg.getRVAType()).append("\n");
.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");
} }
buffer.append(" .Menu text (Unicode string without length field) = ").append( field_14_custom_menu_text ) sb.append(" .Menu text = ").append(field_14_custom_menu_text).append("\n");
.append("\n"); sb.append(" .Description text= ").append(field_15_description_text).append("\n");
buffer.append(" .Description text (Unicode string without length field) = ").append( field_15_description_text ) sb.append(" .Help topic text = ").append(field_16_help_topic_text).append("\n");
.append("\n"); sb.append(" .Status bar text = ").append(field_17_status_bar_text).append("\n");
buffer.append(" .Help topic text (Unicode string without length field) = ").append( field_16_help_topic_text ) sb.append("[/NAME]\n");
.append("\n");
buffer.append(" .Status bar text (Unicode string without length field) = ").append( field_17_status_bar_text ) return sb.toString();
.append("\n");
buffer.append("[/NAME]\n");
return buffer.toString();
} }
/**Creates a human readable name for built in types /**Creates a human readable name for built in types
* @return Unknown if the built-in name cannot be translated * @return Unknown if the built-in name cannot be translated
*/ */
protected String translateBuiltInName(byte name) private static String translateBuiltInName(byte name)
{ {
switch (name) switch (name)
{ {
@ -859,14 +686,15 @@ public final class NameRecord extends Record {
case NameRecord.BUILTIN_CONSOLIDATE_AREA : return "Consolidate_Area"; case NameRecord.BUILTIN_CONSOLIDATE_AREA : return "Consolidate_Area";
case NameRecord.BUILTIN_CRITERIA : return "Criteria"; case NameRecord.BUILTIN_CRITERIA : return "Criteria";
case NameRecord.BUILTIN_DATABASE : return "Database"; 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_AREA : return "Print_Area";
case NameRecord.BUILTIN_PRINT_TITLE : return "Print_Titles"; case NameRecord.BUILTIN_PRINT_TITLE : return "Print_Titles";
case NameRecord.BUILTIN_RECORDER : return "Recorder"; case NameRecord.BUILTIN_RECORDER : return "Recorder";
case NameRecord.BUILTIN_SHEET_TITLE : return "Sheet_Title"; case NameRecord.BUILTIN_SHEET_TITLE : return "Sheet_Title";
case NameRecord.BUILTIN_FILTER_DB : return "_FilterDatabase";
} }
return "Unknown"; return "Unknown";
} }
} }

View File

@ -94,10 +94,14 @@ public final class ArrayPtg extends Ptg {
buffer.append("columns = ").append(getColumnCount()).append("\n"); buffer.append("columns = ").append(getColumnCount()).append("\n");
buffer.append("rows = ").append(getRowCount()).append("\n"); buffer.append("rows = ").append(getRowCount()).append("\n");
for (int x=0;x<getColumnCount();x++) { if (token_3_arrayValues == null) {
for (int y=0;y<getRowCount();y++) { buffer.append(" #values#uninitialised#\n");
Object o = token_3_arrayValues[getValueIndex(x, y)]; } else {
buffer.append("[").append(x).append("][").append(y).append("] = ").append(o).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");
}
} }
} }
return buffer.toString(); return buffer.toString();

View File

@ -41,7 +41,7 @@ public final class Ref3DPtg extends OperandPtg {
private static final BitField colRelative = BitFieldFactory.getInstance(0x4000); private static final BitField colRelative = BitFieldFactory.getInstance(0x4000);
private final static int SIZE = 7; // 6 + 1 for Ptg 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 */ /** The row index - zero based unsigned 16 bit value */
private int field_2_row; private int field_2_row;
/** Field 2 /** Field 2
@ -93,10 +93,10 @@ public final class Ref3DPtg extends OperandPtg {
} }
public short getExternSheetIndex(){ 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; field_1_index_extern_sheet = index;
} }

View File

@ -109,8 +109,8 @@ public final class AnalysisToolPak {
r(m, "IMSUB", null); r(m, "IMSUB", null);
r(m, "IMSUM", null); r(m, "IMSUM", null);
r(m, "INTRATE", null); r(m, "INTRATE", null);
r(m, "ISEVEN", null); r(m, "ISEVEN", ParityFunction.IS_EVEN);
r(m, "ISODD", null); r(m, "ISODD", ParityFunction.IS_ODD);
r(m, "LCM", null); r(m, "LCM", null);
r(m, "MDURATION", null); r(m, "MDURATION", null);
r(m, "MROUND", 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.GregorianCalendar;
import java.util.regex.Pattern; 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.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval; 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.EvaluationException;
@ -96,6 +97,9 @@ final class YearFrac implements FreeRefFunction {
Calendar date = parseDate(strVal); Calendar date = parseDate(strVal);
return DateUtil.getExcelDate(date, false); return DateUtil.getExcelDate(date, false);
} }
if (ve instanceof BlankEval) {
return 0.0;
}
return OperandResolver.coerceValueToDouble(ve); return OperandResolver.coerceValueToDouble(ve);
} }
@ -120,7 +124,7 @@ final class YearFrac implements FreeRefFunction {
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new EvaluationException(ErrorEval.VALUE_INVALID); 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 // easy to see this cannot be a valid date
throw new EvaluationException(ErrorEval.VALUE_INVALID); 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)) { if (day <1 || day>cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
throw new EvaluationException(ErrorEval.VALUE_INVALID); throw new EvaluationException(ErrorEval.VALUE_INVALID);
} }
cal.set(Calendar.DAY_OF_MONTH, day);
return cal; return cal;
} }

View File

@ -1,27 +1,22 @@
/* /* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and See the License for the specific language governing permissions and
* limitations under the License. limitations under the License.
*/ ==================================================================== */
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval; 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; * @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 = 2 if A1 contains TRUE or =TRUE
* <li> 1+A1 = #VALUE! 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; public static final OperationEval instance = new AddEval();
private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
));
public AddEval(Ptg ptg) { private AddEval() {
delegate = (AddPtg) ptg; }
}
public ValueEvalToNumericXlator getXlator() { protected double evaluate(double d0, double d1) {
return NUM_XLATOR; return d0 + d1;
} }
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();
}
} }

View File

@ -1,95 +1,36 @@
/* /* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and See the License for the specific language governing permissions and
* limitations under the License. limitations under the License.
*/ ==================================================================== */
package org.apache.poi.hssf.record.formula.eval; 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; * @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 = private DivideEval() {
new ValueEvalToNumericXlator((short) }
( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
));
public DivideEval(Ptg ptg) { protected double evaluate(double d0, double d1) throws EvaluationException {
delegate = (DividePtg) ptg; if (d1 == 0.0) {
} throw new EvaluationException(ErrorEval.DIV_ZERO);
}
protected ValueEvalToNumericXlator getXlator() { return d0 / d1;
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();
}
} }

View File

@ -1,67 +1,34 @@
/* /* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at 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;
import org.apache.poi.hssf.record.formula.EqualPtg; http://www.apache.org/licenses/LICENSE-2.0
import org.apache.poi.hssf.record.formula.Ptg;
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; * @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) { protected boolean convertComparisonResult(int cmpResult) {
this.delegate = (EqualPtg) ptg; return cmpResult == 0;
} }
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();
}
} }

View File

@ -42,7 +42,7 @@ final class ExternalFunction implements FreeRefFunction {
FreeRefFunction targetFunc; FreeRefFunction targetFunc;
try { try {
if (nameArg instanceof NameEval) { if (nameArg instanceof NameEval) {
targetFunc = findInternalUserDefinedFunction(workbook, (NameEval) nameArg); targetFunc = findInternalUserDefinedFunction((NameEval) nameArg);
} else if (nameArg instanceof NameXEval) { } else if (nameArg instanceof NameXEval) {
targetFunc = findExternalUserDefinedFunction(workbook, (NameXEval) nameArg); targetFunc = findExternalUserDefinedFunction(workbook, (NameXEval) nameArg);
} else { } else {
@ -65,7 +65,7 @@ final class ExternalFunction implements FreeRefFunction {
if(false) { if(false) {
System.out.println("received call to external user defined function (" + functionName + ")"); 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. // not sure how much this logic would need to change to support other or multiple add-ins.
FreeRefFunction result = AnalysisToolPak.findFunction(functionName); FreeRefFunction result = AnalysisToolPak.findFunction(functionName);
if (result != null) { if (result != null) {
@ -74,24 +74,12 @@ final class ExternalFunction implements FreeRefFunction {
throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED); throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED);
} }
private FreeRefFunction findInternalUserDefinedFunction(Workbook workbook, NameEval functionNameEval) throws EvaluationException { private FreeRefFunction findInternalUserDefinedFunction(NameEval functionNameEval) throws EvaluationException {
String functionName = functionNameEval.getFunctionName();
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);
if(false) { if(false) {
System.out.println("received call to internal user defined function (" + functionName + ")"); 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 // TODO find the implementation for the user defined function
// throw the right errors in these cases
// TODO find the implementation for the external function e.g. "YEARFRAC" or "ISEVEN"
throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED); throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED);
} }

View File

@ -1,67 +1,34 @@
/* /* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at 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;
import org.apache.poi.hssf.record.formula.GreaterEqualPtg; http://www.apache.org/licenses/LICENSE-2.0
import org.apache.poi.hssf.record.formula.Ptg;
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; * @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 static final OperationEval instance = new GreaterEqualEval();
public GreaterEqualEval(Ptg ptg) { private GreaterEqualEval() {
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();
}
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 Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at 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;
import org.apache.poi.hssf.record.formula.GreaterThanPtg; http://www.apache.org/licenses/LICENSE-2.0
import org.apache.poi.hssf.record.formula.Ptg;
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; * @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 static final OperationEval instance = new GreaterThanEval();
public GreaterThanEval(Ptg ptg) { private GreaterThanEval() {
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();
}
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 Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and See the License for the specific language governing permissions and
* limitations under the License. limitations under the License.
*/ ==================================================================== */
package org.apache.poi.hssf.record.formula.eval; 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.Row;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook; 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 { public final class LazyAreaEval extends AreaEvalBase {
private final Sheet _sheet; 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); super(ptg);
_sheet = sheet; _sheet = sheet;
_workbook = workbook; _evaluator = evaluator;
} }
public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) { public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
@ -53,13 +54,27 @@ public final class LazyAreaEval extends AreaEvalBase {
if (cell == null) { if (cell == null) {
return BlankEval.INSTANCE; 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) { public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(), AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); 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; package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.AreaI; 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.Ref3DPtg;
import org.apache.poi.hssf.record.formula.RefPtg; 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.Cell;
import org.apache.poi.ss.usermodel.FormulaEvaluator; import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellReference;
/**
*
* @author Josh Micich
*/
public final class LazyRefEval extends RefEvalBase { public final class LazyRefEval extends RefEvalBase {
private final Sheet _sheet; 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()); super(ptg.getRow(), ptg.getColumn());
_sheet = sheet; _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()); super(ptg.getRow(), ptg.getColumn());
_sheet = sheet; _sheet = sheet;
_workbook = workbook; _evaluator = evaluator;
} }
public ValueEval getInnerValueEval() { public ValueEval getInnerValueEval() {
@ -39,7 +60,7 @@ public final class LazyRefEval extends RefEvalBase {
if (cell == null) { if (cell == null) {
return BlankEval.INSTANCE; 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) { 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(), AreaI area = new OffsetArea(getRow(), getColumn(),
relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx); 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 Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at 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;
import org.apache.poi.hssf.record.formula.LessEqualPtg; http://www.apache.org/licenses/LICENSE-2.0
import org.apache.poi.hssf.record.formula.Ptg;
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; * @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 static final OperationEval instance = new LessEqualEval();
public LessEqualEval(Ptg ptg) { private LessEqualEval() {
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();
}
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 Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at 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;
import org.apache.poi.hssf.record.formula.LessThanPtg; http://www.apache.org/licenses/LICENSE-2.0
import org.apache.poi.hssf.record.formula.Ptg;
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; * @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 static final OperationEval instance = new LessThanEval();
public LessThanEval(Ptg ptg) { private LessThanEval() {
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();
}
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 Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and See the License for the specific language governing permissions and
* limitations under the License. limitations under the License.
*/ ==================================================================== */
package org.apache.poi.hssf.record.formula.eval; 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; * @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 = private MultiplyEval() {
new ValueEvalToNumericXlator((short) }
( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
));
public MultiplyEval(Ptg ptg) { protected double evaluate(double d0, double d1) {
delegate = (MultiplyPtg) ptg; return d0 * d1;
} }
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();
}
} }

View File

@ -22,26 +22,24 @@ package org.apache.poi.hssf.record.formula.eval;
*/ */
public final class NameEval implements 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) { public NameEval(String functionName) {
_index = index; _functionName = functionName;
} }
/**
* @return zero based index to a defined name record public String getFunctionName() {
*/ return _functionName;
public int getIndex() {
return _index;
} }
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(64); StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" ["); sb.append(getClass().getName()).append(" [");
sb.append(_index); sb.append(_functionName);
sb.append("]"); sb.append("]");
return sb.toString(); return sb.toString();
} }

View File

@ -1,68 +1,34 @@
/* /* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at 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;
import org.apache.poi.hssf.record.formula.NotEqualPtg; http://www.apache.org/licenses/LICENSE-2.0
import org.apache.poi.hssf.record.formula.Ptg;
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; * @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 static final OperationEval instance = new NotEqualEval();
public NotEqualEval(Ptg ptg) { private NotEqualEval() {
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();
}
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; 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/> * Implementation of Excel formula token '%'. <p/>
* @author Josh Micich * @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( private PercentEval() {
(short) (ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.REF_STRING_IS_PARSED));
public PercentEval(Ptg ptg) {
_delegate = (PercentPtg) ptg;
}
protected ValueEvalToNumericXlator getXlator() {
return NUM_XLATOR;
} }
public Eval evaluate(Eval[] args, int srcRow, short srcCol) { public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
if (args.length != 1) { if (args.length != 1) {
return ErrorEval.VALUE_INVALID; return ErrorEval.VALUE_INVALID;
} }
double d0;
ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol); try {
if (ve instanceof NumericValueEval) { ValueEval ve = OperandResolver.getSingleValue(args[0], srcRow, srcCol);
double d0 = ((NumericValueEval) ve).getNumberValue(); if (ve instanceof BlankEval) {
return new NumberEval(d0 / 100); return NumberEval.ZERO;
}
d0 = OperandResolver.coerceValueToDouble(ve);
} catch (EvaluationException e) {
return e.getErrorEval();
} }
return new NumberEval(d0 / 100);
if (ve instanceof BlankEval) {
return NumberEval.ZERO;
}
if (ve instanceof ErrorEval) {
return ve;
}
return ErrorEval.VALUE_INVALID;
} }
public int getNumberOfOperands() { public int getNumberOfOperands() {
return _delegate.getNumberOfOperands(); return 1;
}
public int getType() {
return _delegate.getType();
} }
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 Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and See the License for the specific language governing permissions and
* limitations under the License. limitations under the License.
*/ ==================================================================== */
package org.apache.poi.hssf.record.formula.eval; 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; * @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 = private PowerEval() {
new ValueEvalToNumericXlator((short) }
( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
));
public PowerEval(Ptg ptg) { protected double evaluate(double d0, double d1) {
delegate = (PowerPtg) ptg; return Math.pow(d0, d1);
} }
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();
}
} }

View File

@ -1,216 +1,145 @@
/* /* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and See the License for the specific language governing permissions and
* limitations under the License. limitations under the License.
*/ ==================================================================== */
/*
* Created on May 10, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval; 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; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/ */
public abstract class RelationalOperationEval implements OperationEval { public abstract class RelationalOperationEval implements OperationEval {
protected class RelationalValues { /**
public Double[] ds = new Double[2]; * Converts a standard compare result (-1, 0, 1) to <code>true</code> or <code>false</code>
public Boolean[] bs = new Boolean[2]; * according to subclass' comparison type.
public String[] ss = new String[3]; */
public ErrorEval ee = null; protected abstract boolean convertComparisonResult(int cmpResult);
}
/**
/* * This is a description of how the relational operators apply in MS Excel.
* This is a description of how the relational operators apply in MS Excel. * Use this as a guideline when testing/implementing the evaluate methods
* Use this as a guideline when testing/implementing the evaluate methods * for the relational operators Evals.
* for the relational operators Evals. *
* * <pre>
* Bool > any number. ALWAYS * Bool.TRUE > any number.
* Bool > any string. ALWAYS * Bool > any string. ALWAYS
* Bool.TRUE > Bool.FALSE * Bool.TRUE > Bool.FALSE
* * Bool.FALSE == Blank
* String > any number. ALWAYS *
* String > Blank. ALWAYS * Strings are never converted to numbers or booleans
* String are sorted dictionary wise * String > any number. ALWAYS
* * Non-empty String > Blank
* Blank == 0 (numeric) * Empty String == Blank
*/ * String are sorted dictionary wise
public RelationalValues doEvaluate(Eval[] operands, int srcRow, short srcCol) { *
RelationalValues retval = new RelationalValues(); * Blank > Negative numbers
* Blank == 0
switch (operands.length) { * Blank < Positive numbers
default: * </pre>
retval.ee = ErrorEval.VALUE_INVALID; */
break; public final Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
case 2: if (operands.length != 2) {
internalDoEvaluate(operands, srcRow, srcCol, retval, 0); return ErrorEval.VALUE_INVALID;
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;
}
// if both null return 0, else non null wins, else string compare ValueEval vA;
protected int doComparison(String[] ss) { ValueEval vB;
int retval = 0; try {
if (ss[0] != null || ss[1] != null) { vA = OperandResolver.getSingleValue(operands[0], srcRow, srcCol);
retval = ss[0] != null vB = OperandResolver.getSingleValue(operands[1], srcRow, srcCol);
? ss[1] != null } catch (EvaluationException e) {
? ss[0].compareTo(ss[1]) return e.getErrorEval();
: 1 }
: ss[1] != null int cmpResult = doCompare(vA, vB);
? -1 boolean result = convertComparisonResult(cmpResult);
: 0; return BoolEval.valueOf(result);
} }
return retval;
}
// if both null return 0, else non null wins, else doublevalue compare private static int doCompare(ValueEval va, ValueEval vb) {
protected int doComparison(Double[] ds) { // special cases when one operand is blank
int retval = 0; if (va == BlankEval.INSTANCE) {
if (ds[0] != null || ds[1] != null) { return compareBlank(vb);
retval = ds[0] != null }
? ds[1] != null if (vb == BlankEval.INSTANCE) {
? ds[0].compareTo(ds[1]) return -compareBlank(va);
: 1 }
: ds[1] != null
? -1 if (va instanceof BoolEval) {
: 0; if (vb instanceof BoolEval) {
} BoolEval bA = (BoolEval) va;
return retval; 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 Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and See the License for the specific language governing permissions and
* limitations under the License. limitations under the License.
*/ ==================================================================== */
package org.apache.poi.hssf.record.formula.eval; 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; * @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 = private SubtractEval() {
new ValueEvalToNumericXlator((short) }
( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
));
public SubtractEval(Ptg ptg) { protected double evaluate(double d0, double d1) {
delegate = (SubtractPtg) ptg; return d0 - d1;
} }
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();
}
} }

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 Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and See the License for the specific language governing permissions and
* limitations under the License. limitations under the License.
*/ ==================================================================== */
package org.apache.poi.hssf.record.formula.eval; 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; * @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; public static final OperationEval instance = new UnaryMinusEval();
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 UnaryMinusEval() {
}
public UnaryMinusEval(Ptg ptg) { public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
this.delegate = (UnaryMinusPtg) ptg; 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; 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; * @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 { public final class UnaryPlusEval implements OperationEval {
private UnaryPlusPtg delegate; public static final OperationEval instance = new UnaryPlusEval();
/** private UnaryPlusEval() {
* called by reflection
*/
public UnaryPlusEval(Ptg ptg) {
this.delegate = (UnaryPlusPtg) ptg;
} }
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
@ -59,10 +53,10 @@ public final class UnaryPlusEval implements OperationEval {
} }
public int getNumberOfOperands() { public int getNumberOfOperands() {
return delegate.getNumberOfOperands(); return 1;
} }
public int getType() { 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 * @param eval
*/ */
public ValueEval attemptXlateToNumeric(ValueEval eval) { public ValueEval attemptXlateToNumeric(ValueEval eval) {
ValueEval retval = null;
if (eval == null) { if (eval == null) {
retval = BlankEval.INSTANCE; throw new IllegalArgumentException("eval must not be null");
} }
// most common case - least worries :) // most common case - least worries :)
else if (eval instanceof NumberEval) { if (eval instanceof NumberEval) {
retval = eval; return eval;
} }
// booleval if (eval instanceof BoolEval) {
else if (eval instanceof BoolEval) { return ((flags & BOOL_IS_PARSED) > 0)
retval = ((flags & BOOL_IS_PARSED) > 0)
? (NumericValueEval) eval ? (NumericValueEval) eval
: xlateBlankEval(BLANK_IS_PARSED); : xlateBlankEval(BLANK_IS_PARSED);
} }
// stringeval if (eval instanceof StringEval) {
else if (eval instanceof StringEval) { return xlateStringEval((StringEval) eval); // TODO: recursive call needed
retval = xlateStringEval((StringEval) eval); // TODO: recursive call needed
} }
// refeval if (eval instanceof RefEval) {
else if (eval instanceof RefEval) { return xlateRefEval((RefEval) eval);
retval = xlateRefEval((RefEval) eval);
} }
// erroreval if (eval instanceof ErrorEval) {
else if (eval instanceof ErrorEval) { return eval;
retval = eval;
} }
else if (eval instanceof BlankEval) { if (eval instanceof BlankEval) {
retval = xlateBlankEval(BLANK_IS_PARSED); return xlateBlankEval(BLANK_IS_PARSED);
} }
// probably AreaEval? then not acceptable. // probably AreaEval? then not acceptable.
else { throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass());
throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass());
}
return retval;
} }
/** /**

View File

@ -60,8 +60,7 @@ public final class Hlookup implements Function {
AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]); AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]);
boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol); boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol);
int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createRowVector(tableArray, 0), isRangeLookup); int colIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createRowVector(tableArray, 0), isRangeLookup);
ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol); int rowIndex = LookupUtils.resolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol);
int rowIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex);
ValueVector resultCol = createResultColumnVector(tableArray, rowIndex); ValueVector resultCol = createResultColumnVector(tableArray, rowIndex);
return resultCol.getItem(colIndex); return resultCol.getItem(colIndex);
} catch (EvaluationException e) { } catch (EvaluationException e) {
@ -73,15 +72,14 @@ public final class Hlookup implements Function {
/** /**
* Returns one column from an <tt>AreaEval</tt> * 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 { private ValueVector createResultColumnVector(AreaEval tableArray, int rowIndex) throws EvaluationException {
if(colIndex < 0) { if(rowIndex >= tableArray.getHeight()) {
throw EvaluationException.invalidValue();
}
if(colIndex >= tableArray.getWidth()) {
throw EvaluationException.invalidRef(); 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> * <tr><td>&lt;blank&gt;</td><td>&nbsp;</td><td>#VALUE!</td></tr>
* </table><br/> * </table><br/>
* *
* * Note - out of range errors (both too high and too low) are handled by the caller. * Note - out of range errors (result index too high) are handled by the caller.
* @return column or row index as a zero-based value * @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 { public static int resolveRowOrColIndexArg(Eval rowColIndexArg, int srcCellRow, int srcCellCol) throws EvaluationException {
if(veRowColIndexArg == null) { if(rowColIndexArg == null) {
throw new IllegalArgumentException("argument must not be 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) { 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(); throw EvaluationException.invalidValue();
} }
if(veRowColIndexArg instanceof StringEval) { return oneBasedIndex - 1; // convert to zero based
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;
} }
@ -583,11 +598,13 @@ final class LookupUtils {
return maxIx - 1; return maxIx - 1;
} }
public static LookupValueComparer createLookupComparer(ValueEval lookupValue) throws EvaluationException { public static LookupValueComparer createLookupComparer(ValueEval lookupValue) {
if (lookupValue instanceof BlankEval) { if (lookupValue == BlankEval.INSTANCE) {
// blank eval can never be found in a lookup array // blank eval translates to zero
throw new EvaluationException(ErrorEval.NA); // 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) { if (lookupValue instanceof StringEval) {
return new StringLookupComparer((StringEval) lookupValue); return new StringLookupComparer((StringEval) lookupValue);

View File

@ -60,8 +60,7 @@ public final class Vlookup implements Function {
AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]); AreaEval tableArray = LookupUtils.resolveTableArrayArg(args[1]);
boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol); boolean isRangeLookup = LookupUtils.resolveRangeLookupArg(arg3, srcCellRow, srcCellCol);
int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createColumnVector(tableArray, 0), isRangeLookup); int rowIndex = LookupUtils.lookupIndexOfValue(lookupValue, LookupUtils.createColumnVector(tableArray, 0), isRangeLookup);
ValueEval veColIndex = OperandResolver.getSingleValue(args[2], srcCellRow, srcCellCol); int colIndex = LookupUtils.resolveRowOrColIndexArg(args[2], srcCellRow, srcCellCol);
int colIndex = LookupUtils.resolveRowOrColIndexArg(veColIndex);
ValueVector resultCol = createResultColumnVector(tableArray, colIndex); ValueVector resultCol = createResultColumnVector(tableArray, colIndex);
return resultCol.getItem(rowIndex); return resultCol.getItem(rowIndex);
} catch (EvaluationException e) { } catch (EvaluationException e) {
@ -73,12 +72,11 @@ public final class Vlookup implements Function {
/** /**
* Returns one column from an <tt>AreaEval</tt> * 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 { private ValueVector createResultColumnVector(AreaEval tableArray, int colIndex) throws EvaluationException {
if(colIndex < 0) {
throw EvaluationException.invalidValue();
}
if(colIndex >= tableArray.getWidth()) { if(colIndex >= tableArray.getWidth()) {
throw EvaluationException.invalidRef(); throw EvaluationException.invalidRef();
} }

View File

@ -23,15 +23,15 @@ import org.apache.poi.hssf.util.RangeAddress;
import org.apache.poi.ss.usermodel.Name; 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. * 'named range' or name of a user defined function.
* *
* @author Libin Roman (Vista Portal LDT. Developer) * @author Libin Roman (Vista Portal LDT. Developer)
*/ */
public class HSSFName implements Name { public class HSSFName implements Name {
private HSSFWorkbook _book; private HSSFWorkbook _book;
private NameRecord _definedNameRec; private NameRecord _definedNameRec;
/** Creates new HSSFName - called by HSSFWorkbook to create a sheet from /** Creates new HSSFName - called by HSSFWorkbook to create a sheet from
* scratch. * scratch.
* *
@ -43,58 +43,57 @@ public class HSSFName implements Name {
_book = book; _book = book;
_definedNameRec = name; _definedNameRec = name;
} }
/** Get the sheets name which this named range is referenced to /** Get the sheets name which this named range is referenced to
* @return sheet name, which this named range referred to * @return sheet name, which this named range referred to
*/ */
public String getSheetName() { public String getSheetName() {
short indexToExternSheet = _definedNameRec.getExternSheetNumber(); short indexToExternSheet = _definedNameRec.getExternSheetNumber();
return _book.getWorkbook().findSheetNameFromExternSheet(indexToExternSheet); return _book.getWorkbook().findSheetNameFromExternSheet(indexToExternSheet);
} }
/** /**
* @return text name of this defined name * @return text name of this defined name
*/ */
public String getNameName(){ public String getNameName(){
return _definedNameRec.getNameText(); return _definedNameRec.getNameText();
} }
/** /**
* sets the name of the named range * sets the name of the named range
* @param nameName named range name to set * @param nameName named range name to set
*/ */
public void setNameName(String nameName){ public void setNameName(String nameName){
_definedNameRec.setNameText(nameName); _definedNameRec.setNameText(nameName);
_definedNameRec.setNameTextLength((byte)nameName.length());
Workbook wb = _book.getWorkbook(); Workbook wb = _book.getWorkbook();
//Check to ensure no other names have the same case-insensitive name //Check to ensure no other names have the same case-insensitive name
for ( int i = wb.getNumNames()-1; i >=0; i-- ) for ( int i = wb.getNumNames()-1; i >=0; i-- )
{ {
NameRecord rec = wb.getNameRecord(i); NameRecord rec = wb.getNameRecord(i);
if (rec != _definedNameRec) { if (rec != _definedNameRec) {
if (rec.getNameText().equalsIgnoreCase(getNameName())) if (rec.getNameText().equalsIgnoreCase(getNameName()))
throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)"); throw new IllegalArgumentException("The workbook already contains this name (case-insensitive)");
} }
} }
} }
/** /**
* Note - this method only applies to named ranges * Note - this method only applies to named ranges
* @return the formula text defining the named range * @return the formula text defining the named range
*/ */
public String getReference() { public String getReference() {
if (_definedNameRec.isFunctionName()) { if (_definedNameRec.isFunctionName()) {
throw new IllegalStateException("Only applicable to named ranges"); throw new IllegalStateException("Only applicable to named ranges");
} }
return _definedNameRec.getAreaReference(_book); return _definedNameRec.getAreaReference(_book);
} }
/** /**
* sets the sheet name which this named range referenced to * sets the sheet name which this named range referenced to
* @param sheetName the sheet name of the reference * @param sheetName the sheet name of the reference
*/ */
private void setSheetName(String sheetName){ private void setSheetName(String sheetName){
int sheetNumber = _book.getSheetIndex(sheetName); int sheetNumber = _book.getSheetIndex(sheetName);
short externSheetNumber = (short) short externSheetNumber = (short)
@ -102,11 +101,11 @@ public class HSSFName implements Name {
_definedNameRec.setExternSheetNumber(externSheetNumber); _definedNameRec.setExternSheetNumber(externSheetNumber);
} }
/** /**
* sets the reference of this named range * sets the reference of this named range
* @param ref the reference to set * @param ref the reference to set
*/ */
public void setReference(String ref){ public void setReference(String ref){
RangeAddress ra = new RangeAddress(ref); RangeAddress ra = new RangeAddress(ref);
@ -117,7 +116,7 @@ public class HSSFName implements Name {
setSheetName(sheetName); setSheetName(sheetName);
} }
//allow the poi utilities to parse it out //allow the poi utilities to parse it out
_definedNameRec.setAreaReference(ref); _definedNameRec.setAreaReference(ref);
} }
@ -131,7 +130,7 @@ public class HSSFName implements Name {
return "#REF!".endsWith(ref); return "#REF!".endsWith(ref);
} }
public boolean isFunctionName() { public boolean isFunctionName() {
return _definedNameRec.isFunctionName(); return _definedNameRec.isFunctionName();
} }
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(64); StringBuffer sb = new StringBuffer(64);

View File

@ -27,7 +27,6 @@ import java.util.ArrayList;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Stack;
import org.apache.poi.POIDocument; import org.apache.poi.POIDocument;
import org.apache.poi.ddf.EscherBSERecord; 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.ddf.EscherRecord;
import org.apache.poi.hssf.model.Sheet; import org.apache.poi.hssf.model.Sheet;
import org.apache.poi.hssf.model.Workbook; 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.AbstractEscherHolderRecord;
import org.apache.poi.hssf.record.BackupRecord; import org.apache.poi.hssf.record.BackupRecord;
import org.apache.poi.hssf.record.DrawingGroupRecord; 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.Area3DPtg;
import org.apache.poi.hssf.record.formula.MemFuncPtg; import org.apache.poi.hssf.record.formula.MemFuncPtg;
import org.apache.poi.hssf.record.formula.NameXPtg; 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.record.formula.UnionPtg;
import org.apache.poi.hssf.util.CellReference; import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.hssf.util.SheetReferences; 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. * @return HSSFSheet representing the cloned sheet.
*/ */
public HSSFSheet cloneSheet(int sheetNum) { public HSSFSheet cloneSheet(int sheetIndex) {
validateSheetIndex(sheetNum); validateSheetIndex(sheetIndex);
HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetNum); HSSFSheet srcSheet = (HSSFSheet) _sheets.get(sheetIndex);
String srcName = workbook.getSheetName(sheetNum); String srcName = workbook.getSheetName(sheetIndex);
HSSFSheet clonedSheet = srcSheet.cloneSheet(this); HSSFSheet clonedSheet = srcSheet.cloneSheet(this);
clonedSheet.setSelected(false); clonedSheet.setSelected(false);
clonedSheet.setActive(false); clonedSheet.setActive(false);
String name = getUniqueSheetName(srcName);
int newSheetIndex = _sheets.size();
_sheets.add(clonedSheet); _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) { while (true) {
// Try and find the next sheet name that is unique // Try and find the next sheet name that is unique
String name = srcName; String index = Integer.toString(uniqueIndex++);
String index = Integer.toString(i++); String name;
if (name.length() + index.length() + 2 < 31) { if (baseName.length() + index.length() + 2 < 31) {
name = name + "(" + index + ")"; name = baseName + " (" + index + ")";
} else { } 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 the sheet name is unique, then set it otherwise move on to the next number.
if (workbook.getSheetIndex(name) == -1) { if (workbook.getSheetIndex(name) == -1) {
workbook.setSheetName(_sheets.size()-1, name); return name;
break;
} }
} }
return clonedSheet;
} }
/** /**
@ -907,7 +957,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
boolean removingRange = boolean removingRange =
startColumn == -1 && endColumn == -1 && startRow == -1 && endRow == -1; startColumn == -1 && endColumn == -1 && startRow == -1 && endRow == -1;
int rowColHeaderNameIndex = findExistingRowColHeaderNameRecordIdx(sheetIndex); int rowColHeaderNameIndex = findExistingBuiltinNameRecordIdx(sheetIndex, NameRecord.BUILTIN_PRINT_TITLE);
if (removingRange) { if (removingRange) {
if (rowColHeaderNameIndex >= 0) { if (rowColHeaderNameIndex >= 0) {
workbook.removeName(rowColHeaderNameIndex); workbook.removeName(rowColHeaderNameIndex);
@ -925,29 +975,27 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
isNewRecord = false; isNewRecord = false;
} }
short definitionTextLength = settingRowAndColumn ? (short)0x001a : (short)0x000b; List temp = new ArrayList();
nameRecord.setDefinitionTextLength(definitionTextLength); // TODO - remove
Stack ptgs = new Stack();
if (settingRowAndColumn) { if (settingRowAndColumn) {
final int exprsSize = 2 * 11 + 1; // 2 * Area3DPtg.SIZE + UnionPtg.SIZE final int exprsSize = 2 * 11 + 1; // 2 * Area3DPtg.SIZE + UnionPtg.SIZE
ptgs.add(new MemFuncPtg(exprsSize)); temp.add(new MemFuncPtg(exprsSize));
} }
if (startColumn >= 0) { if (startColumn >= 0) {
Area3DPtg colArea = new Area3DPtg(0, MAX_ROW, startColumn, endColumn, Area3DPtg colArea = new Area3DPtg(0, MAX_ROW, startColumn, endColumn,
false, false, false, false, externSheetIndex); false, false, false, false, externSheetIndex);
ptgs.add(colArea); temp.add(colArea);
} }
if (startRow >= 0) { if (startRow >= 0) {
Area3DPtg rowArea = new Area3DPtg(startRow, endRow, 0, MAX_COLUMN, Area3DPtg rowArea = new Area3DPtg(startRow, endRow, 0, MAX_COLUMN,
false, false, false, false, externSheetIndex); false, false, false, false, externSheetIndex);
ptgs.add(rowArea); temp.add(rowArea);
} }
if (settingRowAndColumn) if (settingRowAndColumn) {
{ temp.add(UnionPtg.instance);
ptgs.add(UnionPtg.instance);
} }
Ptg[] ptgs = new Ptg[temp.size()];
temp.toArray(ptgs);
nameRecord.setNameDefinition(ptgs); nameRecord.setNameDefinition(ptgs);
if (isNewRecord) 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++) { for(int defNameIndex =0; defNameIndex<names.size(); defNameIndex++) {
NameRecord r = workbook.getNameRecord(defNameIndex); NameRecord r = workbook.getNameRecord(defNameIndex);
if (r == null) { if (r == null) {
throw new RuntimeException("Unable to find all defined names to iterate over"); throw new RuntimeException("Unable to find all defined names to iterate over");
} }
if (!isRowColHeaderRecord( r )) { if (!r.isBuiltInName() || r.getBuiltInName() != builtinCode) {
continue; continue;
} }
if(r.getSheetNumber() == 0) { if(r.getSheetNumber() == 0) {
@ -985,10 +1033,6 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
return -1; 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 * create a new Font and add it to the workbook's font table
* @return new font object * @return new font object
@ -1288,6 +1332,9 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
return result; return result;
} }
public NameRecord getNameRecord(int nameIndex) {
return getWorkbook().getNameRecord(nameIndex);
}
/** gets the named range name /** gets the named range name
* @param index the named range index (0 based) * @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.Iterator;
import java.util.Stack; 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.model.FormulaParser;
import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.AreaPtg; import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.BoolPtg; 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; 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 Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
* * @author Josh Micich
*/ */
public class FormulaEvaluator { 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 Sheet _sheet;
protected Workbook _workbook; protected Workbook _workbook;
private final EvaluationCache _cache;
private Counter _evaluationCounter;
public FormulaEvaluator(Sheet sheet, Workbook workbook) { 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; _sheet = sheet;
_workbook = workbook; _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) { public void setCurrentRow(Row row) {
// do nothing // 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()); retval.setErrorValue(cell.getErrorCellValue());
break; break;
case Cell.CELL_TYPE_FORMULA: case Cell.CELL_TYPE_FORMULA:
retval = getCellValueForEval(internalEvaluate(cell, _sheet, _workbook), _workbook.getCreationHelper()); retval = getCellValueForEval(internalEvaluate(cell, _sheet), _workbook.getCreationHelper());
break; break;
case Cell.CELL_TYPE_NUMERIC: case Cell.CELL_TYPE_NUMERIC:
retval = new CellValue(Cell.CELL_TYPE_NUMERIC, _workbook.getCreationHelper()); retval = new CellValue(Cell.CELL_TYPE_NUMERIC, _workbook.getCreationHelper());
@ -140,7 +203,7 @@ public class FormulaEvaluator {
if (cell != null) { if (cell != null) {
switch (cell.getCellType()) { switch (cell.getCellType()) {
case Cell.CELL_TYPE_FORMULA: 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()) { switch (cv.getCellType()) {
case Cell.CELL_TYPE_BOOLEAN: case Cell.CELL_TYPE_BOOLEAN:
cell.setCellValue(cv.getBooleanValue()); cell.setCellValue(cv.getBooleanValue());
@ -185,7 +248,7 @@ public class FormulaEvaluator {
if (cell != null) { if (cell != null) {
switch (cell.getCellType()) { switch (cell.getCellType()) {
case Cell.CELL_TYPE_FORMULA: 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()) { switch (cv.getCellType()) {
case Cell.CELL_TYPE_BOOLEAN: case Cell.CELL_TYPE_BOOLEAN:
cell.setCellType(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. * else a runtime exception will be thrown somewhere inside the method.
* (Hence this is a private 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(); 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(); EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) { if(!tracker.startEvaluate(_workbook, sheet, srcRowNum, srcColNum)) {
return ErrorEval.CIRCULAR_REF_ERROR; return ErrorEval.CIRCULAR_REF_ERROR;
} }
try { try {
return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula()); result = evaluateCell(srcRowNum, (short)srcColNum, srcCell.getCellFormula());
} finally { } 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, private ValueEval evaluateCell(int srcRowNum, short srcColNum, String cellFormulaText) {
int srcRowNum, short srcColNum, String cellFormulaText) {
Ptg[] ptgs = FormulaParser.parse(cellFormulaText, workbook); Ptg[] ptgs = FormulaParser.parse(cellFormulaText, _workbook);
Stack stack = new Stack(); Stack stack = new Stack();
for (int i = 0, iSize = ptgs.length; i < iSize; i++) { for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
@ -314,19 +391,10 @@ public class FormulaEvaluator {
continue; continue;
} }
if (ptg instanceof MemErrPtg) { continue; } if (ptg instanceof MemErrPtg) { continue; }
if (ptg instanceof MissingArgPtg) { continue; } if (ptg instanceof MissingArgPtg) {
if (ptg instanceof NamePtg) { // TODO - might need to push BlankEval or MissingArgEval
// named ranges, macro functions continue;
NamePtg namePtg = (NamePtg) ptg;
stack.push(new NameEval(namePtg.getIndex()));
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; Eval opResult;
if (ptg instanceof OperationPtg) { if (ptg instanceof OperationPtg) {
OperationPtg optg = (OperationPtg) ptg; OperationPtg optg = (OperationPtg) ptg;
@ -343,10 +411,15 @@ public class FormulaEvaluator {
Eval p = (Eval) stack.pop(); Eval p = (Eval) stack.pop();
ops[j] = p; 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 { } 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); stack.push(opResult);
} }
@ -403,28 +476,63 @@ public class FormulaEvaluator {
return operation.evaluate(ops, srcRowNum, srcColNum); 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 * returns an appropriate Eval impl instance for the Ptg. The Ptg must be
* one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg, * one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,
* StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be * StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
* passed here! * 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) { if (ptg instanceof RefPtg) {
return new LazyRefEval(((RefPtg) ptg), sheet, workbook); return new LazyRefEval(((RefPtg) ptg), sheet, this);
} }
if (ptg instanceof Ref3DPtg) { if (ptg instanceof Ref3DPtg) {
Ref3DPtg refPtg = (Ref3DPtg) ptg; Ref3DPtg refPtg = (Ref3DPtg) ptg;
Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex())); Sheet xsheet = getOtherSheet(refPtg.getExternSheetIndex());
return new LazyRefEval(refPtg, xsheet, workbook); return new LazyRefEval(refPtg, xsheet, createEvaluatorForAnotherSheet(xsheet));
} }
if (ptg instanceof AreaPtg) { if (ptg instanceof AreaPtg) {
return new LazyAreaEval(((AreaPtg) ptg), sheet, workbook); return new LazyAreaEval(((AreaPtg) ptg), sheet, this);
} }
if (ptg instanceof Area3DPtg) { if (ptg instanceof Area3DPtg) {
Area3DPtg a3dp = (Area3DPtg) ptg; Area3DPtg a3dp = (Area3DPtg) ptg;
Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex())); Sheet xsheet = getOtherSheet(a3dp.getExternSheetIndex());
return new LazyAreaEval(a3dp, xsheet, workbook); return new LazyAreaEval(a3dp, xsheet, createEvaluatorForAnotherSheet(xsheet));
} }
if (ptg instanceof IntPtg) { if (ptg instanceof IntPtg) {
@ -442,8 +550,19 @@ public class FormulaEvaluator {
if (ptg instanceof ErrPtg) { if (ptg instanceof ErrPtg) {
return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode()); 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() + ")"); 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 * 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 * impl instance and return that. Since the cell could be an external
@ -453,7 +572,7 @@ public class FormulaEvaluator {
* @param sheet * @param sheet
* @param workbook * @param workbook
*/ */
public static ValueEval getEvalForCell(Cell cell, Sheet sheet, Workbook workbook) { public ValueEval getEvalForCell(Cell cell, Sheet sheet) {
if (cell == null) { if (cell == null) {
return BlankEval.INSTANCE; return BlankEval.INSTANCE;
@ -464,7 +583,7 @@ public class FormulaEvaluator {
case Cell.CELL_TYPE_STRING: case Cell.CELL_TYPE_STRING:
return new StringEval(cell.getRichStringCellValue().getString()); return new StringEval(cell.getRichStringCellValue().getString());
case Cell.CELL_TYPE_FORMULA: case Cell.CELL_TYPE_FORMULA:
return internalEvaluate(cell, sheet, workbook); return internalEvaluate(cell, sheet);
case Cell.CELL_TYPE_BOOLEAN: case Cell.CELL_TYPE_BOOLEAN:
return BoolEval.valueOf(cell.getBooleanCellValue()); return BoolEval.valueOf(cell.getBooleanCellValue());
case Cell.CELL_TYPE_BLANK: case Cell.CELL_TYPE_BLANK:

View File

@ -69,8 +69,9 @@ import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
*/ */
final class OperationEvaluatorFactory { final class OperationEvaluatorFactory {
private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class }; 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 _constructorsByPtgClass = initialiseConstructorsMap();
private static final Map _instancesByPtgClass = initialiseInstancesMap();
private OperationEvaluatorFactory() { private OperationEvaluatorFactory() {
// no instances of this class // no instances of this class
@ -78,32 +79,44 @@ final class OperationEvaluatorFactory {
private static Map initialiseConstructorsMap() { private static Map initialiseConstructorsMap() {
Map m = new HashMap(32); Map m = new HashMap(32);
add(m, AddPtg.class, AddEval.class);
add(m, ConcatPtg.class, ConcatEval.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, FuncPtg.class, FuncVarEval.class);
add(m, FuncVarPtg.class, FuncVarEval.class); add(m, FuncVarPtg.class, FuncVarEval.class);
add(m, GreaterEqualPtg.class, GreaterEqualEval.class); return m;
add(m, GreaterThanPtg.class, GreaterThanEval.class); }
add(m, LessEqualPtg.class, LessEqualEval.class); private static Map initialiseInstancesMap() {
add(m, LessThanPtg.class, LessThanEval.class); Map m = new HashMap(32);
add(m, MultiplyPtg.class, MultiplyEval.class); add(m, EqualPtg.class, EqualEval.instance);
add(m, NotEqualPtg.class, NotEqualEval.class); add(m, GreaterEqualPtg.class, GreaterEqualEval.instance);
add(m, PercentPtg.class, PercentEval.class); add(m, GreaterThanPtg.class, GreaterThanEval.instance);
add(m, PowerPtg.class, PowerEval.class); add(m, LessEqualPtg.class, LessEqualEval.instance);
add(m, SubtractPtg.class, SubtractEval.class); add(m, LessThanPtg.class, LessThanEval.instance);
add(m, UnaryMinusPtg.class, UnaryMinusEval.class); add(m, NotEqualPtg.class, NotEqualEval.instance);
add(m, UnaryPlusPtg.class, UnaryPlusEval.class);
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; 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) { private static void add(Map m, Class ptgClass, Class evalClass) {
// perform some validation now, to keep later exception handlers simple // perform some validation now, to keep later exception handlers simple
if(!Ptg.class.isAssignableFrom(ptgClass)) { if(!Ptg.class.isAssignableFrom(ptgClass)) {
throw new IllegalArgumentException("Expected Ptg subclass"); throw new IllegalArgumentException("Expected Ptg subclass");
} }
if(!OperationEval.class.isAssignableFrom(evalClass)) { if(!OperationEval.class.isAssignableFrom(evalClass)) {
throw new IllegalArgumentException("Expected OperationEval subclass"); throw new IllegalArgumentException("Expected OperationEval subclass");
} }
@ -125,7 +138,7 @@ final class OperationEvaluatorFactory {
} }
m.put(ptgClass, constructor); m.put(ptgClass, constructor);
} }
/** /**
* returns the OperationEval concrete impl instance corresponding * returns the OperationEval concrete impl instance corresponding
* to the supplied operationPtg * to the supplied operationPtg
@ -134,9 +147,16 @@ final class OperationEvaluatorFactory {
if(ptg == null) { if(ptg == null) {
throw new IllegalArgumentException("ptg must not be null"); throw new IllegalArgumentException("ptg must not be null");
} }
Object result;
Class ptgClass = ptg.getClass(); Class ptgClass = ptg.getClass();
result = _instancesByPtgClass.get(ptgClass);
if (result != null) {
return (OperationEval) result;
}
Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass); Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass);
if(constructor == null) { if(constructor == null) {
if(ptgClass == ExpPtg.class) { if(ptgClass == ExpPtg.class) {
@ -147,7 +167,6 @@ final class OperationEvaluatorFactory {
throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")"); throw new RuntimeException("Unexpected operation ptg class (" + ptgClass.getName() + ")");
} }
Object result;
Object[] initargs = { ptg }; Object[] initargs = { ptg };
try { try {
result = constructor.newInstance(initargs); result = constructor.newInstance(initargs);

View File

@ -46,29 +46,59 @@ public class AreaReference {
} }
String[] parts = separateAreaRefs(reference); String[] parts = separateAreaRefs(reference);
String part0 = parts[0];
// Special handling for whole-column references if (parts.length == 1) {
if(parts.length == 2 && parts[0].length() == 1 && // TODO - probably shouldn't initialize area ref when text is really a cell ref
parts[1].length() == 1 && // Need to fix some named range stuff to get rid of this
parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' && _firstCell = new CellReference(part0);
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 {
_lastCell = _firstCell; _lastCell = _firstCell;
_isSingleCell = true; _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. * Creates an area ref from a pair of Cell References.
*/ */

View File

@ -80,7 +80,7 @@ public class CellReference {
if (_isColAbs) { if (_isColAbs) {
colRef=colRef.substring(1); colRef=colRef.substring(1);
} }
_colIndex = convertColStringToNum(colRef); _colIndex = convertColStringToIndex(colRef);
String rowRef=parts[2]; String rowRef=parts[2];
if (rowRef.length() < 1) { if (rowRef.length() < 1) {
@ -94,7 +94,7 @@ public class CellReference {
} }
public CellReference(int pRow, int pCol) { public CellReference(int pRow, int pCol) {
this(pRow, pCol, false, false); this(pRow, pCol & 0xFFFF, false, false);
} }
public CellReference(int pRow, short pCol) { public CellReference(int pRow, short pCol) {
this(pRow, (int)pCol, false, false); this(pRow, (int)pCol, false, false);
@ -130,18 +130,31 @@ public class CellReference {
public String getSheetName(){ public String getSheetName(){
return _sheetName; 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 * takes in a column reference portion of a CellRef and converts it from
* ALPHA-26 number format to 0-based base 10. * 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) { protected static int convertColStringToIndex(String ref) {
int lastIx = ref.length()-1; int pos = 0;
int retval=0; int retval=0;
int pos = 0; for (int k = ref.length()-1; k >= 0; k--) {
for (int k = lastIx; k > -1; k--) {
char thechar = ref.charAt(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 // Character.getNumericValue() returns the values
// 10-35 for the letter A-Z // 10-35 for the letter A-Z
int shift = (int)Math.pow(26, pos); 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 * This will normally return an array of size 2 or 3
*/ */
public SlideListWithText[] getSlideListWithTexts() { return slwts; } public SlideListWithText[] getSlideListWithTexts() { return slwts; }
/**
/**
* Returns the SlideListWithText that deals with the * Returns the SlideListWithText that deals with the
* Master Slides * Master Slides
*/ */
public SlideListWithText getMasterSlideListWithText() { public SlideListWithText getMasterSlideListWithText() {
if(slwts.length > 0) { return slwts[0]; } for (int i = 0; i < slwts.length; i++) {
return null; } if(slwts[i].getInstance() == SlideListWithText.MASTER) {
return slwts[i];
}
}
return null;
}
/** /**
* Returns the SlideListWithText that deals with the * Returns the SlideListWithText that deals with the
* Slides, or null if there isn't one * Slides, or null if there isn't one
*/ */
public SlideListWithText getSlideSlideListWithText() { public SlideListWithText getSlideSlideListWithText() {
if(slwts.length > 1) { return slwts[1]; } for (int i = 0; i < slwts.length; i++) {
return null; } if(slwts[i].getInstance() == SlideListWithText.SLIDES) {
return slwts[i];
}
}
return null;
}
/** /**
* Returns the SlideListWithText that deals with the * Returns the SlideListWithText that deals with the
* notes, or null if there isn't one * notes, or null if there isn't one
*/ */
public SlideListWithText getNotesSlideListWithText() { public SlideListWithText getNotesSlideListWithText() {
if(slwts.length > 2) { return slwts[2]; } for (int i = 0; i < slwts.length; i++) {
return null; } 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); moveChildRecords(oldLoc, newLoc, number);
} }
} }
/**
* Set child records.
*
* @param records the new child records
*/
public void setChildRecord(Record[] records) {
this._children = records;
}
/* =============================================================== /* ===============================================================
* External Serialisation Methods * External Serialisation Methods
* =============================================================== * ===============================================================

View File

@ -50,7 +50,24 @@ import java.util.Vector;
// For now, pretend to be an atom // For now, pretend to be an atom
public class SlideListWithText extends RecordContainer 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 static long _type = 4080;
private SlideAtomsSet[] slideAtomsSets; private SlideAtomsSet[] slideAtomsSets;
@ -123,9 +140,9 @@ public class SlideListWithText extends RecordContainer
public void addSlidePersistAtom(SlidePersistAtom spa) { public void addSlidePersistAtom(SlidePersistAtom spa) {
// Add the new SlidePersistAtom at the end // Add the new SlidePersistAtom at the end
appendChildRecord(spa); appendChildRecord(spa);
SlideAtomsSet newSAS = new SlideAtomsSet(spa, new Record[0]); SlideAtomsSet newSAS = new SlideAtomsSet(spa, new Record[0]);
// Update our SlideAtomsSets with this // Update our SlideAtomsSets with this
SlideAtomsSet[] sas = new SlideAtomsSet[slideAtomsSets.length+1]; SlideAtomsSet[] sas = new SlideAtomsSet[slideAtomsSets.length+1];
System.arraycopy(slideAtomsSets, 0, sas, 0, slideAtomsSets.length); System.arraycopy(slideAtomsSets, 0, sas, 0, slideAtomsSets.length);
@ -133,7 +150,15 @@ public class SlideListWithText extends RecordContainer
slideAtomsSets = sas; 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 * Get access to the SlideAtomsSets of the children of this record
*/ */
public SlideAtomsSet[] getSlideAtomsSets() { return slideAtomsSets; } 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 * Inner class to wrap up a matching set of records that hold the
* text for a given sheet. Contains the leading SlidePersistAtom, * text for a given sheet. Contains the leading SlidePersistAtom,
* and all of the records until the next SlidePersistAtom. This * 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. * 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) * @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 // 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"); 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 + ")"); throw new IllegalArgumentException("Old and new slide numbers must not exceed the number of slides (" + _slides.length + ")");
} }
// Shift the SlideAtomsSet // The order of slides is defined by the order of slide atom sets in the SlideListWithText container.
SlideListWithText slwt = _documentRecord.getSlideSlideListWithText(); SlideListWithText slwt = _documentRecord.getSlideSlideListWithText();
slwt.repositionSlideAtomsSet( SlideAtomsSet[] sas = slwt.getSlideAtomsSets();
slwt.getSlideAtomsSets()[(oldSlideNumer-1)],
(newSlideNumber-1) SlideAtomsSet tmp = sas[oldSlideNumber-1];
); sas[oldSlideNumber-1] = sas[newSlideNumber-1];
sas[newSlideNumber-1] = tmp;
// Re-order the slides
ArrayUtil.arrayMoveWithin(_slides, (oldSlideNumer-1), (newSlideNumber-1), 1); ArrayList lst = new ArrayList();
for (int i = 0; i < sas.length; i++) {
// Tell the appropriate slides their new numbers lst.add(sas[i].getSlidePersistAtom());
for(int i=0; i<_slides.length; i++) { Record[] r = sas[i].getSlideRecords();
_slides[i].setSlideNumber( (i+1) ); 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) { if(slist == null) {
// Need to add a new one // Need to add a new one
slist = new SlideListWithText(); slist = new SlideListWithText();
_documentRecord.addSlideListWithText(slist); slist.setInstance(SlideListWithText.SLIDES);
_documentRecord.addSlideListWithText(slist);
} }
// Grab the SlidePersistAtom with the highest Slide Number. // Grab the SlidePersistAtom with the highest Slide Number.
@ -678,11 +684,11 @@ public final class SlideShow {
ptr.addSlideLookup(sp.getRefID(), slideOffset); ptr.addSlideLookup(sp.getRefID(), slideOffset);
logger.log(POILogger.INFO, "New slide ended up at " + 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; return slide;
} }
/** /**
* Adds a picture to this presentation and returns the associated index. * 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); assertEquals(3, ss_read.getSlides().length);
// And check it's as expected // And check it's as expected
s1 = ss_read.getSlides()[0]; Slide _s1 = ss_read.getSlides()[0];
s2 = ss_read.getSlides()[1]; Slide _s2 = ss_read.getSlides()[1];
s3 = ss_read.getSlides()[2]; Slide _s3 = ss_read.getSlides()[2];
assertEquals(257, s1._getSheetNumber()); // 1 --> 3
assertEquals(4, s1._getSheetRefId()); assertEquals(s1._getSheetNumber(), _s3._getSheetNumber());
assertEquals(s1._getSheetRefId(), _s3._getSheetRefId());
assertEquals(1, s1.getSlideNumber()); 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(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()); assertEquals(3, s3.getSlideNumber());
} }
} }

Binary file not shown.

View File

@ -74,9 +74,18 @@ public final class TestExternalFunctionFormulas extends TestCase {
public void testEvaluate() { public void testEvaluate() {
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls"); HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls");
HSSFSheet sheet = wb.getSheetAt(0); HSSFSheet sheet = wb.getSheetAt(0);
HSSFCell cell = sheet.getRow(0).getCell(0);
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb); HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
CellValue evalResult = fe.evaluate(cell); confirmCellEval(sheet, 0, 0, fe, "YEARFRAC(B1,C1)", 29.0/90.0);
evalResult.toString(); 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, 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, 5), md(1999, 4, 8), 1, 0.008219178);
confirm(md(1999, 4, 4), md(1999, 4, 7), 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) { 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()); TestSuite result = new TestSuite(AllFormulaEvalTests.class.getName());
result.addTestSuite(TestAreaEval.class); result.addTestSuite(TestAreaEval.class);
result.addTestSuite(TestCircularReferences.class); result.addTestSuite(TestCircularReferences.class);
result.addTestSuite(TestDivideEval.class);
result.addTestSuite(TestEqualEval.class);
result.addTestSuite(TestExternalFunction.class); result.addTestSuite(TestExternalFunction.class);
result.addTestSuite(TestFormulaBugs.class); result.addTestSuite(TestFormulaBugs.class);
result.addTestSuite(TestFormulasFromSpreadsheet.class); result.addTestSuite(TestFormulasFromSpreadsheet.class);

View File

@ -93,7 +93,6 @@ public final class TestCircularReferences extends TestCase {
HSSFCell testCell = row.createCell(0); HSSFCell testCell = row.createCell(0);
testCell.setCellFormula("A1"); testCell.setCellFormula("A1");
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
CellValue cellValue = evaluateWithCycles(wb, sheet, testCell); CellValue cellValue = evaluateWithCycles(wb, sheet, testCell);
confirmCycleErrorCode(cellValue); confirmCycleErrorCode(cellValue);
@ -114,7 +113,6 @@ public final class TestCircularReferences extends TestCase {
HSSFCell testCell = row.createCell(3); HSSFCell testCell = row.createCell(3);
testCell.setCellFormula("A1"); testCell.setCellFormula("A1");
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
CellValue cellValue = evaluateWithCycles(wb, sheet, testCell); CellValue cellValue = evaluateWithCycles(wb, sheet, testCell);
confirmCycleErrorCode(cellValue); 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.AssertionFailedError;
import junit.framework.TestCase; 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.record.formula.functions.NumericFunctionInvoker;
import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
@ -41,8 +41,8 @@ public final class TestPercentEval extends TestCase {
arg, arg,
}; };
PercentEval opEval = new PercentEval(PercentPtg.instance); OperationEval opEval = PercentEval.instance;
double result = NumericFunctionInvoker.invoke(opEval, args, -1, (short)-1); double result = NumericFunctionInvoker.invoke(opEval, args, 0, 0);
assertEquals(expectedResult, result, 0); assertEquals(expectedResult, result, 0);
} }
@ -55,6 +55,10 @@ public final class TestPercentEval extends TestCase {
confirm(BoolEval.TRUE, 0.01); 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() { public void testInSpreadSheet() {
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1"); HSSFSheet sheet = wb.createSheet("Sheet1");

View File

@ -1,27 +1,25 @@
/* /* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0 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 not use this file except in compliance with
* the License. You may obtain a copy of the License at 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.
*/
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; package org.apache.poi.hssf.record.formula.eval;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AreaPtg; 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.EvalFactory;
import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker; import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker;
@ -53,7 +51,7 @@ public final class TestUnaryPlusEval extends TestCase {
EvalFactory.createAreaEval(areaPtg, values), 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); assertEquals(35, result, 0);
} }

View File

@ -36,6 +36,7 @@ public final class AllIndividualFunctionEvaluationTests {
result.addTestSuite(TestIndex.class); result.addTestSuite(TestIndex.class);
result.addTestSuite(TestIsBlank.class); result.addTestSuite(TestIsBlank.class);
result.addTestSuite(TestLen.class); result.addTestSuite(TestLen.class);
result.addTestSuite(TestLookupFunctionsFromSpreadsheet.class);
result.addTestSuite(TestMid.class); result.addTestSuite(TestMid.class);
result.addTestSuite(TestMathX.class); result.addTestSuite(TestMathX.class);
result.addTestSuite(TestMatch.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) * @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
*/ */
public final class TestDate extends TestCase { public final class TestDate extends TestCase {
private HSSFCell cell11; private HSSFCell cell11;
private HSSFFormulaEvaluator evaluator; private HSSFFormulaEvaluator evaluator;
public void setUp() { public void setUp() {
HSSFWorkbook wb = new HSSFWorkbook(); HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet"); HSSFSheet sheet = wb.createSheet("new sheet");
cell11 = sheet.createRow(0).createCell(0); cell11 = sheet.createRow(0).createCell(0);
cell11.setCellType(HSSFCell.CELL_TYPE_FORMULA); cell11.setCellType(HSSFCell.CELL_TYPE_FORMULA);
evaluator = new HSSFFormulaEvaluator(sheet, wb); evaluator = new HSSFFormulaEvaluator(sheet, wb);
} }
/** /**
* Test disabled pending a fix in the formula evaluator * Test disabled pending a fix in the formula evaluator
* TODO - create MissingArgEval and modify the formula evaluator to handle this * TODO - create MissingArgEval and modify the formula evaluator to handle this
*/ */
public void DISABLEDtestSomeArgumentsMissing() { public void DISABLEDtestSomeArgumentsMissing() {
confirm("DATE(, 1, 0)", 0.0); confirm("DATE(, 1, 0)", 0.0);
confirm("DATE(, 1, 1)", 1.0); confirm("DATE(, 1, 1)", 1.0);
} }
public void testValid() { public void testValid() {
confirm("DATE(1900, 1, 1)", 1); confirm("DATE(1900, 1, 1)", 1);
confirm("DATE(1900, 1, 32)", 32); confirm("DATE(1900, 1, 32)", 32);
confirm("DATE(1900, 222, 1)", 6727); 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(2000, 1, 222)", 36747.00);
confirm("DATE(2007, 1, 1)", 39083); confirm("DATE(2007, 1, 1)", 39083);
} }
public void testBugDate() { public void testBugDate() {
confirm("DATE(1900, 2, 29)", 60); confirm("DATE(1900, 2, 29)", 60);
confirm("DATE(1900, 2, 30)", 61); 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, 2222)", 2222);
confirm("DATE(1900, 1, 22222)", 22222); confirm("DATE(1900, 1, 22222)", 22222);
} }
public void testPartYears() { public void testPartYears() {
confirm("DATE(4, 1, 1)", 1462.00); confirm("DATE(4, 1, 1)", 1462.00);
confirm("DATE(14, 1, 1)", 5115.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); confirm("DATE(1004, 1, 1)", 366705.00);
} }
private void confirm(String formulaText, double expectedResult) { private void confirm(String formulaText, double expectedResult) {
cell11.setCellFormula(formulaText); cell11.setCellFormula(formulaText);
evaluator.clearCache();
double actualValue = evaluator.evaluate(cell11).getNumberValue(); 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(TestHSSFConditionalFormatting.class);
result.addTestSuite(TestHSSFDataFormatter.class); result.addTestSuite(TestHSSFDataFormatter.class);
result.addTestSuite(TestHSSFDateUtil.class); result.addTestSuite(TestHSSFDateUtil.class);
result.addTestSuite(TestHSSFFormulaEvaluator.class);
result.addTestSuite(TestHSSFHeaderFooter.class); result.addTestSuite(TestHSSFHeaderFooter.class);
result.addTestSuite(TestHSSFHyperlink.class); result.addTestSuite(TestHSSFHyperlink.class);
result.addTestSuite(TestHSSFOptimiser.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.NameRecord;
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate; import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
import org.apache.poi.hssf.record.formula.DeletedArea3DPtg; import org.apache.poi.hssf.record.formula.DeletedArea3DPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.util.TempFile; import org.apache.poi.util.TempFile;
/** /**
@ -1020,9 +1021,9 @@ public final class TestBugs extends TestCase {
NameRecord r = w.getNameRecord(i); NameRecord r = w.getNameRecord(i);
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
List nd = r.getNameDefinition(); Ptg[] nd = r.getNameDefinition();
assertEquals(1, nd.size()); assertEquals(1, nd.length);
assertTrue(nd.get(0) instanceof DeletedArea3DPtg); assertTrue(nd[0] instanceof DeletedArea3DPtg);
} }
@ -1038,9 +1039,9 @@ public final class TestBugs extends TestCase {
NameRecord r = w.getNameRecord(i); NameRecord r = w.getNameRecord(i);
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
List nd = r.getNameDefinition(); Ptg[] nd = r.getNameDefinition();
assertEquals(1, nd.size()); assertEquals(1, nd.length);
assertTrue(nd.get(0) instanceof DeletedArea3DPtg); assertTrue(nd[0] instanceof DeletedArea3DPtg);
} }
@ -1055,9 +1056,9 @@ public final class TestBugs extends TestCase {
NameRecord r = w.getNameRecord(i); NameRecord r = w.getNameRecord(i);
assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets()); assertTrue(r.getSheetNumber() <= wb.getNumberOfSheets());
List nd = r.getNameDefinition(); Ptg[] nd = r.getNameDefinition();
assertEquals(1, nd.size()); assertEquals(1, nd.length);
assertTrue(nd.get(0) instanceof DeletedArea3DPtg); assertTrue(nd[0] instanceof DeletedArea3DPtg);
} }
} }

View File

@ -19,6 +19,8 @@ package org.apache.poi.hssf.usermodel;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator; import java.util.Iterator;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
@ -35,6 +37,7 @@ import org.apache.poi.hssf.record.formula.Ptg;
*/ */
public final class TestFormulaEvaluatorBugs extends TestCase { public final class TestFormulaEvaluatorBugs extends TestCase {
private static final boolean OUTPUT_TEST_FILES = false;
private String tmpDirName; private String tmpDirName;
protected void setUp() { protected void setUp() {
@ -65,13 +68,15 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
assertEquals(4.2 * 25, row.getCell(3).getNumericCellValue(), 0.0001); assertEquals(4.2 * 25, row.getCell(3).getNumericCellValue(), 0.0001);
// Save FileOutputStream out;
File existing = new File(tmpDirName, "44636-existing.xls"); if (OUTPUT_TEST_FILES) {
FileOutputStream out = new FileOutputStream(existing); // Save
wb.write(out); File existing = new File(tmpDirName, "44636-existing.xls");
out.close(); out = new FileOutputStream(existing);
System.err.println("Existing file for bug #44636 written to " + existing.toString()); wb.write(out);
out.close();
System.err.println("Existing file for bug #44636 written to " + existing.toString());
}
// Now, do a new file from scratch // Now, do a new file from scratch
wb = new HSSFWorkbook(); wb = new HSSFWorkbook();
sheet = wb.createSheet(); sheet = wb.createSheet();
@ -86,12 +91,14 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); HSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
assertEquals(5.4, row.getCell(0).getNumericCellValue(), 0.0001); assertEquals(5.4, row.getCell(0).getNumericCellValue(), 0.0001);
// Save if (OUTPUT_TEST_FILES) {
File scratch = new File(tmpDirName, "44636-scratch.xls"); // Save
out = new FileOutputStream(scratch); File scratch = new File(tmpDirName, "44636-scratch.xls");
wb.write(out); out = new FileOutputStream(scratch);
out.close(); wb.write(out);
System.err.println("New file for bug #44636 written to " + scratch.toString()); 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 * The HSSFFormula evaluator performance benefits greatly from caching of intermediate cell values
* odd.
* We think it's because the formulas are recursive and crazy
*/ */
public void DISABLEDtestSlowEvaluate45376() throws Exception { public void testSlowEvaluate45376() {
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("45376.xls");
final String SHEET_NAME = "Eingabe"; // Firstly set up a sequence of formula cells where each depends on the previous multiple
final int row = 6; // times. Without caching, each subsequent cell take about 4 times longer to evaluate.
final HSSFSheet sheet = wb.getSheet(SHEET_NAME); HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("Sheet1");
int firstCol = 4; HSSFRow row = sheet.createRow(0);
int lastCol = 14; for(int i=1; i<10; i++) {
long[] timings = new long[lastCol-firstCol+1]; HSSFCell cell = row.createCell(i);
long[] rtimings = new long[lastCol-firstCol+1]; char prevCol = (char) ('A' + i-1);
String prevCell = prevCol + "1";
long then, now; // 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," +
final HSSFRow excelRow = sheet.getRow(row); "DATE(YEAR(" + prevCell + "),MONTH(" + prevCell + ")+1,1),NA())";
for(int i = firstCol; i <= lastCol; i++) { cell.setCellFormula(formula);
final HSSFCell excelCell = excelRow.getCell(i);
final HSSFFormulaEvaluator evaluator = new }
HSSFFormulaEvaluator(sheet, wb); Calendar cal = new GregorianCalendar(2000, 0, 1, 0, 0, 0);
row.createCell(0).setCellValue(cal);
now = System.currentTimeMillis();
evaluator.evaluate(excelCell); // Choose cell A9, so that the failing test case doesn't take too long to execute.
then = System.currentTimeMillis(); HSSFCell cell = row.getCell(8);
timings[i-firstCol] = (then-now); HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(sheet, wb);
System.err.println("Col " + i + " took " + (then-now) + "ms"); evaluator.evaluate(cell);
} int evalCount = evaluator.getEvaluationCount();
for(int i = lastCol; i >= firstCol; i--) { // With caching, the evaluationCount is 8 which is a big improvement
final HSSFCell excelCell = excelRow.getCell(i); if (evalCount > 10) {
final HSSFFormulaEvaluator evaluator = new // Without caching, evaluating cell 'A9' takes 21845 evaluations which consumes
HSSFFormulaEvaluator(sheet, wb); // much time (~3 sec on Core 2 Duo 2.2GHz)
System.err.println("Cell A9 took " + evalCount + " intermediate evaluations");
now = System.currentTimeMillis(); throw new AssertionFailedError("Identifed bug 45376 - Formula evaluator should cache values");
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"
);
}
}
} }
} }

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("On2", nr.getNameText());
assertEquals(0, nr.getSheetNumber()); assertEquals(0, nr.getSheetNumber());
assertEquals(1, nr.getExternSheetNumber()); 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(1, ptg.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn()); assertEquals(0, ptg.getFirstColumn());
assertEquals(0, ptg.getFirstRow()); assertEquals(0, ptg.getFirstRow());
@ -452,9 +452,9 @@ public final class TestHSSFWorkbook extends TestCase {
assertEquals("OnOne", nr.getNameText()); assertEquals("OnOne", nr.getNameText());
assertEquals(0, nr.getSheetNumber()); assertEquals(0, nr.getSheetNumber());
assertEquals(0, nr.getExternSheetNumber()); 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.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn()); assertEquals(0, ptg.getFirstColumn());
assertEquals(2, ptg.getFirstRow()); assertEquals(2, ptg.getFirstRow());
@ -475,9 +475,9 @@ public final class TestHSSFWorkbook extends TestCase {
assertEquals("OnSheet3", nr.getNameText()); assertEquals("OnSheet3", nr.getNameText());
assertEquals(0, nr.getSheetNumber()); assertEquals(0, nr.getSheetNumber());
assertEquals(2, nr.getExternSheetNumber()); 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(2, ptg.getExternSheetIndex());
assertEquals(0, ptg.getFirstColumn()); assertEquals(0, ptg.getFirstColumn());
assertEquals(0, ptg.getFirstRow()); assertEquals(0, ptg.getFirstRow());

View File

@ -19,7 +19,6 @@ package org.apache.poi.hssf.util;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List;
import junit.framework.TestCase; 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.NameRecord;
import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.MemFuncPtg; 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.record.formula.UnionPtg;
import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFName; import org.apache.poi.hssf.usermodel.HSSFName;
@ -85,7 +85,7 @@ public final class TestAreaReference extends TestCase {
public void testReferenceWithSheet() { public void testReferenceWithSheet() {
AreaReference ar; AreaReference ar;
ar = new AreaReference("Tabelle1!B5"); ar = new AreaReference("Tabelle1!B5:B5");
assertTrue(ar.isSingleCell()); assertTrue(ar.isSingleCell());
TestCellReference.confirmCell(ar.getFirstCell(), "Tabelle1", 4, 1, false, false, "Tabelle1!B5"); 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 { public void testContiguousReferences() {
String refSimple = "$C$10"; String refSimple = "$C$10:$C$10";
String ref2D = "$C$10:$D$11"; String ref2D = "$C$10:$D$11";
String refDCSimple = "$C$10,$D$12,$E$14"; String refDCSimple = "$C$10:$C$10,$D$12:$D$12,$E$14:$E$14";
String refDC2D = "$C$10:$C$11,$D$12,$E$14:$E$20"; 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"; String refDC3D = "Tabelle1!$C$10:$C$14,Tabelle1!$D$10:$D$12";
// Check that we detect as contiguous properly // Check that we detect as contiguous properly
@ -206,13 +206,13 @@ public final class TestAreaReference extends TestCase {
assertNotNull(nr); assertNotNull(nr);
assertEquals("test", nr.getNameText()); assertEquals("test", nr.getNameText());
List def =nr.getNameDefinition(); Ptg[] def =nr.getNameDefinition();
assertEquals(4, def.size()); assertEquals(4, def.length);
MemFuncPtg ptgA = (MemFuncPtg)def.get(0); MemFuncPtg ptgA = (MemFuncPtg)def[0];
Area3DPtg ptgB = (Area3DPtg)def.get(1); Area3DPtg ptgB = (Area3DPtg)def[1];
Area3DPtg ptgC = (Area3DPtg)def.get(2); Area3DPtg ptgC = (Area3DPtg)def[2];
UnionPtg ptgD = (UnionPtg)def.get(3); UnionPtg ptgD = (UnionPtg)def[3];
assertEquals("", ptgA.toFormulaString(wb)); assertEquals("", ptgA.toFormulaString(wb));
assertEquals(refA, ptgB.toFormulaString(wb)); assertEquals(refA, ptgB.toFormulaString(wb));
assertEquals(refB, ptgC.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) { private static void confirmResolveCellRef(HSSFWorkbook wb, CellReference cref) {
HSSFSheet s = wb.getSheet(cref.getSheetName()); HSSFSheet s = wb.getSheet(cref.getSheetName());
HSSFRow r = s.getRow(cref.getRow()); HSSFRow r = s.getRow(cref.getRow());
HSSFCell c = r.getCell(cref.getCol()); HSSFCell c = r.getCell((int)cref.getCol());
assertNotNull(c); assertNotNull(c);
} }
public void testSpecialSheetNames() { public void testSpecialSheetNames() {
AreaReference ar; AreaReference ar;
ar = new AreaReference("'Sheet A'!A1"); ar = new AreaReference("'Sheet A'!A1:A1");
confirmAreaSheetName(ar, "Sheet A", "'Sheet A'!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"); confirmAreaSheetName(ar, "Hey! Look Here!", "'Hey! Look Here!'!A1");
ar = new AreaReference("'O''Toole'!A1:B2"); ar = new AreaReference("'O''Toole'!A1:B2");
@ -270,7 +270,24 @@ public final class TestAreaReference extends TestCase {
assertEquals(expectedFullText, ar.formatAsString()); assertEquals(expectedFullText, ar.formatAsString());
} }
public static void main(String[] args) { public void testWholeColumnRefs() {
junit.textui.TestRunner.run(TestAreaReference.class); 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());
}
} }