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-691382 via svnmerge from

https://svn.apache.org/repos/asf/poi/trunk

........
  r690739 | nick | 2008-08-31 18:24:10 +0100 (Sun, 31 Aug 2008) | 1 line
  
  HPBF docs update
........
  r690761 | josh | 2008-08-31 20:08:36 +0100 (Sun, 31 Aug 2008) | 1 line
  
  removed AreaEval.getValues (initial work for bug 45358)
........
  r690772 | josh | 2008-08-31 20:44:11 +0100 (Sun, 31 Aug 2008) | 1 line
  
  Partial fix for bug 45358 - parsing area refs with rows above 32767
........
  r690825 | josh | 2008-09-01 00:59:26 +0100 (Mon, 01 Sep 2008) | 1 line
  
  Improving AreaI interface and AreaPtg hierarchy
........
  r690835 | josh | 2008-09-01 02:48:45 +0100 (Mon, 01 Sep 2008) | 1 line
  
  Converted AreEval and RefEval to be lazy (part of fix for bug 45358)
........
  r690836 | josh | 2008-09-01 03:26:33 +0100 (Mon, 01 Sep 2008) | 1 line
  
  Fixed IF() to handle different types for the first arg
........
  r690837 | josh | 2008-09-01 03:34:05 +0100 (Mon, 01 Sep 2008) | 1 line
  
  Update status for bug 45358 (fixed with r690772, r690835 and r690836)
........
  r691017 | nick | 2008-09-01 17:51:09 +0100 (Mon, 01 Sep 2008) | 1 line
  
  DV related fixes from Pierre Lavignotte
........
  r691180 | yegor | 2008-09-02 10:59:53 +0100 (Tue, 02 Sep 2008) | 1 line
  
  improved handling of StyleTextPropAtom bit masks, added more read-write roundtrip tests
........
  r691182 | yegor | 2008-09-02 11:03:11 +0100 (Tue, 02 Sep 2008) | 1 line
  
  continue making progress with hslf hyperlinks
........


git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@692907 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2008-09-07 17:34:36 +00:00
parent 6c3a086277
commit 2a02163d66
68 changed files with 2313 additions and 2541 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="add">Initial support for embedded movies and controls in HSLF</action>
<action dev="POI-DEVELOPERS" type="fix">45358 - signed/unsigned error when parsing 3-d area refs, performance problem evaluating area refs, and ClassCastExcecption in IF()</action>
<action dev="POI-DEVELOPERS" type="add">Support for HPBF Publisher hyperlinks, including during text extraction</action> <action dev="POI-DEVELOPERS" type="add">Support for HPBF Publisher hyperlinks, including during text extraction</action>
<action dev="POI-DEVELOPERS" type="fix">26321 and 44958 - preserve position of ArrayRecords and TableRecords among cell value records</action> <action dev="POI-DEVELOPERS" type="fix">26321 and 44958 - preserve position of ArrayRecords and TableRecords among cell value records</action>
<action dev="POI-DEVELOPERS" type="fix">Impove empty header or footer handling in HWPF HeaderStories</action> <action dev="POI-DEVELOPERS" type="fix">Impove empty header or footer handling in HWPF HeaderStories</action>

View File

@ -171,6 +171,27 @@ PL 62 1a 00 00 48 00 00 00 // PL from: 1a62 (6754), len: 48 (72)
think that the second 4 bytes of text describes the format think that the second 4 bytes of text describes the format
of data block at the offset. The format of the text block of data block at the offset. The format of the text block
is easy, but we're still trying to figure out the others.</p> is easy, but we're still trying to figure out the others.</p>
<section><title>Structure of TEXT bit</title>
<p>This is very simple. All the text for the document is
stored in a single bit of the Quill CONTENTS. The text
is stored as little endian 16 bit unicode strings.</p>
</section>
<section><title>Structure of PLC bit</title>
<p>The first four bytes seem to hold the count of the
entries in the bit, and the second four bytes seem to hold
the type. There is then some pre-data, and then data for
each of the entries, the exact format dependant on the type.</p>
<p>Type 0 has 4 2 byte unsigned ints, then a pair of 2 byte
unsigned ints for each entry.</p>
<p>Type 4 has 4 2 byte unsigned ints, then a pair of 4 byte
unsigned ints for each entry.</p>
<p>Type 8 has 7 2 byte unsigned ints, then a pair of 4 byte
unsigned ints for each entry.</p>
<p>Type 12 holds hyperlinks, and is very much more complex.
See <code>org.apache.poi.hpbf.model.qcbits.QCPLCBit</code>
for our best guess as to how the contents match up.</p>
</section>
</section> </section>
</body> </body>
</document> </document>

View File

@ -41,7 +41,7 @@
lots of offsets to other parts of the file.</p> lots of offsets to other parts of the file.</p>
<p>Our initial aim is to provude a text extractor for the format <p>Our initial aim is to provude a text extractor for the format
(now done), and be able to extract hyperlinks from within (now done), and be able to extract hyperlinks from within
the document (not yet supported). Additional low level the document (partly supported). Additional low level
code to process the file format may follow, if there code to process the file format may follow, if there
is demand and developer interest warrant it.</p> is demand and developer interest warrant it.</p>
<p>At this time, there is no <em>usermodel</em> api or similar. <p>At this time, there is no <em>usermodel</em> api or similar.

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="add">Initial support for embedded movies and controls in HSLF</action>
<action dev="POI-DEVELOPERS" type="fix">45358 - signed/unsigned error when parsing 3-d area refs, performance problem evaluating area refs, and ClassCastExcecption in IF()</action>
<action dev="POI-DEVELOPERS" type="add">Support for HPBF Publisher hyperlinks, including during text extraction</action> <action dev="POI-DEVELOPERS" type="add">Support for HPBF Publisher hyperlinks, including during text extraction</action>
<action dev="POI-DEVELOPERS" type="fix">26321 and 44958 - preserve position of ArrayRecords and TableRecords among cell value records</action> <action dev="POI-DEVELOPERS" type="fix">26321 and 44958 - preserve position of ArrayRecords and TableRecords among cell value records</action>
<action dev="POI-DEVELOPERS" type="fix">Impove empty header or footer handling in HWPF HeaderStories</action> <action dev="POI-DEVELOPERS" type="fix">Impove empty header or footer handling in HWPF HeaderStories</action>

View File

@ -27,8 +27,8 @@ import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.Ref3DPtg; 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.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.AreaReference;
import org.apache.poi.hssf.util.RangeAddress; import org.apache.poi.hssf.util.RangeAddress;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.util.HexDump; import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil; import org.apache.poi.util.StringUtil;
@ -626,7 +626,7 @@ public final class NameRecord extends Record {
} }
private Ptg createNewPtg(){ private Ptg createNewPtg(){
Ptg ptg = new Area3DPtg(); Ptg ptg = new Area3DPtg("A1", 0); // TODO - change to not be partially initialised
field_13_name_definition.push(ptg); field_13_name_definition.push(ptg);
return ptg; return ptg;
@ -673,9 +673,7 @@ public final class NameRecord extends Record {
// 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(); ptg = new Area3DPtg(refs[i].formatAsString(), externSheetIndex);
((Area3DPtg) ptg).setExternSheetIndex(externSheetIndex);
((Area3DPtg) ptg).setArea(refs[i].formatAsString());
field_13_name_definition.push(ptg); field_13_name_definition.push(ptg);
this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) ); this.setDefinitionTextLength( (short)(getDefinitionLength() + ptg.getSize()) );
} }

View File

@ -0,0 +1,59 @@
/* ====================================================================
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;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.util.LittleEndian;
/**
* Common superclass of 2-D area refs
*/
public abstract class Area2DPtgBase extends AreaPtgBase {
private final static int SIZE = 9;
protected Area2DPtgBase(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) {
super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative);
}
protected Area2DPtgBase(RecordInputStream in) {
readCoordinates(in);
}
protected abstract byte getSid();
public final void writeBytes(byte [] array, int offset) {
LittleEndian.putByte(array, offset+0, getSid() + getPtgClass());
writeCoordinates(array, offset+1);
}
public Area2DPtgBase(String arearef) {
super(arearef);
}
public final int getSize() {
return SIZE;
}
public final String toFormulaString(HSSFWorkbook book) {
return formatReferenceAsString();
}
public final String toString() {
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName());
sb.append(" [");
sb.append(formatReferenceAsString());
sb.append("]");
return sb.toString();
}
}

View File

@ -18,11 +18,9 @@
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.hssf.util.AreaReference;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.BitField; import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.util.BitFieldFactory; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
/** /**
@ -34,238 +32,63 @@ import org.apache.poi.util.LittleEndian;
* @author Jason Height (jheight at chariot dot net dot au) * @author Jason Height (jheight at chariot dot net dot au)
* @version 1.0-pre * @version 1.0-pre
*/ */
public final class Area3DPtg extends OperandPtg implements AreaI { public final class Area3DPtg extends AreaPtgBase {
public final static byte sid = 0x3b; public final static byte sid = 0x3b;
private final static int SIZE = 11; // 10 + 1 for Ptg private final static int SIZE = 11; // 10 + 1 for Ptg
private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000); private int field_1_index_extern_sheet;
private static final BitField colRelative = BitFieldFactory.getInstance(0x4000);
private short field_1_index_extern_sheet;
private int field_2_first_row;
private int field_3_last_row;
private int field_4_first_column;
private int field_5_last_column;
/** Creates new AreaPtg */ public Area3DPtg( String arearef, int externIdx ) {
public Area3DPtg() super(arearef);
{
}
public Area3DPtg( String arearef, short externIdx )
{
setArea(arearef);
setExternSheetIndex( externIdx ); setExternSheetIndex( externIdx );
} }
public Area3DPtg(RecordInputStream in) public Area3DPtg(RecordInputStream in) {
{
field_1_index_extern_sheet = in.readShort(); field_1_index_extern_sheet = in.readShort();
field_2_first_row = in.readUShort(); readCoordinates(in);
field_3_last_row = in.readUShort();
field_4_first_column = in.readUShort();
field_5_last_column = in.readUShort();
} }
public Area3DPtg(short firstRow, short lastRow, short firstColumn, short lastColumn, public Area3DPtg(int firstRow, int lastRow, int firstColumn, int lastColumn,
boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative,
short externalSheetIndex) { int externalSheetIndex) {
setFirstRow(firstRow); super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative);
setLastRow(lastRow); setExternSheetIndex(externalSheetIndex);
setFirstColumn(firstColumn);
setLastColumn(lastColumn);
setFirstRowRelative(firstRowRelative);
setLastRowRelative(lastRowRelative);
setFirstColRelative(firstColRelative);
setLastColRelative(lastColRelative);
setExternSheetIndex(externalSheetIndex);
} }
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append(getClass().getName()); sb.append(getClass().getName());
sb.append(" [");
sb.append("sheetIx=").append(getExternSheetIndex()); sb.append("sheetIx=").append(getExternSheetIndex());
sb.append(" ! "); sb.append(" ! ");
sb.append(AreaReference.formatAsString(this)); sb.append(formatReferenceAsString());
sb.append("]"); sb.append("]");
return sb.toString(); return sb.toString();
} }
public void writeBytes( byte[] array, int offset ) public void writeBytes(byte[] array, int offset) {
{ LittleEndian.putByte(array, offset + 0, sid + getPtgClass());
array[0 + offset] = (byte) ( sid + getPtgClass() ); LittleEndian.putUShort(array, 1 + offset, field_1_index_extern_sheet);
LittleEndian.putShort( array, 1 + offset, getExternSheetIndex() ); writeCoordinates(array, offset+3);
LittleEndian.putShort( array, 3 + offset, (short)getFirstRow() );
LittleEndian.putShort( array, 5 + offset, (short)getLastRow() );
LittleEndian.putShort( array, 7 + offset, (short)getFirstColumnRaw() );
LittleEndian.putShort( array, 9 + offset, (short)getLastColumnRaw() );
} }
public int getSize() public int getSize() {
{
return SIZE; return SIZE;
} }
public short getExternSheetIndex() public short getExternSheetIndex() {
{ return (short)field_1_index_extern_sheet;
return 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;
} }
public int getFirstRow()
{
return field_2_first_row;
}
public void setFirstRow( int row )
{
field_2_first_row = row;
}
public int getLastRow()
{
return field_3_last_row;
}
public void setLastRow( int row )
{
field_3_last_row = row;
}
public int getFirstColumn()
{
return field_4_first_column & 0xFF;
}
public int getFirstColumnRaw()
{
return field_4_first_column;
}
public boolean isFirstRowRelative()
{
return rowRelative.isSet( field_4_first_column );
}
public boolean isFirstColRelative()
{
return colRelative.isSet( field_4_first_column );
}
public void setFirstColumn( short column )
{
field_4_first_column &= 0xFF00;
field_4_first_column |= column & 0xFF;
}
public void setFirstColumnRaw( short column )
{
field_4_first_column = column;
}
public int getLastColumn()
{
return field_5_last_column & 0xFF;
}
public int getLastColumnRaw()
{
return field_5_last_column;
}
public boolean isLastRowRelative()
{
return rowRelative.isSet( field_5_last_column );
}
public boolean isLastColRelative()
{
return colRelative.isSet( field_5_last_column );
}
public void setLastColumn( short column )
{
field_5_last_column &= 0xFF00;
field_5_last_column |= column & 0xFF;
}
public void setLastColumnRaw( short column )
{
field_5_last_column = column;
}
/**
* sets the first row to relative or not
* @param rel FIXME: Document this!
*/
public void setFirstRowRelative( boolean rel )
{
field_4_first_column = rowRelative.setBoolean( field_4_first_column, rel );
}
/**
* set whether the first column is relative
*/
public void setFirstColRelative( boolean rel )
{
field_4_first_column = colRelative.setBoolean( field_4_first_column, rel );
}
/**
* set whether the last row is relative or not
* @param rel FIXME: Document this!
*/
public void setLastRowRelative( boolean rel )
{
field_5_last_column = rowRelative.setBoolean( field_5_last_column, rel );
}
/**
* set whether the last column should be relative or not
*/
public void setLastColRelative( boolean rel )
{
field_5_last_column = colRelative.setBoolean( field_5_last_column, rel );
}
/*public String getArea(){
RangeAddress ra = new RangeAddress( getFirstColumn(),getFirstRow() + 1, getLastColumn(), getLastRow() + 1);
String result = ra.getAddress();
return result;
}*/
public void setArea( String ref )
{
AreaReference ar = new AreaReference( ref );
CellReference frstCell = ar.getFirstCell();
CellReference lastCell = ar.getLastCell();
setFirstRow( (short) frstCell.getRow() );
setFirstColumn( (short) frstCell.getCol() );
setLastRow( (short) lastCell.getRow() );
setLastColumn( (short) lastCell.getCol() );
setFirstColRelative( !frstCell.isColAbsolute() );
setLastColRelative( !lastCell.isColAbsolute() );
setFirstRowRelative( !frstCell.isRowAbsolute() );
setLastRowRelative( !lastCell.isRowAbsolute() );
}
/** /**
* @return text representation of this area reference that can be used in text * @return text representation of this area reference that can be used in text
* formulas. The sheet name will get properly delimited if required. * formulas. The sheet name will get properly delimited if required.
*/ */
public String toFormulaString(Workbook book) public String toFormulaString(Workbook book) {
{
// First do the sheet name // First do the sheet name
StringBuffer retval = new StringBuffer(); StringBuffer retval = new StringBuffer();
String sheetName = Ref3DPtg.getSheetName(book, field_1_index_extern_sheet); String sheetName = Ref3DPtg.getSheetName(book, field_1_index_extern_sheet);
@ -282,29 +105,8 @@ public final class Area3DPtg extends OperandPtg implements AreaI {
} }
// Now the normal area bit // Now the normal area bit
retval.append(AreaReference.formatAsString(this)); retval.append(formatReferenceAsString());
// All done
return retval.toString(); return retval.toString();
} }
public byte getDefaultOperandClass() {
return Ptg.CLASS_REF;
}
// TODO - one junit relies on this. remove
public boolean equals( Object o )
{
if ( this == o ) return true;
if ( !( o instanceof Area3DPtg ) ) return false;
final Area3DPtg area3DPtg = (Area3DPtg) o;
if ( field_1_index_extern_sheet != area3DPtg.field_1_index_extern_sheet ) return false;
if ( field_2_first_row != area3DPtg.field_2_first_row ) return false;
if ( field_3_last_row != area3DPtg.field_3_last_row ) return false;
if ( field_4_first_column != area3DPtg.field_4_first_column ) return false;
if ( field_5_last_column != area3DPtg.field_5_last_column ) return false;
return true;
}
} }

View File

@ -14,47 +14,63 @@
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; package org.apache.poi.hssf.record.formula;
/** /**
* Common interface for AreaPtg and Area3DPtg, and their * Common interface for AreaPtg and Area3DPtg, and their child classes.
* child classes.
*/ */
public interface AreaI { public interface AreaI {
/** /**
* @return the first row in the area * @return the first row in the area
*/ */
public int getFirstRow(); public int getFirstRow();
/**
* @return last row in the range (x2 in x1,y1-x2,y2)
*/
public int getLastRow();
/**
* @return the first column number in the area.
*/
public int getFirstColumn();
/**
* @return lastcolumn in the area
*/
public int getLastColumn();
class OffsetArea implements AreaI {
private final int _firstColumn;
private final int _firstRow;
private final int _lastColumn;
private final int _lastRow;
public OffsetArea(int baseRow, int baseColumn, int relFirstRowIx, int relLastRowIx,
int relFirstColIx, int relLastColIx) {
_firstRow = baseRow + relFirstRowIx;
_lastRow = baseRow + relLastRowIx;
_firstColumn = baseColumn + relFirstColIx;
_lastColumn = baseColumn + relLastColIx;
}
public int getFirstColumn() {
return _firstColumn;
}
public int getFirstRow() {
return _firstRow;
}
public int getLastColumn() {
return _lastColumn;
}
public int getLastRow() {
return _lastRow;
}
}
/**
* @return last row in the range (x2 in x1,y1-x2,y2)
*/
public int getLastRow();
/**
* @return the first column number in the area.
*/
public int getFirstColumn();
/**
* @return lastcolumn in the area
*/
public int getLastColumn();
/**
* @return isrelative first column to relative or not
*/
public boolean isFirstColRelative();
/**
* @return lastcol relative or not
*/
public boolean isLastColRelative();
/**
* @return whether or not the first row is a relative reference or not.
*/
public boolean isFirstRowRelative();
/**
* @return last row relative or not
*/
public boolean isLastRowRelative();
} }

View File

@ -23,7 +23,7 @@ import org.apache.poi.hssf.record.RecordInputStream;
* Specifies a rectangular area of cells A1:A4 for instance. * Specifies a rectangular area of cells A1:A4 for instance.
* @author Jason Height (jheight at chariot dot net dot au) * @author Jason Height (jheight at chariot dot net dot au)
*/ */
public final class AreaNPtg extends AreaPtgBase { public final class AreaNPtg extends Area2DPtgBase {
public final static short sid = 0x2D; public final static short sid = 0x2D;
public AreaNPtg(RecordInputStream in) { public AreaNPtg(RecordInputStream in) {

View File

@ -23,16 +23,16 @@ import org.apache.poi.hssf.record.RecordInputStream;
* Specifies a rectangular area of cells A1:A4 for instance. * Specifies a rectangular area of cells A1:A4 for instance.
* @author Jason Height (jheight at chariot dot net dot au) * @author Jason Height (jheight at chariot dot net dot au)
*/ */
public final class AreaPtg extends AreaPtgBase { public final class AreaPtg extends Area2DPtgBase {
public final static short sid = 0x25; public final static short sid = 0x25;
public AreaPtg(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) { public AreaPtg(int firstRow, int lastRow, int firstColumn, int lastColumn, boolean firstRowRelative, boolean lastRowRelative, boolean firstColRelative, boolean lastColRelative) {
super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative); super(firstRow, lastRow, firstColumn, lastColumn, firstRowRelative, lastRowRelative, firstColRelative, lastColRelative);
} }
public AreaPtg(RecordInputStream in) { public AreaPtg(RecordInputStream in) {
super(in); super(in);
} }
public AreaPtg(String arearef) { public AreaPtg(String arearef) {
super(arearef); super(arearef);
} }
protected byte getSid() { protected byte getSid() {

View File

@ -23,7 +23,7 @@ import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.hssf.util.AreaReference; import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.RecordInputStream;
/** /**
@ -37,11 +37,9 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI {
* see similar comment in ReferencePtg * see similar comment in ReferencePtg
*/ */
protected final RuntimeException notImplemented() { protected final RuntimeException notImplemented() {
return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted");
} }
public final static short sid = 0x25;
private final static int SIZE = 9;
/** zero based, unsigned 16 bit */ /** zero based, unsigned 16 bit */
private int field_1_first_row; private int field_1_first_row;
/** zero based, unsigned 16 bit */ /** zero based, unsigned 16 bit */
@ -55,6 +53,10 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI {
private final static BitField colRelative = BitFieldFactory.getInstance(0x4000); private final static BitField colRelative = BitFieldFactory.getInstance(0x4000);
private final static BitField columnMask = BitFieldFactory.getInstance(0x3FFF); private final static BitField columnMask = BitFieldFactory.getInstance(0x3FFF);
protected AreaPtgBase() {
// do nothing
}
protected AreaPtgBase(String arearef) { protected AreaPtgBase(String arearef) {
AreaReference ar = new AreaReference(arearef); AreaReference ar = new AreaReference(arearef);
CellReference firstCell = ar.getFirstCell(); CellReference firstCell = ar.getFirstCell();
@ -97,35 +99,17 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI {
} }
} }
protected AreaPtgBase(RecordInputStream in) protected final void readCoordinates(RecordInputStream in) {
{ field_1_first_row = in.readUShort();
field_1_first_row = in.readUShort(); field_2_last_row = in.readUShort();
field_2_last_row = in.readUShort();
field_3_first_column = in.readUShort(); field_3_first_column = in.readUShort();
field_4_last_column = in.readUShort(); field_4_last_column = in.readUShort();
} }
protected final void writeCoordinates(byte[] array, int offset) {
public final String toString() { LittleEndian.putUShort(array, offset + 0, field_1_first_row);
StringBuffer sb = new StringBuffer(); LittleEndian.putUShort(array, offset + 2, field_2_last_row);
sb.append(getClass().getName()); LittleEndian.putUShort(array, offset + 4, field_3_first_column);
sb.append(" ["); LittleEndian.putUShort(array, offset + 6, field_4_last_column);
sb.append(AreaReference.formatAsString(this));
sb.append("]");
return sb.toString();
}
protected abstract byte getSid();
public final void writeBytes(byte [] array, int offset) {
array[offset] = (byte) (getSid() + getPtgClass());
LittleEndian.putShort(array,offset+1,(short)field_1_first_row);
LittleEndian.putShort(array,offset+3,(short)field_2_last_row);
LittleEndian.putShort(array,offset+5,(short)field_3_first_column);
LittleEndian.putShort(array,offset+7,(short)field_4_last_column);
}
public final int getSize() {
return SIZE;
} }
/** /**
@ -207,7 +191,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI {
*/ */
public final void setFirstColumn(int colIx) { public final void setFirstColumn(int colIx) {
checkColumnBounds(colIx); checkColumnBounds(colIx);
field_3_first_column=columnMask.setValue(field_3_first_column, colIx); field_3_first_column=columnMask.setValue(field_3_first_column, colIx);
} }
/** /**
@ -266,7 +250,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI {
*/ */
public final void setLastColumn(int colIx) { public final void setLastColumn(int colIx) {
checkColumnBounds(colIx); checkColumnBounds(colIx);
field_4_last_column=columnMask.setValue(field_4_last_column, colIx); field_4_last_column=columnMask.setValue(field_4_last_column, colIx);
} }
/** /**
@ -275,9 +259,18 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI {
public final void setLastColumnRaw(short column) { public final void setLastColumnRaw(short column) {
field_4_last_column = column; field_4_last_column = column;
} }
protected final String formatReferenceAsString() {
CellReference topLeft = new CellReference(getFirstRow(),getFirstColumn(),!isFirstRowRelative(),!isFirstColRelative());
CellReference botRight = new CellReference(getLastRow(),getLastColumn(),!isLastRowRelative(),!isLastColRelative());
if(AreaReference.isWholeColumnReference(topLeft, botRight)) {
return (new AreaReference(topLeft, botRight)).formatAsString();
}
return topLeft.formatAsString() + ":" + botRight.formatAsString();
}
public String toFormulaString(Workbook book) { public String toFormulaString(Workbook book) {
return AreaReference.formatAsString(this); return formatReferenceAsString();
} }
public byte getDefaultOperandClass() { public byte getDefaultOperandClass() {

View File

@ -131,14 +131,6 @@ public abstract class RefPtgBase extends OperandPtg {
field_2_col=colRelative.setBoolean(field_2_col,rel); field_2_col=colRelative.setBoolean(field_2_col,rel);
} }
public final void setColumnRawX(int col) { // TODO
field_2_col = col;
}
public int getColumnRawX() { // TODO
return field_2_col;
}
public final void setColumn(int col) { public final void setColumn(int col) {
if(col < 0 || col >= 0x100) { if(col < 0 || col >= 0x100) {
throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range"); throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range");

View File

@ -1,32 +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.
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.Ptg;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class Area2DEval extends AreaEvalBase {
public Area2DEval(Ptg ptg, ValueEval[] values) {
super((AreaPtg) ptg, values);
}
}

View File

@ -1,39 +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.
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.Ptg;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class Area3DEval extends AreaEvalBase {
private final int _externSheetIndex;
public Area3DEval(Ptg ptg, ValueEval[] values) {
super((Area3DPtg) ptg, values);
_externSheetIndex = ((Area3DPtg) ptg).getExternSheetIndex();
}
public int getExternSheetIndex() {
return _externSheetIndex;
}
}

View File

@ -61,13 +61,6 @@ public interface AreaEval extends ValueEval {
*/ */
boolean isColumn(); boolean isColumn();
/**
* The array of values in this area. Although the area
* maybe 1D (ie. isRow() or isColumn() returns true) or 2D
* the returned array is 1D.
*/
ValueEval[] getValues();
/** /**
* @return the ValueEval from within this area at the specified row and col index. Never * @return the ValueEval from within this area at the specified row and col index. Never
* <code>null</code> (possibly {@link BlankEval}). The specified indexes should be absolute * <code>null</code> (possibly {@link BlankEval}). The specified indexes should be absolute
@ -104,4 +97,10 @@ public interface AreaEval extends ValueEval {
* specified indexes should relative to the top left corner of this area. * specified indexes should relative to the top left corner of this area.
*/ */
ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex); ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex);
/**
* Creates an {@link AreaEval} offset by a relative amount from from the upper left cell
* of this area
*/
AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx);
} }

View File

@ -22,20 +22,16 @@ import org.apache.poi.hssf.record.formula.AreaI;
/** /**
* @author Josh Micich * @author Josh Micich
*/ */
abstract class AreaEvalBase implements AreaEval { public abstract class AreaEvalBase implements AreaEval {
private final int _firstColumn; private final int _firstColumn;
private final int _firstRow; private final int _firstRow;
private final int _lastColumn; private final int _lastColumn;
private final int _lastRow; private final int _lastRow;
private final ValueEval[] _values;
private final int _nColumns; private final int _nColumns;
private final int _nRows; private final int _nRows;
protected AreaEvalBase(AreaI ptg, ValueEval[] values) { protected AreaEvalBase(AreaI ptg) {
if (values == null) {
throw new IllegalArgumentException("values must not be null");
}
_firstRow = ptg.getFirstRow(); _firstRow = ptg.getFirstRow();
_firstColumn = ptg.getFirstColumn(); _firstColumn = ptg.getFirstColumn();
_lastRow = ptg.getLastRow(); _lastRow = ptg.getLastRow();
@ -43,22 +39,6 @@ abstract class AreaEvalBase implements AreaEval {
_nColumns = _lastColumn - _firstColumn + 1; _nColumns = _lastColumn - _firstColumn + 1;
_nRows = _lastRow - _firstRow + 1; _nRows = _lastRow - _firstRow + 1;
int expectedItemCount = _nRows * _nColumns;
if ((values.length != expectedItemCount)) {
// Note - this math may need alteration when POI starts to support full column or full row refs
throw new IllegalArgumentException("Array size should be (" + expectedItemCount
+ ") but was (" + values.length + ")");
}
for (int i = values.length - 1; i >= 0; i--) {
if (values[i] == null) {
throw new IllegalArgumentException("value array elements must not be null");
}
}
_values = values;
} }
public final int getFirstColumn() { public final int getFirstColumn() {
@ -77,11 +57,6 @@ abstract class AreaEvalBase implements AreaEval {
return _lastRow; return _lastRow;
} }
public final ValueEval[] getValues() {
// TODO - clone() - but some junits rely on not cloning at the moment
return _values;
}
public final ValueEval getValueAt(int row, int col) { public final ValueEval getValueAt(int row, int col) {
int rowOffsetIx = row - _firstRow; int rowOffsetIx = row - _firstRow;
int colOffsetIx = col - _firstColumn; int colOffsetIx = col - _firstColumn;
@ -121,14 +96,7 @@ abstract class AreaEvalBase implements AreaEval {
return _lastRow-_firstRow+1; return _lastRow-_firstRow+1;
} }
public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) { public abstract ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex);
int index = relativeRowIndex * _nColumns + relativeColumnIndex;
ValueEval result = _values[index];
if (result == null) {
return BlankEval.INSTANCE;
}
return result;
}
public int getWidth() { public int getWidth() {
return _lastColumn-_firstColumn+1; return _lastColumn-_firstColumn+1;

View File

@ -50,7 +50,6 @@ public abstract class FunctionEval implements OperationEval {
static { static {
Map m = new HashMap(); Map m = new HashMap();
addMapping(m, ID.OFFSET, new Offset());
addMapping(m, ID.INDIRECT, new Indirect()); addMapping(m, ID.INDIRECT, new Indirect());
addMapping(m, ID.EXTERNAL_FUNC, new ExternalFunction()); addMapping(m, ID.EXTERNAL_FUNC, new ExternalFunction());
freeRefFunctionsByIdMap = m; freeRefFunctionsByIdMap = m;
@ -155,7 +154,7 @@ public abstract class FunctionEval implements OperationEval {
retval[75] = new Areas(); // AREAS retval[75] = new Areas(); // AREAS
retval[76] = new Rows(); // ROWS retval[76] = new Rows(); // ROWS
retval[77] = new Columns(); // COLUMNS retval[77] = new Columns(); // COLUMNS
retval[ID.OFFSET] = null; // Offset.evaluate has a different signature retval[ID.OFFSET] = new Offset(); // OFFSET
retval[79] = new Absref(); // ABSREF retval[79] = new Absref(); // ABSREF
retval[80] = new Relref(); // RELREF retval[80] = new Relref(); // RELREF
retval[81] = new Argument(); // ARGUMENT retval[81] = new Argument(); // ARGUMENT

View File

@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.AreaI;
import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public final class LazyAreaEval extends AreaEvalBase {
private final Sheet _sheet;
private Workbook _workbook;
public LazyAreaEval(AreaI ptg, Sheet sheet, Workbook workbook) {
super(ptg);
_sheet = sheet;
_workbook = workbook;
}
public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
int rowIx = (relativeRowIndex + getFirstRow() ) & 0xFFFF;
int colIx = (relativeColumnIndex + getFirstColumn() ) & 0x00FF;
Row row = _sheet.getRow(rowIx);
if (row == null) {
return BlankEval.INSTANCE;
}
Cell cell = row.getCell(colIx);
if (cell == null) {
return BlankEval.INSTANCE;
}
return FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook);
}
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
return new LazyAreaEval(area, _sheet, _workbook);
}
}

View File

@ -0,0 +1,52 @@
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.AreaI;
import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
public final class LazyRefEval extends RefEvalBase {
private final Sheet _sheet;
private final Workbook _workbook;
public LazyRefEval(RefPtg ptg, Sheet sheet, Workbook workbook) {
super(ptg.getRow(), ptg.getColumn());
_sheet = sheet;
_workbook = workbook;
}
public LazyRefEval(Ref3DPtg ptg, Sheet sheet, Workbook workbook) {
super(ptg.getRow(), ptg.getColumn());
_sheet = sheet;
_workbook = workbook;
}
public ValueEval getInnerValueEval() {
int rowIx = getRow();
int colIx = getColumn();
Row row = _sheet.getRow(rowIx);
if (row == null) {
return BlankEval.INSTANCE;
}
Cell cell = row.getCell(colIx);
if (cell == null) {
return BlankEval.INSTANCE;
}
return FormulaEvaluator.getEvalForCell(cell, _sheet, _workbook);
}
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
AreaI area = new OffsetArea(getRow(), getColumn(),
relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
return new LazyAreaEval(area, _sheet, _workbook);
}
}

View File

@ -69,5 +69,11 @@ public class NumberEval implements NumericValueEval, StringValueEval {
} }
} }
} }
public final String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");
sb.append(getStringValue());
sb.append("]");
return sb.toString();
}
} }

View File

@ -47,4 +47,7 @@ public final class Ref2DEval implements RefEval {
public int getColumn() { public int getColumn() {
return delegate.getColumn(); return delegate.getColumn();
} }
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
throw new RuntimeException("should not be called"); // TODO - delete this whole class
}
} }

View File

@ -1,53 +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.
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
/**
* @author Amol S. Deshmukh
*
*/
public final class Ref3DEval implements RefEval {
private final ValueEval value;
private final Ref3DPtg delegate;
public Ref3DEval(Ref3DPtg ptg, ValueEval ve) {
if(ve == null) {
throw new IllegalArgumentException("ve must not be null");
}
if(ptg == null) {
throw new IllegalArgumentException("ptg must not be null");
}
value = ve;
delegate = ptg;
}
public ValueEval getInnerValueEval() {
return value;
}
public int getRow() {
return delegate.getRow();
}
public int getColumn() {
return delegate.getColumn();
}
public int getExternSheetIndex() {
return delegate.getExternSheetIndex();
}
}

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;
@ -30,22 +30,22 @@ package org.apache.poi.hssf.record.formula.eval;
public interface RefEval extends ValueEval { public interface RefEval extends ValueEval {
/** /**
* The (possibly evaluated) ValueEval contained * @return the evaluated value of the cell referred to by this RefEval.
* in this RefEval. eg. if cell A1 contains "test"
* then in a formula referring to cell A1
* the RefEval representing
* A1 will return as the getInnerValueEval() the
* object of concrete type StringEval
*/ */
public ValueEval getInnerValueEval(); ValueEval getInnerValueEval();
/** /**
* returns the zero based column index. * returns the zero based column index.
*/ */
public int getColumn(); int getColumn();
/** /**
* returns the zero based row index. * returns the zero based row index.
*/ */
public int getRow(); int getRow();
/**
* Creates an {@link AreaEval} offset by a relative amount from this RefEval
*/
AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx);
} }

View File

@ -0,0 +1,18 @@
package org.apache.poi.hssf.record.formula.eval;
public abstract class RefEvalBase implements RefEval {
private final int _rowIndex;
private final int _columnIndex;
protected RefEvalBase(int rowIndex, int columnIndex) {
_rowIndex = rowIndex;
_columnIndex = columnIndex;
}
public final int getRow() {
return _rowIndex;
}
public final int getColumn() {
return _columnIndex;
}
}

View File

@ -50,9 +50,9 @@ final class CountUtils {
for (int rrIx=0; rrIx<height; rrIx++) { for (int rrIx=0; rrIx<height; rrIx++) {
for (int rcIx=0; rcIx<width; rcIx++) { for (int rcIx=0; rcIx<width; rcIx++) {
ValueEval ve = areaEval.getRelativeValue(rrIx, rcIx); ValueEval ve = areaEval.getRelativeValue(rrIx, rcIx);
if(criteriaPredicate.matches(ve)) { if(criteriaPredicate.matches(ve)) {
result++; result++;
} }
} }
} }
return result; return result;
@ -67,6 +67,9 @@ final class CountUtils {
return 0; return 0;
} }
public static int countArg(Eval eval, I_MatchPredicate criteriaPredicate) { public static int countArg(Eval eval, I_MatchPredicate criteriaPredicate) {
if (eval == null) {
throw new IllegalArgumentException("eval must not be null");
}
if (eval instanceof AreaEval) { if (eval instanceof AreaEval) {
return CountUtils.countMatchingCellsInArea((AreaEval) eval, criteriaPredicate); return CountUtils.countMatchingCellsInArea((AreaEval) eval, criteriaPredicate);
} }

View File

@ -1,28 +1,28 @@
/* /* ====================================================================
* 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 Nov 25, 2006
*
*/
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.BoolEval; 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.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.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
@ -30,20 +30,37 @@ import org.apache.poi.hssf.record.formula.eval.Eval;
*/ */
public final class If implements Function { public final class If implements Function {
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
Eval falseResult;
switch (args.length) {
case 3:
falseResult = args[2];
break;
case 2:
falseResult = BoolEval.FALSE;
break;
default:
return ErrorEval.VALUE_INVALID;
}
boolean b;
try {
b = evaluateFirstArg(args[0], srcCellRow, srcCellCol);
} catch (EvaluationException e) {
return e.getErrorEval();
}
if (b) {
return args[1];
}
return falseResult;
}
Eval evalWhenFalse = BoolEval.FALSE; private static boolean evaluateFirstArg(Eval arg, int srcCellRow, short srcCellCol)
switch (args.length) { throws EvaluationException {
case 3: ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
evalWhenFalse = args[2]; Boolean b = OperandResolver.coerceValueToBoolean(ve, false);
case 2: if (b == null) {
BoolEval beval = (BoolEval) args[0]; // TODO - class cast exception return false;
if (beval.getBooleanValue()) { }
return args[1]; return b.booleanValue();
} }
return evalWhenFalse;
default:
return ErrorEval.VALUE_INVALID;
}
}
} }

View File

@ -1,28 +1,28 @@
/* /* ====================================================================
* 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.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.AreaEval;
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.NumberEval; import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* Implementation for the Excel function INDEX<p/> * Implementation for the Excel function INDEX<p/>
@ -51,15 +51,23 @@ public final class Index implements Function {
return ErrorEval.VALUE_INVALID; return ErrorEval.VALUE_INVALID;
} }
Eval firstArg = args[0]; Eval firstArg = args[0];
if(firstArg instanceof AreaEval) { if(!(firstArg instanceof AreaEval)) {
AreaEval reference = (AreaEval) firstArg;
int rowIx = 0; // else the other variation of this function takes an array as the first argument
int columnIx = 0; // it seems like interface 'ArrayEval' does not even exist yet
int areaIx = 0;
throw new RuntimeException("Incomplete code - cannot handle first arg of type ("
+ firstArg.getClass().getName() + ")");
}
AreaEval reference = (AreaEval) firstArg;
int rowIx = 0;
int columnIx = 0;
int areaIx = 0;
try {
switch(nArgs) { switch(nArgs) {
case 4: case 4:
areaIx = convertIndexArgToZeroBase(args[3]); areaIx = convertIndexArgToZeroBase(args[3], srcCellRow, srcCellCol);
throw new RuntimeException("Incomplete code" + throw new RuntimeException("Incomplete code" +
" - don't know how to support the 'area_num' parameter yet)"); " - don't know how to support the 'area_num' parameter yet)");
// Excel expression might look like this "INDEX( (A1:B4, C3:D6, D2:E5 ), 1, 2, 3) // Excel expression might look like this "INDEX( (A1:B4, C3:D6, D2:E5 ), 1, 2, 3)
@ -68,41 +76,41 @@ public final class Index implements Function {
// The formula parser doesn't seem to support this yet. Not sure if the evaluator does either // The formula parser doesn't seem to support this yet. Not sure if the evaluator does either
case 3: case 3:
columnIx = convertIndexArgToZeroBase(args[2]); columnIx = convertIndexArgToZeroBase(args[2], srcCellRow, srcCellCol);
case 2: case 2:
rowIx = convertIndexArgToZeroBase(args[1]); rowIx = convertIndexArgToZeroBase(args[1], srcCellRow, srcCellCol);
break; break;
default: default:
// too many arguments // too many arguments
return ErrorEval.VALUE_INVALID; return ErrorEval.VALUE_INVALID;
} }
return getValueFromArea(reference, rowIx, columnIx);
int nColumns = reference.getLastColumn()-reference.getFirstColumn()+1; } catch (EvaluationException e) {
int index = rowIx * nColumns + columnIx; return e.getErrorEval();
return reference.getValues()[index];
} }
// else the other variation of this function takes an array as the first argument
// it seems like interface 'ArrayEval' does not even exist yet
throw new RuntimeException("Incomplete code - cannot handle first arg of type ("
+ firstArg.getClass().getName() + ")");
} }
private static ValueEval getValueFromArea(AreaEval ae, int rowIx, int columnIx) throws EvaluationException {
int width = ae.getWidth();
int height = ae.getHeight();
// Slightly irregular logic for bounds checking errors
if (rowIx >= height || columnIx >= width) {
throw new EvaluationException(ErrorEval.REF_INVALID);
}
if (rowIx < 0 || columnIx < 0) {
throw new EvaluationException(ErrorEval.VALUE_INVALID);
}
return ae.getRelativeValue(rowIx, columnIx);
}
/** /**
* takes a NumberEval representing a 1-based index and returns the zero-based int value * takes a NumberEval representing a 1-based index and returns the zero-based int value
*/ */
private static int convertIndexArgToZeroBase(Eval ev) { private static int convertIndexArgToZeroBase(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
NumberEval ne;
if(ev instanceof RefEval) {
// TODO - write junit to justify this
RefEval re = (RefEval) ev;
ne = (NumberEval) re.getInnerValueEval();
} else {
ne = (NumberEval)ev;
}
return (int)ne.getNumberValue() - 1; ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
int oneBasedVal = OperandResolver.coerceValueToInt(ev);
return oneBasedVal - 1;
} }
} }

View File

@ -17,8 +17,6 @@
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval; import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.BoolEval;
@ -366,13 +364,7 @@ final class LookupUtils {
// Make this cell ref look like a 1x1 area ref. // Make this cell ref look like a 1x1 area ref.
// It doesn't matter if eval is a 2D or 3D ref, because that detail is never asked of AreaEval. // It doesn't matter if eval is a 2D or 3D ref, because that detail is never asked of AreaEval.
// This code only requires the value array item. return refEval.offset(0, 0, 0, 0);
// anything would be ok for rowIx and colIx, but may as well get it right.
int rowIx = refEval.getRow();
int colIx = refEval.getColumn();
AreaPtg ap = new AreaPtg(rowIx, rowIx, colIx, colIx, false, false, false, false);
ValueEval value = refEval.getInnerValueEval();
return new Area2DEval(ap, new ValueEval[] { value, });
} }
throw EvaluationException.invalidValue(); throw EvaluationException.invalidValue();
} }

View File

@ -455,85 +455,6 @@ public final class MathX {
return d; return d;
} }
/**
* returns the sum of difference of squares of corresponding double
* value in each subarray: ie. sigma (xarr[i]^2-yarr[i]^2)
* <br/>
* It is the responsibility of the caller
* to ensure that the two subarrays are of equal length. If the
* subarrays are not of equal length, the return value can be
* unpredictable.
* @param xarr
* @param yarr
*/
public static double sumx2my2(double[] xarr, double[] yarr) {
double d = 0;
try {
for (int i=0, iSize=xarr.length; i<iSize; i++) {
d += (xarr[i] + yarr[i]) * (xarr[i] - yarr[i]);
}
}
catch (ArrayIndexOutOfBoundsException ae) {
d = Double.NaN;
}
return d;
}
/**
* returns the sum of sum of squares of corresponding double
* value in each subarray: ie. sigma (xarr[i]^2 + yarr[i]^2)
* <br/>
* It is the responsibility of the caller
* to ensure that the two subarrays are of equal length. If the
* subarrays are not of equal length, the return value can be
* unpredictable.
* @param xarr
* @param yarr
*/
public static double sumx2py2(double[] xarr, double[] yarr) {
double d = 0;
try {
for (int i=0, iSize=xarr.length; i<iSize; i++) {
d += (xarr[i] * xarr[i]) + (yarr[i] * yarr[i]);
}
}
catch (ArrayIndexOutOfBoundsException ae) {
d = Double.NaN;
}
return d;
}
/**
* returns the sum of squares of difference of corresponding double
* value in each subarray: ie. sigma ( (xarr[i]-yarr[i])^2 )
* <br/>
* It is the responsibility of the caller
* to ensure that the two subarrays are of equal length. If the
* subarrays are not of equal length, the return value can be
* unpredictable.
* @param xarr
* @param yarr
*/
public static double sumxmy2(double[] xarr, double[] yarr) {
double d = 0;
try {
for (int i=0, iSize=xarr.length; i<iSize; i++) {
double t = (xarr[i] - yarr[i]);
d += t * t;
}
}
catch (ArrayIndexOutOfBoundsException ae) {
d = Double.NaN;
}
return d;
}
/** /**
* returns the total number of combinations possible when * returns the total number of combinations possible when

View File

@ -126,30 +126,34 @@ public abstract class MultiOperandNumericFunction extends NumericFunction {
if (operand instanceof AreaEval) { if (operand instanceof AreaEval) {
AreaEval ae = (AreaEval) operand; AreaEval ae = (AreaEval) operand;
ValueEval[] values = ae.getValues();
DoubleList retval = new DoubleList(); DoubleList retval = new DoubleList();
for (int j=0, jSize=values.length; j<jSize; j++) { int width = ae.getWidth();
/* int height = ae.getHeight();
* TODO: For an AreaEval, we are constructing a RefEval for (int rrIx=0; rrIx<height; rrIx++) {
* per element. for (int rcIx=0; rcIx<width; rcIx++) {
* For now this is a tempfix solution since this may ValueEval ve1 = ae.getRelativeValue(rrIx, rcIx);
* require a more generic fix at the level of /*
* HSSFFormulaEvaluator where we store an array * TODO: For an AreaEval, we are constructing a RefEval
* of RefEvals as the "values" array. * per element.
*/ * For now this is a tempfix solution since this may
RefEval re = new Ref2DEval(null, values[j]); * require a more generic fix at the level of
ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol); * HSSFFormulaEvaluator where we store an array
* of RefEvals as the "values" array.
if (ve instanceof NumericValueEval) { */
NumericValueEval nve = (NumericValueEval) ve; RefEval re = new Ref2DEval(null, ve1);
retval.add(nve.getNumberValue()); ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol);
}
else if (ve instanceof BlankEval) { if (ve instanceof NumericValueEval) {
// note - blanks are ignored, so returned array will be smaller. NumericValueEval nve = (NumericValueEval) ve;
} retval.add(nve.getNumberValue());
else { }
return null; // indicate to calling subclass that error occurred else if (ve instanceof BlankEval) {
} // note - blanks are ignored, so returned array will be smaller.
}
else {
return null; // indicate to calling subclass that error occurred
}
}
} }
return retval.toArray(); return retval.toArray();
} }

View File

@ -1,26 +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
*
* 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.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.eval.Area3DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval; 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.ErrorEval;
@ -28,13 +24,9 @@ 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;
import org.apache.poi.hssf.record.formula.eval.NumericValueEval; import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
import org.apache.poi.hssf.record.formula.eval.OperandResolver; import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.Ref3DEval;
import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval; 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;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
/** /**
* Implementation for Excel function OFFSET()<p/> * Implementation for Excel function OFFSET()<p/>
* *
@ -51,7 +43,7 @@ import org.apache.poi.ss.usermodel.Workbook;
* *
* @author Josh Micich * @author Josh Micich
*/ */
public final class Offset implements FreeRefFunction { public final class Offset implements Function {
// These values are specific to BIFF8 // These values are specific to BIFF8
private static final int LAST_VALID_ROW_INDEX = 0xFFFF; private static final int LAST_VALID_ROW_INDEX = 0xFFFF;
private static final int LAST_VALID_COLUMN_INDEX = 0xFF; private static final int LAST_VALID_COLUMN_INDEX = 0xFF;
@ -125,37 +117,29 @@ public final class Offset implements FreeRefFunction {
* Encapsulates either an area or cell reference which may be 2d or 3d. * Encapsulates either an area or cell reference which may be 2d or 3d.
*/ */
private static final class BaseRef { private static final class BaseRef {
private static final int INVALID_SHEET_INDEX = -1;
private final int _firstRowIndex; private final int _firstRowIndex;
private final int _firstColumnIndex; private final int _firstColumnIndex;
private final int _width; private final int _width;
private final int _height; private final int _height;
private final int _externalSheetIndex; private final RefEval _refEval;
private final AreaEval _areaEval;
public BaseRef(RefEval re) { public BaseRef(RefEval re) {
_refEval = re;
_areaEval = null;
_firstRowIndex = re.getRow(); _firstRowIndex = re.getRow();
_firstColumnIndex = re.getColumn(); _firstColumnIndex = re.getColumn();
_height = 1; _height = 1;
_width = 1; _width = 1;
if (re instanceof Ref3DEval) {
Ref3DEval r3e = (Ref3DEval) re;
_externalSheetIndex = r3e.getExternSheetIndex();
} else {
_externalSheetIndex = INVALID_SHEET_INDEX;
}
} }
public BaseRef(AreaEval ae) { public BaseRef(AreaEval ae) {
_refEval = null;
_areaEval = ae;
_firstRowIndex = ae.getFirstRow(); _firstRowIndex = ae.getFirstRow();
_firstColumnIndex = ae.getFirstColumn(); _firstColumnIndex = ae.getFirstColumn();
_height = ae.getLastRow() - ae.getFirstRow() + 1; _height = ae.getLastRow() - ae.getFirstRow() + 1;
_width = ae.getLastColumn() - ae.getFirstColumn() + 1; _width = ae.getLastColumn() - ae.getFirstColumn() + 1;
if (ae instanceof Area3DEval) {
Area3DEval a3e = (Area3DEval) ae;
_externalSheetIndex = a3e.getExternSheetIndex();
} else {
_externalSheetIndex = INVALID_SHEET_INDEX;
}
} }
public int getWidth() { public int getWidth() {
@ -170,20 +154,17 @@ public final class Offset implements FreeRefFunction {
public int getFirstColumnIndex() { public int getFirstColumnIndex() {
return _firstColumnIndex; return _firstColumnIndex;
} }
public boolean isIs3d() {
return _externalSheetIndex > 0;
}
public short getExternalSheetIndex() { public AreaEval offset(int relFirstRowIx, int relLastRowIx,
if(_externalSheetIndex < 0) { int relFirstColIx, int relLastColIx) {
throw new IllegalStateException("external sheet index only available for 3d refs"); if (_refEval == null) {
return _areaEval.offset(relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
} }
return (short) _externalSheetIndex; return _refEval.offset(relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
} }
} }
public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, Workbook workbook, Sheet sheet) { public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
if(args.length < 3 || args.length > 5) { if(args.length < 3 || args.length > 5) {
return ErrorEval.VALUE_INVALID; return ErrorEval.VALUE_INVALID;
} }
@ -206,37 +187,24 @@ public final class Offset implements FreeRefFunction {
} }
LinearOffsetRange rowOffsetRange = new LinearOffsetRange(rowOffset, height); LinearOffsetRange rowOffsetRange = new LinearOffsetRange(rowOffset, height);
LinearOffsetRange colOffsetRange = new LinearOffsetRange(columnOffset, width); LinearOffsetRange colOffsetRange = new LinearOffsetRange(columnOffset, width);
return createOffset(baseRef, rowOffsetRange, colOffsetRange, workbook, sheet); return createOffset(baseRef, rowOffsetRange, colOffsetRange);
} catch (EvaluationException e) { } catch (EvaluationException e) {
return e.getErrorEval(); return e.getErrorEval();
} }
} }
private static AreaEval createOffset(BaseRef baseRef, private static AreaEval createOffset(BaseRef baseRef,
LinearOffsetRange rowOffsetRange, LinearOffsetRange colOffsetRange, LinearOffsetRange orRow, LinearOffsetRange orCol) throws EvaluationException {
Workbook workbook, Sheet sheet) throws EvaluationException { LinearOffsetRange absRows = orRow.normaliseAndTranslate(baseRef.getFirstRowIndex());
LinearOffsetRange absCols = orCol.normaliseAndTranslate(baseRef.getFirstColumnIndex());
LinearOffsetRange rows = rowOffsetRange.normaliseAndTranslate(baseRef.getFirstRowIndex());
LinearOffsetRange cols = colOffsetRange.normaliseAndTranslate(baseRef.getFirstColumnIndex());
if(rows.isOutOfBounds(0, LAST_VALID_ROW_INDEX)) { if(absRows.isOutOfBounds(0, LAST_VALID_ROW_INDEX)) {
throw new EvaluationException(ErrorEval.REF_INVALID); throw new EvaluationException(ErrorEval.REF_INVALID);
} }
if(cols.isOutOfBounds(0, LAST_VALID_COLUMN_INDEX)) { if(absCols.isOutOfBounds(0, LAST_VALID_COLUMN_INDEX)) {
throw new EvaluationException(ErrorEval.REF_INVALID); throw new EvaluationException(ErrorEval.REF_INVALID);
} }
if(baseRef.isIs3d()) { return baseRef.offset(orRow.getFirstIndex(), orRow.getLastIndex(), orCol.getFirstIndex(), orCol.getLastIndex());
Area3DPtg a3dp = new Area3DPtg(rows.getFirstIndex(), rows.getLastIndex(),
cols.getFirstIndex(), cols.getLastIndex(),
false, false, false, false,
baseRef.getExternalSheetIndex());
return HSSFFormulaEvaluator.evaluateArea3dPtg(workbook, a3dp);
}
AreaPtg ap = new AreaPtg(rows.getFirstIndex(), rows.getLastIndex(),
cols.getFirstIndex(), cols.getLastIndex(),
false, false, false, false);
return HSSFFormulaEvaluator.evaluateAreaPtg(sheet, workbook, ap);
} }
private static BaseRef evaluateBaseRef(Eval eval) throws EvaluationException { private static BaseRef evaluateBaseRef(Eval eval) throws EvaluationException {

View File

@ -1,22 +1,23 @@
/* /* ====================================================================
* 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.functions; package org.apache.poi.hssf.record.formula.functions;
/** /**
* Implementation of Excel function SUMX2MY2()<p/> * Implementation of Excel function SUMX2MY2()<p/>
* *
@ -30,7 +31,13 @@ package org.apache.poi.hssf.record.formula.functions;
*/ */
public final class Sumx2my2 extends XYNumericFunction { public final class Sumx2my2 extends XYNumericFunction {
protected double evaluate(double[] xArray, double[] yArray) { private static final Accumulator XSquaredMinusYSquaredAccumulator = new Accumulator() {
return MathX.sumx2my2(xArray, yArray); public double accumulate(double x, double y) {
} return x * x - y * y;
}
};
protected Accumulator createAccumulator() {
return XSquaredMinusYSquaredAccumulator;
}
} }

View File

@ -1,22 +1,23 @@
/* /* ====================================================================
* 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.functions; package org.apache.poi.hssf.record.formula.functions;
/** /**
* Implementation of Excel function SUMX2PY2()<p/> * Implementation of Excel function SUMX2PY2()<p/>
* *
@ -30,7 +31,13 @@ package org.apache.poi.hssf.record.formula.functions;
*/ */
public final class Sumx2py2 extends XYNumericFunction { public final class Sumx2py2 extends XYNumericFunction {
protected double evaluate(double[] xArray, double[] yArray) { private static final Accumulator XSquaredPlusYSquaredAccumulator = new Accumulator() {
return MathX.sumx2py2(xArray, yArray); public double accumulate(double x, double y) {
} return x * x + y * y;
}
};
protected Accumulator createAccumulator() {
return XSquaredPlusYSquaredAccumulator;
}
} }

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.functions; package org.apache.poi.hssf.record.formula.functions;
@ -30,7 +30,14 @@ package org.apache.poi.hssf.record.formula.functions;
*/ */
public final class Sumxmy2 extends XYNumericFunction { public final class Sumxmy2 extends XYNumericFunction {
protected double evaluate(double[] xArray, double[] yArray) { private static final Accumulator XMinusYSquaredAccumulator = new Accumulator() {
return MathX.sumxmy2(xArray, yArray); public double accumulate(double x, double y) {
} double xmy = x - y;
return xmy * xmy;
}
};
protected Accumulator createAccumulator() {
return XMinusYSquaredAccumulator;
}
} }

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.functions; package org.apache.poi.hssf.record.formula.functions;
@ -24,180 +24,162 @@ import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval;
import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
/** /**
* @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 XYNumericFunction implements Function { public abstract class XYNumericFunction implements Function {
protected static final int X = 0;
protected static final int Y = 1;
protected static final class DoubleArrayPair {
private final double[] _xArray; private static abstract class ValueArray implements ValueVector {
private final double[] _yArray; private final int _size;
protected ValueArray(int size) {
public DoubleArrayPair(double[] xArray, double[] yArray) { _size = size;
_xArray = xArray;
_yArray = yArray;
} }
public double[] getXArray() { public ValueEval getItem(int index) {
return _xArray; if (index < 0 || index > _size) {
throw new IllegalArgumentException("Specified index " + index
+ " is outside range (0.." + (_size - 1) + ")");
}
return getItemInternal(index);
} }
public double[] getYArray() { protected abstract ValueEval getItemInternal(int index);
return _yArray; public final int getSize() {
return _size;
} }
} }
private static final class SingleCellValueArray extends ValueArray {
private final ValueEval _value;
public SingleCellValueArray(ValueEval value) {
super(1);
_value = value;
}
protected ValueEval getItemInternal(int index) {
return _value;
}
}
public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) { private static final class RefValueArray extends ValueArray {
if(args.length != 2) { private final RefEval _ref;
return ErrorEval.VALUE_INVALID; public RefValueArray(RefEval ref) {
} super(1);
_ref = ref;
double[][] values; }
protected ValueEval getItemInternal(int index) {
return _ref.getInnerValueEval();
}
}
private static final class AreaValueArray extends ValueArray {
private final AreaEval _ae;
private final int _width;
public AreaValueArray(AreaEval ae) {
super(ae.getWidth() * ae.getHeight());
_ae = ae;
_width = ae.getWidth();
}
protected ValueEval getItemInternal(int index) {
int rowIx = index / _width;
int colIx = index % _width;
return _ae.getRelativeValue(rowIx, colIx);
}
}
protected static interface Accumulator {
double accumulate(double x, double y);
}
/**
* Constructs a new instance of the Accumulator used to calculated this function
*/
protected abstract Accumulator createAccumulator();
public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
if (args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
double result;
try { try {
values = getValues(args[0], args[1]); ValueVector vvX = createValueVector(args[0]);
ValueVector vvY = createValueVector(args[1]);
int size = vvX.getSize();
if (size == 0 || vvY.getSize() != size) {
return ErrorEval.NA;
}
result = evaluateInternal(vvX, vvY, size);
} catch (EvaluationException e) { } catch (EvaluationException e) {
return e.getErrorEval(); return e.getErrorEval();
} }
if (values==null if (Double.isNaN(result) || Double.isInfinite(result)) {
|| values[X] == null || values[Y] == null
|| values[X].length == 0 || values[Y].length == 0
|| values[X].length != values[Y].length) {
return ErrorEval.VALUE_INVALID;
}
double d = evaluate(values[X], values[Y]);
if (Double.isNaN(d) || Double.isInfinite(d)) {
return ErrorEval.NUM_ERROR; return ErrorEval.NUM_ERROR;
} }
return new NumberEval(d); return new NumberEval(result);
} }
protected abstract double evaluate(double[] xArray, double[] yArray);
/** private double evaluateInternal(ValueVector x, ValueVector y, int size)
* Returns a double array that contains values for the numeric cells throws EvaluationException {
* from among the list of operands. Blanks and Blank equivalent cells Accumulator acc = createAccumulator();
* are ignored. Error operands or cells containing operands of type
* that are considered invalid and would result in #VALUE! error in // error handling is as if the x is fully evaluated before y
* excel cause this function to return null. ErrorEval firstXerr = null;
*/ ErrorEval firstYerr = null;
private static double[][] getNumberArray(Eval[] xops, Eval[] yops) throws EvaluationException { boolean accumlatedSome = false;
double result = 0.0;
// check for errors first: size mismatch, value errors in x, value errors in y
for (int i = 0; i < size; i++) {
int nArrayItems = xops.length; ValueEval vx = x.getItem(i);
if(nArrayItems != yops.length) { ValueEval vy = y.getItem(i);
throw new EvaluationException(ErrorEval.NA); if (vx instanceof ErrorEval) {
} if (firstXerr == null) {
for (int i = 0; i < xops.length; i++) { firstXerr = (ErrorEval) vx;
Eval eval = xops[i]; continue;
if (eval instanceof ErrorEval) { }
throw new EvaluationException((ErrorEval) eval); }
if (vy instanceof ErrorEval) {
if (firstYerr == null) {
firstYerr = (ErrorEval) vy;
continue;
}
}
// only count pairs if both elements are numbers
if (vx instanceof NumberEval && vy instanceof NumberEval) {
accumlatedSome = true;
NumberEval nx = (NumberEval) vx;
NumberEval ny = (NumberEval) vy;
result += acc.accumulate(nx.getNumberValue(), ny.getNumberValue());
} else {
// all other combinations of value types are silently ignored
} }
} }
for (int i = 0; i < yops.length; i++) { if (firstXerr != null) {
Eval eval = yops[i]; throw new EvaluationException(firstXerr);
if (eval instanceof ErrorEval) {
throw new EvaluationException((ErrorEval) eval);
}
} }
if (firstYerr != null) {
double[] xResult = new double[nArrayItems]; throw new EvaluationException(firstYerr);
double[] yResult = new double[nArrayItems];
int count = 0;
for (int i=0, iSize=nArrayItems; i<iSize; i++) {
Eval xEval = xops[i];
Eval yEval = yops[i];
if (isNumberEval(xEval) && isNumberEval(yEval)) {
xResult[count] = getDoubleValue(xEval);
yResult[count] = getDoubleValue(yEval);
if (Double.isNaN(xResult[count]) || Double.isNaN(xResult[count])) {
throw new EvaluationException(ErrorEval.NUM_ERROR);
}
count++;
}
} }
if (!accumlatedSome) {
return new double[][] { throw new EvaluationException(ErrorEval.DIV_ZERO);
trimToSize(xResult, count),
trimToSize(yResult, count),
};
}
private static double[][] getValues(Eval argX, Eval argY) throws EvaluationException {
if (argX instanceof ErrorEval) {
throw new EvaluationException((ErrorEval) argX);
} }
if (argY instanceof ErrorEval) { return result;
throw new EvaluationException((ErrorEval) argY); }
private static ValueVector createValueVector(Eval arg) throws EvaluationException {
if (arg instanceof ErrorEval) {
throw new EvaluationException((ErrorEval) arg);
} }
if (arg instanceof AreaEval) {
Eval[] xEvals; return new AreaValueArray((AreaEval) arg);
Eval[] yEvals;
if (argX instanceof AreaEval) {
AreaEval ae = (AreaEval) argX;
xEvals = ae.getValues();
} else {
xEvals = new Eval[] { argX, };
} }
if (arg instanceof RefEval) {
if (argY instanceof AreaEval) { return new RefValueArray((RefEval) arg);
AreaEval ae = (AreaEval) argY;
yEvals = ae.getValues();
} else {
yEvals = new Eval[] { argY, };
} }
if (arg instanceof ValueEval) {
return getNumberArray(xEvals, yEvals); return new SingleCellValueArray((ValueEval) arg);
} }
throw new RuntimeException("Unexpected eval class (" + arg.getClass().getName() + ")");
private static double[] trimToSize(double[] arr, int len) { }
double[] tarr = arr;
if (arr.length > len) {
tarr = new double[len];
System.arraycopy(arr, 0, tarr, 0, len);
}
return tarr;
}
private static boolean isNumberEval(Eval eval) {
boolean retval = false;
if (eval instanceof NumberEval) {
retval = true;
}
else if (eval instanceof RefEval) {
RefEval re = (RefEval) eval;
ValueEval ve = re.getInnerValueEval();
retval = (ve instanceof NumberEval);
}
return retval;
}
private static double getDoubleValue(Eval eval) {
double retval = 0;
if (eval instanceof NumberEval) {
NumberEval ne = (NumberEval) eval;
retval = ne.getNumberValue();
}
else if (eval instanceof RefEval) {
RefEval re = (RefEval) eval;
ValueEval ve = re.getInnerValueEval();
retval = (ve instanceof NumberEval)
? ((NumberEval) ve).getNumberValue()
: Double.NaN;
}
else if (eval instanceof ErrorEval) {
retval = Double.NaN;
}
return retval;
}
} }

View File

@ -43,19 +43,11 @@ public class HSSFFormulaEvaluator extends FormulaEvaluator {
return new FormulaParser(formula, workbook); return new FormulaParser(formula, workbook);
} }
/** /**
* debug method * debug method
*
* @param formula
* @param sheet
* @param workbook
*/ */
void inspectPtgs(String formula) { void inspectPtgs(String formula) {
HSSFWorkbook hssfWb = (HSSFWorkbook)_workbook; Ptg[] ptgs = FormulaParser.parse(formula, _workbook);
FormulaParser fp = new FormulaParser(formula, hssfWb);
fp.parse();
Ptg[] ptgs = fp.getRPNPtg();
System.out.println("<ptg-group>"); System.out.println("<ptg-group>");
for (int i = 0, iSize = ptgs.length; i < iSize; i++) { for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
System.out.println("<ptg>"); System.out.println("<ptg>");

View File

@ -35,17 +35,11 @@ import org.apache.poi.hssf.model.FormulaParser;
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.record.CellValueRecordInterface; import org.apache.poi.hssf.record.CellValueRecordInterface;
import org.apache.poi.hssf.record.CFRuleRecord;
import org.apache.poi.hssf.record.DVALRecord;
import org.apache.poi.hssf.record.DVRecord; import org.apache.poi.hssf.record.DVRecord;
import org.apache.poi.hssf.record.EOFRecord;
import org.apache.poi.hssf.record.EscherAggregate; import org.apache.poi.hssf.record.EscherAggregate;
import org.apache.poi.hssf.record.HCenterRecord;
import org.apache.poi.hssf.record.PageBreakRecord;
import org.apache.poi.hssf.record.Record; import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RowRecord; import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.hssf.record.SCLRecord; import org.apache.poi.hssf.record.SCLRecord;
import org.apache.poi.hssf.record.VCenterRecord;
import org.apache.poi.hssf.record.WSBoolRecord; import org.apache.poi.hssf.record.WSBoolRecord;
import org.apache.poi.hssf.record.WindowTwoRecord; import org.apache.poi.hssf.record.WindowTwoRecord;
import org.apache.poi.hssf.record.aggregates.DataValidityTable; import org.apache.poi.hssf.record.aggregates.DataValidityTable;
@ -385,6 +379,22 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
DVRecord dvRecord = dataValidation.createDVRecord(workbook); DVRecord dvRecord = dataValidation.createDVRecord(workbook);
dvt.addDataValidation(dvRecord); dvt.addDataValidation(dvRecord);
} }
/**
* Get the DVRecords objects that are associated to this sheet
* @return a list of DVRecord instances
*/
public List getDVRecords() {
List dvRecords = new ArrayList();
List records = sheet.getRecords();
for(int index=0; index<records.size(); index++) {
if(records.get(index) instanceof DVRecord) {
dvRecords.add(records.get(index));
}
}
return dvRecords;
}
/** /**
@ -1273,11 +1283,9 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
// Since it's a formula cell, process the // Since it's a formula cell, process the
// formula string, and look to see if // formula string, and look to see if
// it contains any references // it contains any references
FormulaParser fp = new FormulaParser(c.getCellFormula(), workbook);
fp.parse();
// Look for references, and update if needed // Look for references, and update if needed
Ptg[] ptgs = fp.getRPNPtg(); Ptg[] ptgs = FormulaParser.parse(c.getCellFormula(), workbook);
boolean changed = false; boolean changed = false;
for(int i=0; i<ptgs.length; i++) { for(int i=0; i<ptgs.length; i++) {
if(ptgs[i] instanceof RefPtg) { if(ptgs[i] instanceof RefPtg) {
@ -1294,7 +1302,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
// re-create the formula string // re-create the formula string
if(changed) { if(changed) {
c.setCellFormula( c.setCellFormula(
fp.toFormulaString(ptgs) FormulaParser.toFormulaString(workbook, ptgs)
); );
} }
} }

View File

@ -931,35 +931,17 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
Stack ptgs = new Stack(); Stack ptgs = new Stack();
if (settingRowAndColumn) { if (settingRowAndColumn) {
final int exprsSize = 2 * 11 + 1; // Area3DPtg.SIZE + UnionPtg.SIZE final int exprsSize = 2 * 11 + 1; // 2 * Area3DPtg.SIZE + UnionPtg.SIZE
ptgs.add(new MemFuncPtg(exprsSize)); ptgs.add(new MemFuncPtg(exprsSize));
} }
if (startColumn >= 0) if (startColumn >= 0) {
{ Area3DPtg colArea = new Area3DPtg(0, MAX_ROW, startColumn, endColumn,
Area3DPtg colArea = new Area3DPtg(); false, false, false, false, externSheetIndex);
colArea.setExternSheetIndex(externSheetIndex);
colArea.setFirstColumn((short)startColumn);
colArea.setLastColumn((short)endColumn);
colArea.setFirstRow(0);
colArea.setLastRow(MAX_ROW);
colArea.setFirstColRelative(false);
colArea.setLastColRelative(false);
colArea.setFirstRowRelative(false);
colArea.setLastRowRelative(false);
ptgs.add(colArea); ptgs.add(colArea);
} }
if (startRow >= 0) if (startRow >= 0) {
{ Area3DPtg rowArea = new Area3DPtg(startRow, endRow, 0, MAX_COLUMN,
Area3DPtg rowArea = new Area3DPtg(); false, false, false, false, externSheetIndex);
rowArea.setExternSheetIndex(externSheetIndex);
rowArea.setFirstColumn((short)0);
rowArea.setLastColumn(MAX_COLUMN);
rowArea.setFirstRow(startRow);
rowArea.setLastRow(endRow);
rowArea.setFirstColRelative(false);
rowArea.setLastColRelative(false);
rowArea.setFirstRowRelative(false);
rowArea.setLastRowRelative(false);
ptgs.add(rowArea); ptgs.add(rowArea);
} }
if (settingRowAndColumn) if (settingRowAndColumn)

View File

@ -17,293 +17,21 @@
package org.apache.poi.hssf.util; package org.apache.poi.hssf.util;
import java.util.ArrayList;
import java.util.StringTokenizer;
import org.apache.poi.hssf.record.formula.AreaI;
public final class AreaReference {
/** The character (!) that separates sheet names from cell references */
private static final char SHEET_NAME_DELIMITER = '!';
/** The character (:) that separates the two cell references in a multi-cell area reference */
private static final char CELL_DELIMITER = ':';
/** The character (') used to quote sheet names when they contain special characters */
private static final char SPECIAL_NAME_DELIMITER = '\'';
private final CellReference _firstCell;
private final CellReference _lastCell;
private final boolean _isSingleCell;
public final class AreaReference extends org.apache.poi.ss.util.AreaReference {
/** /**
* Create an area ref from a string representation. Sheet names containing special characters should be * Create an area ref from a string representation. Sheet names containing special characters should be
* delimited and escaped as per normal syntax rules for formulas.<br/> * delimited and escaped as per normal syntax rules for formulas.<br/>
* The area reference must be contiguous (i.e. represent a single rectangle, not a union of rectangles) * The area reference must be contiguous (i.e. represent a single rectangle, not a union of rectangles)
*/ */
public AreaReference(String reference) { public AreaReference(String reference) {
if(! isContiguous(reference)) { super(reference);
throw new IllegalArgumentException(
"References passed to the AreaReference must be contiguous, " +
"use generateContiguous(ref) if you have non-contiguous references");
}
String[] parts = separateAreaRefs(reference);
// Special handling for whole-column references
if(parts.length == 2 && parts[0].length() == 1 &&
parts[1].length() == 1 &&
parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' &&
parts[1].charAt(0) >= 'A' && parts[1].charAt(0) <= 'Z') {
// Represented internally as x$1 to x$65536
// which is the maximum range of rows
parts[0] = parts[0] + "$1";
parts[1] = parts[1] + "$65536";
}
_firstCell = new CellReference(parts[0]);
if(parts.length == 2) {
_lastCell = new CellReference(parts[1]);
_isSingleCell = false;
} else {
_lastCell = _firstCell;
_isSingleCell = true;
}
} }
/** /**
* Creates an area ref from a pair of Cell References. * Creates an area ref from a pair of Cell References.
*/ */
public AreaReference(CellReference topLeft, CellReference botRight) { public AreaReference(CellReference topLeft, CellReference botRight) {
_firstCell = topLeft; super(topLeft, botRight);
_lastCell = botRight;
_isSingleCell = false;
} }
}
/**
* Is the reference for a contiguous (i.e.
* unbroken) area, or is it made up of
* several different parts?
* (If it is, you will need to call
* ....
*/
public static boolean isContiguous(String reference) {
if(reference.indexOf(',') == -1) {
return true;
}
return false;
}
/**
* Is the reference for a whole-column reference,
* such as C:C or D:G ?
*/
public static boolean isWholeColumnReference(CellReference topLeft, CellReference botRight) {
// These are represented as something like
// C$1:C$65535 or D$1:F$0
// i.e. absolute from 1st row to 0th one
if(topLeft.getRow() == 0 && topLeft.isRowAbsolute() &&
botRight.getRow() == 65535 && botRight.isRowAbsolute()) {
return true;
}
return false;
}
public boolean isWholeColumnReference() {
return isWholeColumnReference(_firstCell, _lastCell);
}
/**
* Takes a non-contiguous area reference, and
* returns an array of contiguous area references.
*/
public static AreaReference[] generateContiguous(String reference) {
ArrayList refs = new ArrayList();
StringTokenizer st = new StringTokenizer(reference, ",");
while(st.hasMoreTokens()) {
refs.add(
new AreaReference(st.nextToken())
);
}
return (AreaReference[])refs.toArray(new AreaReference[refs.size()]);
}
/**
* @return <code>false</code> if this area reference involves more than one cell
*/
public boolean isSingleCell() {
return _isSingleCell;
}
/**
* @return the first cell reference which defines this area. Usually this cell is in the upper
* left corner of the area (but this is not a requirement).
*/
public CellReference getFirstCell() {
return _firstCell;
}
/**
* Note - if this area reference refers to a single cell, the return value of this method will
* be identical to that of <tt>getFirstCell()</tt>
* @return the second cell reference which defines this area. For multi-cell areas, this is
* cell diagonally opposite the 'first cell'. Usually this cell is in the lower right corner
* of the area (but this is not a requirement).
*/
public CellReference getLastCell() {
return _lastCell;
}
/**
* Returns a reference to every cell covered by this area
*/
public CellReference[] getAllReferencedCells() {
// Special case for single cell reference
if(_isSingleCell) {
return new CellReference[] { _firstCell, };
}
// Interpolate between the two
int minRow = Math.min(_firstCell.getRow(), _lastCell.getRow());
int maxRow = Math.max(_firstCell.getRow(), _lastCell.getRow());
int minCol = Math.min(_firstCell.getCol(), _lastCell.getCol());
int maxCol = Math.max(_firstCell.getCol(), _lastCell.getCol());
String sheetName = _firstCell.getSheetName();
ArrayList refs = new ArrayList();
for(int row=minRow; row<=maxRow; row++) {
for(int col=minCol; col<=maxCol; col++) {
CellReference ref = new CellReference(sheetName, row, col, _firstCell.isRowAbsolute(), _firstCell.isColAbsolute());
refs.add(ref);
}
}
return (CellReference[])refs.toArray(new CellReference[refs.size()]);
}
/**
* Example return values:
* <table border="0" cellpadding="1" cellspacing="0" summary="Example return values">
* <tr><th align='left'>Result</th><th align='left'>Comment</th></tr>
* <tr><td>A1:A1</td><td>Single cell area reference without sheet</td></tr>
* <tr><td>A1:$C$1</td><td>Multi-cell area reference without sheet</td></tr>
* <tr><td>Sheet1!A$1:B4</td><td>Standard sheet name</td></tr>
* <tr><td>'O''Brien''s Sales'!B5:C6'&nbsp;</td><td>Sheet name with special characters</td></tr>
* </table>
* @return the text representation of this area reference as it would appear in a formula.
*/
public String formatAsString() {
// Special handling for whole-column references
if(isWholeColumnReference()) {
return
CellReference.convertNumToColString(_firstCell.getCol())
+ ":" +
CellReference.convertNumToColString(_lastCell.getCol());
}
StringBuffer sb = new StringBuffer(32);
sb.append(_firstCell.formatAsString());
if(!_isSingleCell) {
sb.append(CELL_DELIMITER);
if(_lastCell.getSheetName() == null) {
sb.append(_lastCell.formatAsString());
} else {
// don't want to include the sheet name twice
_lastCell.appendCellReference(sb);
}
}
return sb.toString();
}
/**
* Formats a 2-D area as it would appear in a formula. See formatAsString() (no-arg)
*/
public static String formatAsString(AreaI area) {
CellReference topLeft = new CellReference(area.getFirstRow(),area.getFirstColumn(),!area.isFirstRowRelative(),!area.isFirstColRelative());
CellReference botRight = new CellReference(area.getLastRow(),area.getLastColumn(),!area.isLastRowRelative(),!area.isLastColRelative());
if(isWholeColumnReference(topLeft, botRight)) {
return (new AreaReference(topLeft, botRight)).formatAsString();
}
return topLeft.formatAsString() + ":" + botRight.formatAsString();
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");
sb.append(formatAsString());
sb.append("]");
return sb.toString();
}
/**
* Separates Area refs in two parts and returns them as separate elements in a String array,
* each qualified with the sheet name (if present)
*
* @return array with one or two elements. never <code>null</code>
*/
private static String[] separateAreaRefs(String reference) {
// TODO - refactor cell reference parsing logic to one place.
// Current known incarnations:
// FormulaParser.GetName()
// CellReference.separateRefParts()
// AreaReference.separateAreaRefs() (here)
// SheetNameFormatter.format() (inverse)
int len = reference.length();
int delimiterPos = -1;
boolean insideDelimitedName = false;
for(int i=0; i<len; i++) {
switch(reference.charAt(i)) {
case CELL_DELIMITER:
if(!insideDelimitedName) {
if(delimiterPos >=0) {
throw new IllegalArgumentException("More than one cell delimiter '"
+ CELL_DELIMITER + "' appears in area reference '" + reference + "'");
}
delimiterPos = i;
}
default:
continue;
case SPECIAL_NAME_DELIMITER:
// fall through
}
if(!insideDelimitedName) {
insideDelimitedName = true;
continue;
}
if(i >= len-1) {
// reference ends with the delimited name.
// Assume names like: "Sheet1!'A1'" are never legal.
throw new IllegalArgumentException("Area reference '" + reference
+ "' ends with special name delimiter '" + SPECIAL_NAME_DELIMITER + "'");
}
if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) {
// two consecutive quotes is the escape sequence for a single one
i++; // skip this and keep parsing the special name
} else {
// this is the end of the delimited name
insideDelimitedName = false;
}
}
if(delimiterPos < 0) {
return new String[] { reference, };
}
String partA = reference.substring(0, delimiterPos);
String partB = reference.substring(delimiterPos+1);
if(partB.indexOf(SHEET_NAME_DELIMITER) >=0) {
// TODO - are references like "Sheet1!A1:Sheet1:B2" ever valid?
// FormulaParser has code to handle that.
throw new RuntimeException("Unexpected " + SHEET_NAME_DELIMITER
+ " in second cell reference of '" + reference + "'");
}
int plingPos = partA.lastIndexOf(SHEET_NAME_DELIMITER);
if(plingPos < 0) {
return new String [] { partA, partB, };
}
String sheetName = partA.substring(0, plingPos + 1); // +1 to include delimiter
return new String [] { partA, sheetName + partB, };
}
}

View File

@ -1,26 +1,23 @@
/* /* ====================================================================
* 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.ss.usermodel; package org.apache.poi.ss.usermodel;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map;
import java.util.Stack; import java.util.Stack;
import org.apache.poi.hssf.model.FormulaParser; import org.apache.poi.hssf.model.FormulaParser;
@ -28,6 +25,7 @@ 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;
import org.apache.poi.hssf.record.formula.ControlPtg; import org.apache.poi.hssf.record.formula.ControlPtg;
import org.apache.poi.hssf.record.formula.ErrPtg;
import org.apache.poi.hssf.record.formula.IntPtg; import org.apache.poi.hssf.record.formula.IntPtg;
import org.apache.poi.hssf.record.formula.MemErrPtg; import org.apache.poi.hssf.record.formula.MemErrPtg;
import org.apache.poi.hssf.record.formula.MissingArgPtg; import org.apache.poi.hssf.record.formula.MissingArgPtg;
@ -41,77 +39,51 @@ import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.record.formula.StringPtg; import org.apache.poi.hssf.record.formula.StringPtg;
import org.apache.poi.hssf.record.formula.UnionPtg; import org.apache.poi.hssf.record.formula.UnionPtg;
import org.apache.poi.hssf.record.formula.UnknownPtg; import org.apache.poi.hssf.record.formula.UnknownPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.Area3DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval; 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.BoolEval;
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.FunctionEval; import org.apache.poi.hssf.record.formula.eval.FunctionEval;
import org.apache.poi.hssf.record.formula.eval.LazyAreaEval;
import org.apache.poi.hssf.record.formula.eval.LazyRefEval;
import org.apache.poi.hssf.record.formula.eval.NameEval; import org.apache.poi.hssf.record.formula.eval.NameEval;
import org.apache.poi.hssf.record.formula.eval.NameXEval; import org.apache.poi.hssf.record.formula.eval.NameXEval;
import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.OperationEval; import org.apache.poi.hssf.record.formula.eval.OperationEval;
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
import org.apache.poi.hssf.record.formula.eval.Ref3DEval;
import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval; 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;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
/** /**
* @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 FormulaEvaluator { public class FormulaEvaluator {
// params to lookup the right constructor using reflection
private static final Class[] VALUE_CONTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
private static final Class[] AREA3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval[].class };
private static final Class[] REFERENCE_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
private static final Class[] REF3D_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class, ValueEval.class };
// Maps for mapping *Eval to *Ptg
private static final Map VALUE_EVALS_MAP = new HashMap();
/*
* Following is the mapping between the Ptg tokens returned
* by the FormulaParser and the *Eval classes that are used
* by the FormulaEvaluator
*/
static {
VALUE_EVALS_MAP.put(BoolPtg.class, BoolEval.class);
VALUE_EVALS_MAP.put(IntPtg.class, NumberEval.class);
VALUE_EVALS_MAP.put(NumberPtg.class, NumberEval.class);
VALUE_EVALS_MAP.put(StringPtg.class, StringEval.class);
}
protected Sheet _sheet; protected Sheet _sheet;
protected Workbook _workbook; protected Workbook _workbook;
public FormulaEvaluator(Sheet sheet, Workbook workbook) { public FormulaEvaluator(Sheet sheet, Workbook workbook) {
this._sheet = sheet; _sheet = sheet;
this._workbook = workbook; _workbook = workbook;
} }
/** /**
* Does nothing * Does nothing
* @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell * @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell
*/ */
public void setCurrentRow(Row row) {} public void setCurrentRow(Row row) {
// do nothing
}
/** /**
* If cell contains a formula, the formula is evaluated and returned, * If cell contains a formula, the formula is evaluated and returned,
* else the CellValue simply copies the appropriate cell value from * else the CellValue simply copies the appropriate cell value from
* the cell and also its cell type. This method should be preferred over * the cell and also its cell type. This method should be preferred over
* evaluateInCell() when the call should not modify the contents of the * evaluateInCell() when the call should not modify the contents of the
* original cell. * original cell.
* @param cell * @param cell
*/ */
public CellValue evaluate(Cell cell) { public CellValue evaluate(Cell cell) {
@ -144,25 +116,25 @@ public class FormulaEvaluator {
} }
return retval; return retval;
} }
/** /**
* If cell contains formula, it evaluates the formula, * If cell contains formula, it evaluates the formula,
* and saves the result of the formula. The cell * and saves the result of the formula. The cell
* remains as a formula cell. * remains as a formula cell.
* Else if cell does not contain formula, this method leaves * Else if cell does not contain formula, this method leaves
* the cell unchanged. * the cell unchanged.
* Note that the type of the formula result is returned, * Note that the type of the formula result is returned,
* so you know what kind of value is also stored with * so you know what kind of value is also stored with
* the formula. * the formula.
* <pre> * <pre>
* int evaluatedCellType = evaluator.evaluateFormulaCell(cell); * int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
* </pre> * </pre>
* Be aware that your cell will hold both the formula, * Be aware that your cell will hold both the formula,
* and the result. If you want the cell replaced with * and the result. If you want the cell replaced with
* the result of the formula, use {@link #evaluateInCell(HSSFCell)} * the result of the formula, use {@link #evaluateInCell(Cell)}
* @param cell The cell to evaluate * @param cell The cell to evaluate
* @return The type of the formula result (the cell's type remains as HSSFCell.CELL_TYPE_FORMULA however) * @return The type of the formula result (the cell's type remains as Cell.CELL_TYPE_FORMULA however)
*/ */
public int evaluateFormulaCell(Cell cell) { public int evaluateFormulaCell(Cell cell) {
if (cell != null) { if (cell != null) {
@ -192,21 +164,21 @@ public class FormulaEvaluator {
} }
return -1; return -1;
} }
/** /**
* If cell contains formula, it evaluates the formula, and * If cell contains formula, it evaluates the formula, and
* puts the formula result back into the cell, in place * puts the formula result back into the cell, in place
* of the old formula. * of the old formula.
* Else if cell does not contain formula, this method leaves * Else if cell does not contain formula, this method leaves
* the cell unchanged. * the cell unchanged.
* Note that the same instance of HSSFCell is returned to * Note that the same instance of Cell is returned to
* allow chained calls like: * allow chained calls like:
* <pre> * <pre>
* int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType(); * int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
* </pre> * </pre>
* Be aware that your cell value will be changed to hold the * Be aware that your cell value will be changed to hold the
* result of the formula. If you simply want the formula * result of the formula. If you simply want the formula
* value computed for you, use {@link #evaluateFormulaCell(HSSFCell)} * value computed for you, use {@link #evaluateFormulaCell(Cell)}
* @param cell * @param cell
*/ */
public Cell evaluateInCell(Cell cell) { public Cell evaluateInCell(Cell cell) {
@ -239,7 +211,7 @@ public class FormulaEvaluator {
} }
return cell; return cell;
} }
/** /**
* Loops over all cells in all sheets of the supplied * Loops over all cells in all sheets of the supplied
* workbook. * workbook.
@ -248,32 +220,32 @@ public class FormulaEvaluator {
* remain as formula cells. * remain as formula cells.
* For cells that do not contain formulas, no changes * For cells that do not contain formulas, no changes
* are made. * are made.
* This is a helpful wrapper around looping over all * This is a helpful wrapper around looping over all
* cells, and calling evaluateFormulaCell on each one. * cells, and calling evaluateFormulaCell on each one.
*/ */
public static void evaluateAllFormulaCells(Workbook wb) { public static void evaluateAllFormulaCells(Workbook wb) {
for(int i=0; i<wb.getNumberOfSheets(); i++) { for(int i=0; i<wb.getNumberOfSheets(); i++) {
Sheet sheet = wb.getSheetAt(i); Sheet sheet = wb.getSheetAt(i);
FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb); FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb);
for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {
Row r = (Row)rit.next();
for (Iterator cit = r.cellIterator(); cit.hasNext();) {
Cell c = (Cell)cit.next();
if (c.getCellType() == Cell.CELL_TYPE_FORMULA)
evaluator.evaluateFormulaCell(c);
}
}
}
}
for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {
Row r = (Row)rit.next();
for (Iterator cit = r.cellIterator(); cit.hasNext();) {
Cell c = (Cell)cit.next();
if (c.getCellType() == Cell.CELL_TYPE_FORMULA)
evaluator.evaluateFormulaCell(c);
}
}
}
}
/** /**
* Returns a CellValue wrapper around the supplied ValueEval instance. * Returns a CellValue wrapper around the supplied ValueEval instance.
* @param eval * @param eval
*/ */
protected static CellValue getCellValueForEval(ValueEval eval, CreationHelper cHelper) { private static CellValue getCellValueForEval(ValueEval eval, CreationHelper cHelper) {
CellValue retval = null; CellValue retval = null;
if (eval != null) { if (eval != null) {
if (eval instanceof NumberEval) { if (eval instanceof NumberEval) {
@ -297,7 +269,7 @@ public class FormulaEvaluator {
else if (eval instanceof ErrorEval) { else if (eval instanceof ErrorEval) {
retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper); retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper);
retval.setErrorValue((byte)((ErrorEval)eval).getErrorCode()); retval.setErrorValue((byte)((ErrorEval)eval).getErrorCode());
// retval.setRichTextStringValue(new HSSFRichTextString("#An error occurred. check cell.getErrorCode()")); // retval.setRichTextStringValue(new RichTextString("#An error occurred. check cell.getErrorCode()"));
} }
else { else {
retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper); retval = new CellValue(Cell.CELL_TYPE_ERROR, cHelper);
@ -305,19 +277,19 @@ public class FormulaEvaluator {
} }
return retval; return retval;
} }
/** /**
* Dev. Note: Internal evaluate must be passed only a formula cell * Dev. Note: Internal evaluate must be passed only a formula cell
* 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 static ValueEval internalEvaluate(Cell srcCell, Sheet sheet, Workbook workbook) {
int srcRowNum = srcCell.getRowIndex(); int srcRowNum = srcCell.getRowIndex();
short srcColNum = srcCell.getCellNum(); short srcColNum = srcCell.getCellNum();
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;
} }
@ -327,8 +299,9 @@ public class FormulaEvaluator {
tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum); tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
} }
} }
private static ValueEval evaluateCell(Workbook workbook, Sheet sheet, private static ValueEval evaluateCell(Workbook workbook, Sheet sheet,
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();
@ -337,16 +310,16 @@ public class FormulaEvaluator {
// since we don't know how to handle these yet :( // since we don't know how to handle these yet :(
Ptg ptg = ptgs[i]; Ptg ptg = ptgs[i];
if (ptg instanceof ControlPtg) { if (ptg instanceof ControlPtg) {
// skip Parentheses, Attr, etc // skip Parentheses, Attr, etc
continue; continue;
} }
if (ptg instanceof MemErrPtg) { continue; } if (ptg instanceof MemErrPtg) { continue; }
if (ptg instanceof MissingArgPtg) { continue; } if (ptg instanceof MissingArgPtg) { continue; }
if (ptg instanceof NamePtg) { if (ptg instanceof NamePtg) {
// named ranges, macro functions // named ranges, macro functions
NamePtg namePtg = (NamePtg) ptg; NamePtg namePtg = (NamePtg) ptg;
stack.push(new NameEval(namePtg.getIndex())); stack.push(new NameEval(namePtg.getIndex()));
continue; continue;
} }
if (ptg instanceof NameXPtg) { if (ptg instanceof NameXPtg) {
NameXPtg nameXPtg = (NameXPtg) ptg; NameXPtg nameXPtg = (NameXPtg) ptg;
@ -354,7 +327,7 @@ public class FormulaEvaluator {
continue; continue;
} }
if (ptg instanceof UnknownPtg) { continue; } if (ptg instanceof UnknownPtg) { continue; }
Eval opResult;
if (ptg instanceof OperationPtg) { if (ptg instanceof OperationPtg) {
OperationPtg optg = (OperationPtg) ptg; OperationPtg optg = (OperationPtg) ptg;
@ -370,42 +343,11 @@ public class FormulaEvaluator {
Eval p = (Eval) stack.pop(); Eval p = (Eval) stack.pop();
ops[j] = p; ops[j] = p;
} }
Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet); opResult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
stack.push(opresult); } else {
} opResult = getEvalForPtg(ptg, sheet, workbook);
else if (ptg instanceof RefPtg) {
RefPtg refPtg = (RefPtg) ptg;
int colIx = refPtg.getColumn();
int rowIx = refPtg.getRow();
Row row = sheet.getRow(rowIx);
Cell cell = (row != null) ? row.getCell(colIx) : null;
stack.push(createRef2DEval(refPtg, cell, sheet, workbook));
}
else if (ptg instanceof Ref3DPtg) {
Ref3DPtg refPtg = (Ref3DPtg) ptg;
int colIx = refPtg.getColumn();
int rowIx = refPtg.getRow();
Sheet xsheet = workbook.getSheetAt(
workbook.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex())
);
Row row = xsheet.getRow(rowIx);
Cell cell = (row != null) ? row.getCell(colIx) : null;
stack.push(createRef3DEval(refPtg, cell, xsheet, workbook));
}
else if (ptg instanceof AreaPtg) {
AreaPtg ap = (AreaPtg) ptg;
AreaEval ae = evaluateAreaPtg(sheet, workbook, ap);
stack.push(ae);
}
else if (ptg instanceof Area3DPtg) {
Area3DPtg a3dp = (Area3DPtg) ptg;
AreaEval ae = evaluateArea3dPtg(workbook, a3dp);
stack.push(ae);
}
else {
Eval ptgEval = getEvalForPtg(ptg);
stack.push(ptgEval);
} }
stack.push(opResult);
} }
ValueEval value = ((ValueEval) stack.pop()); ValueEval value = ((ValueEval) stack.pop());
@ -414,9 +356,9 @@ public class FormulaEvaluator {
} }
value = dereferenceValue(value, srcRowNum, srcColNum); value = dereferenceValue(value, srcRowNum, srcColNum);
if (value instanceof BlankEval) { if (value instanceof BlankEval) {
// Note Excel behaviour here. A blank final final value is converted to zero. // Note Excel behaviour here. A blank final final value is converted to zero.
return NumberEval.ZERO; return NumberEval.ZERO;
// Formulas _never_ evaluate to blank. If a formula appears to have evaluated to // Formulas _never_ evaluate to blank. If a formula appears to have evaluated to
// blank, the actual value is empty string. This can be verified with ISBLANK(). // blank, the actual value is empty string. This can be verified with ISBLANK().
} }
return value; return value;
@ -425,8 +367,8 @@ public class FormulaEvaluator {
/** /**
* Dereferences a single value from any AreaEval or RefEval evaluation result. * Dereferences a single value from any AreaEval or RefEval evaluation result.
* If the supplied evaluationResult is just a plain value, it is returned as-is. * If the supplied evaluationResult is just a plain value, it is returned as-is.
* @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>, * @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
* <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>. * <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
*/ */
private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) { private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) {
if (evaluationResult instanceof RefEval) { if (evaluationResult instanceof RefEval) {
@ -460,107 +402,48 @@ public class FormulaEvaluator {
} }
return operation.evaluate(ops, srcRowNum, srcColNum); return operation.evaluate(ops, srcRowNum, srcColNum);
} }
public static AreaEval evaluateAreaPtg(Sheet sheet, Workbook workbook, AreaPtg ap) {
int row0 = ap.getFirstRow();
int col0 = ap.getFirstColumn();
int row1 = ap.getLastRow();
int col1 = ap.getLastColumn();
// If the last row is -1, then the
// reference is for the rest of the column
// (eg C:C)
// TODO: Handle whole column ranges properly
if(row1 == -1 && row0 >= 0) {
row1 = (short)sheet.getLastRowNum();
}
ValueEval[] values = evalArea(workbook, sheet, row0, col0, row1, col1);
return new Area2DEval(ap, values);
}
public static AreaEval evaluateArea3dPtg(Workbook workbook, Area3DPtg a3dp) {
int row0 = a3dp.getFirstRow();
int col0 = a3dp.getFirstColumn();
int row1 = a3dp.getLastRow();
int col1 = a3dp.getLastColumn();
Sheet xsheet = workbook.getSheetAt(
workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex())
);
// If the last row is -1, then the
// reference is for the rest of the column
// (eg C:C)
// TODO: Handle whole column ranges properly
if(row1 == -1 && row0 >= 0) {
row1 = (short)xsheet.getLastRowNum();
}
ValueEval[] values = evalArea(workbook, xsheet, row0, col0, row1, col1);
return new Area3DEval(a3dp, values);
}
private static ValueEval[] evalArea(Workbook workbook, Sheet sheet,
int row0, int col0, int row1, int col1) {
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
for (int x = row0; sheet != null && x < row1 + 1; x++) {
Row row = sheet.getRow(x);
for (int y = col0; y < col1 + 1; y++) {
ValueEval cellEval;
if(row == null) {
cellEval = BlankEval.INSTANCE;
} else {
cellEval = getEvalForCell(row.getCell(y), row, sheet, workbook);
}
values[(x - row0) * (col1 - col0 + 1) + (y - col0)] = cellEval;
}
}
return values;
}
/** /**
* 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, RefPtg, 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!
*
* @param ptg
*/ */
protected static Eval getEvalForPtg(Ptg ptg) { private static Eval getEvalForPtg(Ptg ptg, Sheet sheet, Workbook workbook) {
Eval retval = null; if (ptg instanceof RefPtg) {
return new LazyRefEval(((RefPtg) ptg), sheet, workbook);
Class clazz = (Class) VALUE_EVALS_MAP.get(ptg.getClass());
try {
if (ptg instanceof Area3DPtg) {
Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
}
else if (ptg instanceof AreaPtg) {
Constructor constructor = clazz.getConstructor(AREA3D_CONSTRUCTOR_CLASS_ARRAY);
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
}
else if (ptg instanceof RefPtg) {
Constructor constructor = clazz.getConstructor(REFERENCE_CONSTRUCTOR_CLASS_ARRAY);
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
}
else if (ptg instanceof Ref3DPtg) {
Constructor constructor = clazz.getConstructor(REF3D_CONSTRUCTOR_CLASS_ARRAY);
retval = (OperationEval) constructor.newInstance(new Ptg[] { ptg });
}
else {
if (ptg instanceof IntPtg || ptg instanceof NumberPtg || ptg instanceof StringPtg
|| ptg instanceof BoolPtg) {
Constructor constructor = clazz.getConstructor(VALUE_CONTRUCTOR_CLASS_ARRAY);
retval = (ValueEval) constructor.newInstance(new Ptg[] { ptg });
}
}
} }
catch (Exception e) { if (ptg instanceof Ref3DPtg) {
throw new RuntimeException("Fatal Error: ", e); Ref3DPtg refPtg = (Ref3DPtg) ptg;
Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex()));
return new LazyRefEval(refPtg, xsheet, workbook);
}
if (ptg instanceof AreaPtg) {
return new LazyAreaEval(((AreaPtg) ptg), sheet, workbook);
}
if (ptg instanceof Area3DPtg) {
Area3DPtg a3dp = (Area3DPtg) ptg;
Sheet xsheet = workbook.getSheetAt(workbook.getSheetIndexFromExternSheetIndex(a3dp.getExternSheetIndex()));
return new LazyAreaEval(a3dp, xsheet, workbook);
} }
return retval;
if (ptg instanceof IntPtg) {
return new NumberEval(((IntPtg)ptg).getValue());
}
if (ptg instanceof NumberPtg) {
return new NumberEval(((NumberPtg)ptg).getValue());
}
if (ptg instanceof StringPtg) {
return new StringEval(((StringPtg) ptg).getValue());
}
if (ptg instanceof BoolPtg) {
return BoolEval.valueOf(((BoolPtg) ptg).getValue());
}
if (ptg instanceof ErrPtg) {
return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
}
throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")");
} }
/** /**
* 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
@ -570,7 +453,7 @@ public class FormulaEvaluator {
* @param sheet * @param sheet
* @param workbook * @param workbook
*/ */
protected static ValueEval getEvalForCell(Cell cell, Row row, Sheet sheet, Workbook workbook) { public static ValueEval getEvalForCell(Cell cell, Sheet sheet, Workbook workbook) {
if (cell == null) { if (cell == null) {
return BlankEval.INSTANCE; return BlankEval.INSTANCE;
@ -593,73 +476,21 @@ public class FormulaEvaluator {
} }
/** /**
* Creates a Ref2DEval for RefPtg. * Mimics the 'data view' of a cell. This allows formula evaluator
* Non existent cells are treated as RefEvals containing BlankEval.
*/
private static Ref2DEval createRef2DEval(RefPtg ptg, Cell cell,
Sheet sheet, Workbook workbook) {
if (cell == null) {
return new Ref2DEval(ptg, BlankEval.INSTANCE);
}
switch (cell.getCellType()) {
case Cell.CELL_TYPE_NUMERIC:
return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()));
case Cell.CELL_TYPE_STRING:
return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
case Cell.CELL_TYPE_FORMULA:
return new Ref2DEval(ptg, internalEvaluate(cell, sheet, workbook));
case Cell.CELL_TYPE_BOOLEAN:
return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
case Cell.CELL_TYPE_BLANK:
return new Ref2DEval(ptg, BlankEval.INSTANCE);
case Cell.CELL_TYPE_ERROR:
return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
}
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
}
/**
* create a Ref3DEval for Ref3DPtg.
*/
private static Ref3DEval createRef3DEval(Ref3DPtg ptg, Cell cell,
Sheet sheet, Workbook workbook) {
if (cell == null) {
return new Ref3DEval(ptg, BlankEval.INSTANCE);
}
switch (cell.getCellType()) {
case Cell.CELL_TYPE_NUMERIC:
return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()));
case Cell.CELL_TYPE_STRING:
return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
case Cell.CELL_TYPE_FORMULA:
return new Ref3DEval(ptg, internalEvaluate(cell, sheet, workbook));
case Cell.CELL_TYPE_BOOLEAN:
return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
case Cell.CELL_TYPE_BLANK:
return new Ref3DEval(ptg, BlankEval.INSTANCE);
case Cell.CELL_TYPE_ERROR:
return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
}
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
}
/**
* Mimics the 'data view' of a cell. This allows formula evaluator
* to return a CellValue instead of precasting the value to String * to return a CellValue instead of precasting the value to String
* or Number or boolean type. * or Number or boolean type.
* @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 static class CellValue { public static class CellValue {
private CreationHelper creationHelper; private CreationHelper creationHelper;
private int cellType; private int cellType;
private RichTextString richTextStringValue; private RichTextString richTextStringValue;
private double numberValue; private double numberValue;
private boolean booleanValue; private boolean booleanValue;
private byte errorValue; private byte errorValue;
/** /**
* CellType should be one of the types defined in HSSFCell * CellType should be one of the types defined in Cell
* @param cellType * @param cellType
*/ */
public CellValue(int cellType, CreationHelper creationHelper) { public CellValue(int cellType, CreationHelper creationHelper) {
@ -705,8 +536,8 @@ public class FormulaEvaluator {
* @deprecated * @deprecated
*/ */
public void setStringValue(String stringValue) { public void setStringValue(String stringValue) {
this.richTextStringValue = this.richTextStringValue =
creationHelper.createRichTextString(stringValue); creationHelper.createRichTextString(stringValue);
} }
/** /**
* @return Returns the cellType. * @return Returns the cellType.

View File

@ -0,0 +1,296 @@
/* ====================================================================
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.util;
import java.util.ArrayList;
import java.util.StringTokenizer;
public class AreaReference {
/** The character (!) that separates sheet names from cell references */
private static final char SHEET_NAME_DELIMITER = '!';
/** The character (:) that separates the two cell references in a multi-cell area reference */
private static final char CELL_DELIMITER = ':';
/** The character (') used to quote sheet names when they contain special characters */
private static final char SPECIAL_NAME_DELIMITER = '\'';
private final CellReference _firstCell;
private final CellReference _lastCell;
private final boolean _isSingleCell;
/**
* Create an area ref from a string representation. Sheet names containing special characters should be
* delimited and escaped as per normal syntax rules for formulas.<br/>
* The area reference must be contiguous (i.e. represent a single rectangle, not a union of rectangles)
*/
public AreaReference(String reference) {
if(! isContiguous(reference)) {
throw new IllegalArgumentException(
"References passed to the AreaReference must be contiguous, " +
"use generateContiguous(ref) if you have non-contiguous references");
}
String[] parts = separateAreaRefs(reference);
// Special handling for whole-column references
if(parts.length == 2 && parts[0].length() == 1 &&
parts[1].length() == 1 &&
parts[0].charAt(0) >= 'A' && parts[0].charAt(0) <= 'Z' &&
parts[1].charAt(0) >= 'A' && parts[1].charAt(0) <= 'Z') {
// Represented internally as x$1 to x$65536
// which is the maximum range of rows
parts[0] = parts[0] + "$1";
parts[1] = parts[1] + "$65536";
}
_firstCell = new CellReference(parts[0]);
if(parts.length == 2) {
_lastCell = new CellReference(parts[1]);
_isSingleCell = false;
} else {
_lastCell = _firstCell;
_isSingleCell = true;
}
}
/**
* Creates an area ref from a pair of Cell References.
*/
public AreaReference(CellReference topLeft, CellReference botRight) {
_firstCell = topLeft;
_lastCell = botRight;
_isSingleCell = false;
}
/**
* Is the reference for a contiguous (i.e.
* unbroken) area, or is it made up of
* several different parts?
* (If it is, you will need to call
* ....
*/
public static boolean isContiguous(String reference) {
if(reference.indexOf(',') == -1) {
return true;
}
return false;
}
/**
* Is the reference for a whole-column reference,
* such as C:C or D:G ?
*/
public static boolean isWholeColumnReference(CellReference topLeft, CellReference botRight) {
// These are represented as something like
// C$1:C$65535 or D$1:F$0
// i.e. absolute from 1st row to 0th one
if(topLeft.getRow() == 0 && topLeft.isRowAbsolute() &&
botRight.getRow() == 65535 && botRight.isRowAbsolute()) {
return true;
}
return false;
}
public boolean isWholeColumnReference() {
return isWholeColumnReference(_firstCell, _lastCell);
}
/**
* Takes a non-contiguous area reference, and
* returns an array of contiguous area references.
*/
public static AreaReference[] generateContiguous(String reference) {
ArrayList refs = new ArrayList();
StringTokenizer st = new StringTokenizer(reference, ",");
while(st.hasMoreTokens()) {
refs.add(
new AreaReference(st.nextToken())
);
}
return (AreaReference[])refs.toArray(new AreaReference[refs.size()]);
}
/**
* @return <code>false</code> if this area reference involves more than one cell
*/
public boolean isSingleCell() {
return _isSingleCell;
}
/**
* @return the first cell reference which defines this area. Usually this cell is in the upper
* left corner of the area (but this is not a requirement).
*/
public CellReference getFirstCell() {
return _firstCell;
}
/**
* Note - if this area reference refers to a single cell, the return value of this method will
* be identical to that of <tt>getFirstCell()</tt>
* @return the second cell reference which defines this area. For multi-cell areas, this is
* cell diagonally opposite the 'first cell'. Usually this cell is in the lower right corner
* of the area (but this is not a requirement).
*/
public CellReference getLastCell() {
return _lastCell;
}
/**
* Returns a reference to every cell covered by this area
*/
public CellReference[] getAllReferencedCells() {
// Special case for single cell reference
if(_isSingleCell) {
return new CellReference[] { _firstCell, };
}
// Interpolate between the two
int minRow = Math.min(_firstCell.getRow(), _lastCell.getRow());
int maxRow = Math.max(_firstCell.getRow(), _lastCell.getRow());
int minCol = Math.min(_firstCell.getCol(), _lastCell.getCol());
int maxCol = Math.max(_firstCell.getCol(), _lastCell.getCol());
String sheetName = _firstCell.getSheetName();
ArrayList refs = new ArrayList();
for(int row=minRow; row<=maxRow; row++) {
for(int col=minCol; col<=maxCol; col++) {
CellReference ref = new CellReference(sheetName, row, col, _firstCell.isRowAbsolute(), _firstCell.isColAbsolute());
refs.add(ref);
}
}
return (CellReference[])refs.toArray(new CellReference[refs.size()]);
}
/**
* Example return values:
* <table border="0" cellpadding="1" cellspacing="0" summary="Example return values">
* <tr><th align='left'>Result</th><th align='left'>Comment</th></tr>
* <tr><td>A1:A1</td><td>Single cell area reference without sheet</td></tr>
* <tr><td>A1:$C$1</td><td>Multi-cell area reference without sheet</td></tr>
* <tr><td>Sheet1!A$1:B4</td><td>Standard sheet name</td></tr>
* <tr><td>'O''Brien''s Sales'!B5:C6'&nbsp;</td><td>Sheet name with special characters</td></tr>
* </table>
* @return the text representation of this area reference as it would appear in a formula.
*/
public String formatAsString() {
// Special handling for whole-column references
if(isWholeColumnReference()) {
return
CellReference.convertNumToColString(_firstCell.getCol())
+ ":" +
CellReference.convertNumToColString(_lastCell.getCol());
}
StringBuffer sb = new StringBuffer(32);
sb.append(_firstCell.formatAsString());
if(!_isSingleCell) {
sb.append(CELL_DELIMITER);
if(_lastCell.getSheetName() == null) {
sb.append(_lastCell.formatAsString());
} else {
// don't want to include the sheet name twice
_lastCell.appendCellReference(sb);
}
}
return sb.toString();
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");
sb.append(formatAsString());
sb.append("]");
return sb.toString();
}
/**
* Separates Area refs in two parts and returns them as separate elements in a String array,
* each qualified with the sheet name (if present)
*
* @return array with one or two elements. never <code>null</code>
*/
private static String[] separateAreaRefs(String reference) {
// TODO - refactor cell reference parsing logic to one place.
// Current known incarnations:
// FormulaParser.GetName()
// CellReference.separateRefParts()
// AreaReference.separateAreaRefs() (here)
// SheetNameFormatter.format() (inverse)
int len = reference.length();
int delimiterPos = -1;
boolean insideDelimitedName = false;
for(int i=0; i<len; i++) {
switch(reference.charAt(i)) {
case CELL_DELIMITER:
if(!insideDelimitedName) {
if(delimiterPos >=0) {
throw new IllegalArgumentException("More than one cell delimiter '"
+ CELL_DELIMITER + "' appears in area reference '" + reference + "'");
}
delimiterPos = i;
}
default:
continue;
case SPECIAL_NAME_DELIMITER:
// fall through
}
if(!insideDelimitedName) {
insideDelimitedName = true;
continue;
}
if(i >= len-1) {
// reference ends with the delimited name.
// Assume names like: "Sheet1!'A1'" are never legal.
throw new IllegalArgumentException("Area reference '" + reference
+ "' ends with special name delimiter '" + SPECIAL_NAME_DELIMITER + "'");
}
if(reference.charAt(i+1) == SPECIAL_NAME_DELIMITER) {
// two consecutive quotes is the escape sequence for a single one
i++; // skip this and keep parsing the special name
} else {
// this is the end of the delimited name
insideDelimitedName = false;
}
}
if(delimiterPos < 0) {
return new String[] { reference, };
}
String partA = reference.substring(0, delimiterPos);
String partB = reference.substring(delimiterPos+1);
if(partB.indexOf(SHEET_NAME_DELIMITER) >=0) {
// TODO - are references like "Sheet1!A1:Sheet1:B2" ever valid?
// FormulaParser has code to handle that.
throw new RuntimeException("Unexpected " + SHEET_NAME_DELIMITER
+ " in second cell reference of '" + reference + "'");
}
int plingPos = partA.lastIndexOf(SHEET_NAME_DELIMITER);
if(plingPos < 0) {
return new String [] { partA, partB, };
}
String sheetName = partA.substring(0, plingPos + 1); // +1 to include delimiter
return new String [] { partA, sheetName + partB, };
}
}

View File

@ -125,7 +125,7 @@ public class PPTXMLDump {
dump(data, pos, size, padding); dump(data, pos, size, padding);
} else { } else {
//dump first 100 bytes of the atom data //dump first 100 bytes of the atom data
dump(out, data, pos, size, padding, true); dump(out, data, pos, Math.min(size, data.length-pos), padding, true);
} }
padding--; padding--;
write(out, "</"+recname + ">" + CR, padding); write(out, "</"+recname + ">" + CR, padding);

View File

@ -34,7 +34,14 @@ import java.util.Iterator;
* @author Yegor Kozlov * @author Yegor Kozlov
*/ */
public class Hyperlink { public class Hyperlink {
public static final byte LINK_NEXTSLIDE = InteractiveInfoAtom.LINK_NextSlide;
public static final byte LINK_PREVIOUSSLIDE = InteractiveInfoAtom.LINK_PreviousSlide;
public static final byte LINK_FIRSTSLIDE = InteractiveInfoAtom.LINK_FirstSlide;
public static final byte LINK_LASTSLIDE = InteractiveInfoAtom.LINK_LastSlide;
public static final byte LINK_URL = InteractiveInfoAtom.LINK_Url;
public static final byte LINK_NULL = InteractiveInfoAtom.LINK_NULL;
private int id=-1;
private int type; private int type;
private String address; private String address;
private String title; private String title;
@ -42,7 +49,7 @@ public class Hyperlink {
/** /**
* Gets the type of the hyperlink action. * Gets the type of the hyperlink action.
* Must be a <code>ACTION_*</code> constant defined in <code>InteractiveInfoAtom</code> * Must be a <code>LINK_*</code> constant</code>
* *
* @return the hyperlink URL * @return the hyperlink URL
* @see InteractiveInfoAtom * @see InteractiveInfoAtom
@ -51,6 +58,32 @@ public class Hyperlink {
return type; return type;
} }
public void setType(int val) {
type = val;
switch(type){
case LINK_NEXTSLIDE:
title = "NEXT";
address = "1,-1,NEXT";
break;
case LINK_PREVIOUSSLIDE:
title = "PREV";
address = "1,-1,PREV";
break;
case LINK_FIRSTSLIDE:
title = "FIRST";
address = "1,-1,FIRST";
break;
case LINK_LASTSLIDE:
title = "LAST";
address = "1,-1,LAST";
break;
default:
title = "";
address = "";
break;
}
}
/** /**
* Gets the hyperlink URL * Gets the hyperlink URL
* *
@ -60,6 +93,18 @@ public class Hyperlink {
return address; return address;
} }
public void setAddress(String str) {
address = str;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
/** /**
* Gets the hyperlink user-friendly title (if different from URL) * Gets the hyperlink user-friendly title (if different from URL)
* *
@ -69,6 +114,10 @@ public class Hyperlink {
return title; return title;
} }
public void setTitle(String str) {
title = str;
}
/** /**
* Gets the beginning character position * Gets the beginning character position
* *

View File

@ -21,6 +21,8 @@ import org.apache.poi.ddf.*;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.hslf.record.ColorSchemeAtom; import org.apache.poi.hslf.record.ColorSchemeAtom;
import org.apache.poi.hslf.record.Record; import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.InteractiveInfo;
import org.apache.poi.hslf.record.InteractiveInfoAtom;
import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.exceptions.HSLFException;
import java.awt.*; import java.awt.*;
@ -343,4 +345,57 @@ public class SimpleShape extends Shape {
_clientData.setRemainingData(out.toByteArray()); _clientData.setRemainingData(out.toByteArray());
} }
} }
public void setHyperlink(Hyperlink link){
if(link.getId() == -1){
throw new HSLFException("You must call SlideShow.addHyperlink(Hyperlink link) first");
}
EscherClientDataRecord cldata = new EscherClientDataRecord();
cldata.setOptions((short)0xF);
getSpContainer().getChildRecords().add(cldata);
InteractiveInfo info = new InteractiveInfo();
InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom();
switch(link.getType()){
case Hyperlink.LINK_FIRSTSLIDE:
infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP);
infoAtom.setJump(InteractiveInfoAtom.JUMP_FIRSTSLIDE);
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_FirstSlide);
break;
case Hyperlink.LINK_LASTSLIDE:
infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP);
infoAtom.setJump(InteractiveInfoAtom.JUMP_LASTSLIDE);
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_LastSlide);
break;
case Hyperlink.LINK_NEXTSLIDE:
infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP);
infoAtom.setJump(InteractiveInfoAtom.JUMP_NEXTSLIDE);
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_NextSlide);
break;
case Hyperlink.LINK_PREVIOUSSLIDE:
infoAtom.setAction(InteractiveInfoAtom.ACTION_JUMP);
infoAtom.setJump(InteractiveInfoAtom.JUMP_PREVIOUSSLIDE);
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_PreviousSlide);
break;
case Hyperlink.LINK_URL:
infoAtom.setAction(InteractiveInfoAtom.ACTION_HYPERLINK);
infoAtom.setJump(InteractiveInfoAtom.JUMP_NONE);
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_Url);
break;
}
infoAtom.setHyperlinkID(link.getId());
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
info.writeOut(out);
} catch(Exception e){
throw new HSLFException(e);
}
cldata.setRemainingData(out.toByteArray());
}
} }

View File

@ -35,19 +35,19 @@ public class CharFlagsTextProp extends BitMaskTextProp {
public static String NAME = "char_flags"; public static String NAME = "char_flags";
public CharFlagsTextProp() { public CharFlagsTextProp() {
super(2,0xffff, NAME, new String[] { super(2,0xffff, NAME, new String[] {
"bold", // 0x0001 "bold", // 0x0001 A bit that specifies whether the characters are bold.
"italic", // 0x0002 "italic", // 0x0002 A bit that specifies whether the characters are italicized.
"underline", // 0x0004 "underline", // 0x0004 A bit that specifies whether the characters are underlined.
"char_unknown_1",// 0x0008 "char_unknown_1", // 0x0008 Undefined and MUST be ignored.
"shadow", // 0x0010 "shadow", // 0x0010 A bit that specifies whether the characters have a shadow effect.
"char_unknown_2",// 0x0020 "fehint", // 0x0020 A bit that specifies whether characters originated from double-byte input.
"char_unknown_3",// 0x0040 "char_unknown_2", // 0x0040 Undefined and MUST be ignored.
"char_unknown_4",// 0x0080 "kumi", // 0x0080 A bit that specifies whether Kumimoji are used for vertical text.
"strikethrough", // 0x0100 "strikethrough", // 0x0100 Undefined and MUST be ignored.
"relief", // 0x0200 "emboss", // 0x0200 A bit that specifies whether the characters are embossed.
"reset_numbering", // 0x0400 "char_unknown_3", // 0x0400 Undefined and MUST be ignored.
"enable_numbering_1", // 0x0800 "char_unknown_4", // 0x0800 Undefined and MUST be ignored.
"enable_numbering_2", // 0x1000 "char_unknown_5", // 0x1000 Undefined and MUST be ignored.
} }
); );
} }

View File

@ -34,6 +34,7 @@ public class TextPropCollection {
private int charactersCovered; private int charactersCovered;
private short reservedField; private short reservedField;
private LinkedList textPropList; private LinkedList textPropList;
private int maskSpecial = 0;
/** Fetch the number of characters this styling applies to */ /** Fetch the number of characters this styling applies to */
public int getCharactersCovered() { return charactersCovered; } public int getCharactersCovered() { return charactersCovered; }
@ -94,21 +95,28 @@ public class TextPropCollection {
// If we do, decode that, save it, and shuffle on // If we do, decode that, save it, and shuffle on
for(int i=0; i<potentialProperties.length; i++) { for(int i=0; i<potentialProperties.length; i++) {
// Check there's still data left to read // Check there's still data left to read
if(dataOffset+bytesPassed >= data.length) {
// Out of data, can't be any more properties to go
return bytesPassed;
}
// Check if this property is found in the mask // Check if this property is found in the mask
if((containsField & potentialProperties[i].getMask()) != 0) { if((containsField & potentialProperties[i].getMask()) != 0) {
if(dataOffset+bytesPassed >= data.length) {
// Out of data, can't be any more properties to go
// remember the mask and return
maskSpecial |= potentialProperties[i].getMask();
return bytesPassed;
}
// Bingo, data contains this property // Bingo, data contains this property
TextProp prop = (TextProp)potentialProperties[i].clone(); TextProp prop = (TextProp)potentialProperties[i].clone();
int val = 0; int val = 0;
if(prop.getSize() == 2) { if(prop.getSize() == 2) {
val = LittleEndian.getShort(data,dataOffset+bytesPassed); val = LittleEndian.getShort(data,dataOffset+bytesPassed);
} else { } else if(prop.getSize() == 4){
val = LittleEndian.getInt(data,dataOffset+bytesPassed); val = LittleEndian.getInt(data,dataOffset+bytesPassed);
} } else if (prop.getSize() == 0){
//remember "special" bits.
maskSpecial |= potentialProperties[i].getMask();
continue;
}
prop.setValue(val); prop.setValue(val);
bytesPassed += prop.getSize(); bytesPassed += prop.getSize();
textPropList.add(prop); textPropList.add(prop);
@ -161,15 +169,18 @@ public class TextPropCollection {
} }
// Then the mask field // Then the mask field
int mask = 0; int mask = maskSpecial;
for(int i=0; i<textPropList.size(); i++) { for(int i=0; i<textPropList.size(); i++) {
TextProp textProp = (TextProp)textPropList.get(i); TextProp textProp = (TextProp)textPropList.get(i);
//sometimes header indicates that the bitmask is present but its value is 0 //sometimes header indicates that the bitmask is present but its value is 0
if (textProp instanceof BitMaskTextProp)
mask |= (textProp.getWriteMask() == 0 ? 1 : textProp.getWriteMask()); if (textProp instanceof BitMaskTextProp) {
else if(mask == 0) mask |= textProp.getWriteMask();
}
else {
mask |= textProp.getWriteMask(); mask |= textProp.getWriteMask();
} }
}
StyleTextPropAtom.writeLittleEndian(mask,o); StyleTextPropAtom.writeLittleEndian(mask,o);
// Then the contents of all the properties // Then the contents of all the properties
@ -178,7 +189,7 @@ public class TextPropCollection {
int val = textProp.getValue(); int val = textProp.getValue();
if(textProp.getSize() == 2) { if(textProp.getSize() == 2) {
StyleTextPropAtom.writeLittleEndian((short)val,o); StyleTextPropAtom.writeLittleEndian((short)val,o);
} else { } else if(textProp.getSize() == 4){
StyleTextPropAtom.writeLittleEndian(val,o); StyleTextPropAtom.writeLittleEndian(val,o);
} }
} }

View File

@ -63,14 +63,16 @@ public class ExHyperlink extends RecordContainer {
* TODO: Figure out if we should always set both * TODO: Figure out if we should always set both
*/ */
public void setLinkURL(String url) { public void setLinkURL(String url) {
linkDetailsA.setText(url);
// linkDetailsB isn't present in all PPT versions
if(linkDetailsB != null) { if(linkDetailsB != null) {
linkDetailsB.setText(url); linkDetailsB.setText(url);
} }
} }
public void setLinkTitle(String title) {
if(linkDetailsA != null) {
linkDetailsA.setText(title);
}
}
/** /**
* Get the link details (field A) * Get the link details (field A)
*/ */

View File

@ -124,47 +124,54 @@ public class StyleTextPropAtom extends RecordAtom
/** All the different kinds of paragraph properties we might handle */ /** All the different kinds of paragraph properties we might handle */
public static TextProp[] paragraphTextPropTypes = new TextProp[] { public static TextProp[] paragraphTextPropTypes = new TextProp[] {
new ParagraphFlagsTextProp(), new TextProp(0, 0x1, "hasBullet"),
new TextProp(0, 0x2, "hasBulletFont"),
new TextProp(0, 0x4, "hasBulletColor"),
new TextProp(0, 0x8, "hasBulletSize"),
new ParagraphFlagsTextProp(),
new TextProp(2, 0x80, "bullet.char"), new TextProp(2, 0x80, "bullet.char"),
new TextProp(2, 0x10, "bullet.font"), new TextProp(2, 0x10, "bullet.font"),
new TextProp(2, 0x40, "bullet.size"), new TextProp(2, 0x40, "bullet.size"),
new TextProp(4, 0x20, "bullet.color"), new TextProp(4, 0x20, "bullet.color"),
new AlignmentTextProp(), new AlignmentTextProp(),
new TextProp(2, 0x100, "text.offset"), new TextProp(2, 0x100, "text.offset"),
new TextProp(2, 0x200, "para_unknown_2"),
new TextProp(2, 0x400, "bullet.offset"), new TextProp(2, 0x400, "bullet.offset"),
new TextProp(2, 0x1000, "linespacing"), new TextProp(2, 0x1000, "linespacing"),
new TextProp(2, 0x2000, "spacebefore"), new TextProp(2, 0x2000, "spacebefore"),
new TextProp(2, 0x4000, "spaceafter"), new TextProp(2, 0x4000, "spaceafter"),
new TextProp(2, 0x8000, "para_unknown_4"), new TextProp(2, 0x8000, "defaultTabSize"),
new TextProp(2, 0x10000, "para_unknown_5"), new TextProp(2, 0x100000, "tabStops"),
new TextProp(2, 0xA0000, "para_unknown_6"), new TextProp(2, 0x10000, "fontAlign"),
new TextProp(2, 0x200000, "para_unknown_7") new TextProp(2, 0xA0000, "wrapFlags"),
new TextProp(2, 0x200000, "textDirection")
}; };
/** All the different kinds of character properties we might handle */ /** All the different kinds of character properties we might handle */
public static TextProp[] characterTextPropTypes = new TextProp[] { public static TextProp[] characterTextPropTypes = new TextProp[] {
new CharFlagsTextProp(), new TextProp(0, 0x1, "bold"),
new TextProp(0, 0x2, "italic"),
new TextProp(0, 0x4, "underline"),
new TextProp(0, 0x8, "unused1"),
new TextProp(0, 0x10, "shadow"),
new TextProp(0, 0x20, "fehint"),
new TextProp(0, 0x40, "unused2"),
new TextProp(0, 0x80, "kumi"),
new TextProp(0, 0x100, "unused3"),
new TextProp(0, 0x200, "emboss"),
new CharFlagsTextProp(),
new TextProp(2, 0x10000, "font.index"), new TextProp(2, 0x10000, "font.index"),
new TextProp(2, 0x200000, "asian_or_complex"), new TextProp(0, 0x100000, "pp10ext"),
new TextProp(2, 0x400000, "char_unknown_2"), new TextProp(2, 0x200000, "asian.font.index"),
new TextProp(2, 0x800000, "symbol"), new TextProp(2, 0x400000, "ansi.font.index"),
new TextProp(2, 0x800000, "symbol.font.index"),
new TextProp(2, 0x20000, "font.size"), new TextProp(2, 0x20000, "font.size"),
new TextProp(4, 0x40000, "font.color"), new TextProp(4, 0x40000, "font.color"),
new TextProp(2, 0x80000, "superscript"), new TextProp(2, 0x80000, "superscript"),
new TextProp(2, 0x100000, "char_unknown_1"),
new TextProp(2, 0x1000000, "char_unknown_3"), };
new TextProp(2, 0x2000000, "char_unknown_4"),
new TextProp(2, 0x4000000, "char_unknown_5"),
new TextProp(2, 0x8000000, "char_unknown_6"),
new TextProp(2, 0x10000000, "char_unknown_7"),
new TextProp(2, 0x20000000, "char_unknown_8"),
new TextProp(2, 0x40000000, "char_unknown_9"),
new TextProp(2, 0x80000000, "char_unknown_10"),
};
/* *************** record code follows ********************** */ /* *************** record code follows ********************** */
/** /**
* For the Text Style Properties (StyleTextProp) Atom * For the Text Style Properties (StyleTextProp) Atom
*/ */
public StyleTextPropAtom(byte[] source, int start, int len) { public StyleTextPropAtom(byte[] source, int start, int len) {
@ -192,7 +199,7 @@ public class StyleTextPropAtom extends RecordAtom
} }
/** /**
* A new set of text style properties for some text without any. * A new set of text style properties for some text without any.
*/ */
public StyleTextPropAtom(int parentTextSize) { public StyleTextPropAtom(int parentTextSize) {
@ -209,11 +216,11 @@ public class StyleTextPropAtom extends RecordAtom
paragraphStyles = new LinkedList(); paragraphStyles = new LinkedList();
charStyles = new LinkedList(); charStyles = new LinkedList();
TextPropCollection defaultParagraphTextProps = TextPropCollection defaultParagraphTextProps =
new TextPropCollection(parentTextSize, (short)0); new TextPropCollection(parentTextSize, (short)0);
paragraphStyles.add(defaultParagraphTextProps); paragraphStyles.add(defaultParagraphTextProps);
TextPropCollection defaultCharacterTextProps = TextPropCollection defaultCharacterTextProps =
new TextPropCollection(parentTextSize); new TextPropCollection(parentTextSize);
charStyles.add(defaultCharacterTextProps); charStyles.add(defaultCharacterTextProps);
@ -269,8 +276,7 @@ public class StyleTextPropAtom extends RecordAtom
textHandled += textLen; textHandled += textLen;
pos += 4; pos += 4;
// Fetch the 2 byte value that is safe to ignore as 0 short indent = LittleEndian.getShort(rawContents,pos);
short paraIgn = LittleEndian.getShort(rawContents,pos);
pos += 2; pos += 2;
// Grab the 4 byte value that tells us what properties follow // Grab the 4 byte value that tells us what properties follow
@ -278,7 +284,7 @@ public class StyleTextPropAtom extends RecordAtom
pos += 4; pos += 4;
// Now make sense of those properties // Now make sense of those properties
TextPropCollection thisCollection = new TextPropCollection(textLen, paraIgn); TextPropCollection thisCollection = new TextPropCollection(textLen, indent);
int plSize = thisCollection.buildTextPropList( int plSize = thisCollection.buildTextPropList(
paraFlags, paragraphTextPropTypes, rawContents, pos); paraFlags, paragraphTextPropTypes, rawContents, pos);
pos += plSize; pos += plSize;

View File

@ -920,7 +920,7 @@ public final class SlideShow {
* @return 0-based index of the control * @return 0-based index of the control
*/ */
public int addControl(String name, String progId) { public int addControl(String name, String progId) {
ExObjList lst = _documentRecord.getExObjList(); ExObjList lst = (ExObjList)_documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID);
if (lst == null) { if (lst == null) {
lst = new ExObjList(); lst = new ExObjList();
_documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom()); _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
@ -943,4 +943,31 @@ public final class SlideShow {
return objectId; return objectId;
} }
/**
* Add a hyperlink to this presentation
*
* @return 0-based index of the hyperlink
*/
public int addHyperlink(Hyperlink link) {
ExObjList lst = (ExObjList)_documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID);
if (lst == null) {
lst = new ExObjList();
_documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom());
}
ExObjListAtom objAtom = lst.getExObjListAtom();
//increment the object ID seed
int objectId = (int) objAtom.getObjectIDSeed() + 1;
objAtom.setObjectIDSeed(objectId);
ExHyperlink ctrl = new ExHyperlink();
ExHyperlinkAtom obj = ctrl.getExHyperlinkAtom();
obj.setNumber(objectId);
ctrl.setLinkURL(link.getAddress());
ctrl.setLinkTitle(link.getTitle());
lst.addChildAfter(ctrl, objAtom);
link.setId(objectId);
return objectId;
}
} }

View File

@ -760,12 +760,8 @@ public final class HSSFChart {
r.setIndexNumberFmtRecord( (short) 0 ); r.setIndexNumberFmtRecord( (short) 0 );
LinkedDataFormulaField formula = new LinkedDataFormulaField(); LinkedDataFormulaField formula = new LinkedDataFormulaField();
Stack tokens = new Stack(); Stack tokens = new Stack();
Area3DPtg p = new Area3DPtg(); Area3DPtg p = new Area3DPtg(0, 31, 1, 1,
p.setExternSheetIndex( (short) 0 ); false, false, false, false, 0);
p.setFirstColumn( (short) 1 );
p.setLastColumn( (short) 1 );
p.setFirstRow( (short) 0 );
p.setLastRow( (short) 31 );
tokens.add( p ); tokens.add( p );
formula.setFormulaTokens( tokens ); formula.setFormulaTokens( tokens );
r.setFormulaOfLink( formula ); r.setFormulaOfLink( formula );
@ -781,12 +777,8 @@ public final class HSSFChart {
r.setIndexNumberFmtRecord( (short) 0 ); r.setIndexNumberFmtRecord( (short) 0 );
LinkedDataFormulaField formula = new LinkedDataFormulaField(); LinkedDataFormulaField formula = new LinkedDataFormulaField();
Stack tokens = new Stack(); Stack tokens = new Stack();
Area3DPtg p = new Area3DPtg(); Area3DPtg p = new Area3DPtg(0, 31, 0, 0,
p.setExternSheetIndex( (short) 0 ); false, false, false, false, 0);
p.setFirstColumn( (short) 0 );
p.setLastColumn( (short) 0 );
p.setFirstRow( (short) 0 );
p.setLastRow( (short) 31 );
tokens.add( p ); tokens.add( p );
formula.setFormulaTokens( tokens ); formula.setFormulaTokens( tokens );
r.setFormulaOfLink( formula ); r.setFormulaOfLink( formula );

View File

@ -24,6 +24,7 @@ import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.model.FormulaParser.FormulaParseException; import org.apache.poi.hssf.model.FormulaParser.FormulaParseException;
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg; import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
import org.apache.poi.hssf.record.formula.AddPtg; import org.apache.poi.hssf.record.formula.AddPtg;
import org.apache.poi.hssf.record.formula.AreaI;
import org.apache.poi.hssf.record.formula.AreaPtg; import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.AttrPtg; import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.BoolPtg; import org.apache.poi.hssf.record.formula.BoolPtg;
@ -836,4 +837,29 @@ public final class TestFormulaParser extends TestCase {
} }
cell.setCellFormula("count(fp1)"); // plain cell ref, col is in range cell.setCellFormula("count(fp1)"); // plain cell ref, col is in range
} }
}
public void testParseAreaRefHighRow_bug45358() {
Ptg[] ptgs;
AreaI aptg;
HSSFWorkbook book = new HSSFWorkbook();
book.createSheet("Sheet1");
ptgs = FormulaParser.parse("Sheet1!A10:A40000", book);
aptg = (AreaI) ptgs[0];
if (aptg.getLastRow() == -25537) {
throw new AssertionFailedError("Identified bug 45358");
}
assertEquals(39999, aptg.getLastRow());
ptgs = FormulaParser.parse("Sheet1!A10:A65536", book);
aptg = (AreaI) ptgs[0];
assertEquals(65535, aptg.getLastRow());
// plain area refs should be ok too
ptgs = parseFormula("A10:A65536");
aptg = (AreaI) ptgs[0];
assertEquals(65535, aptg.getLastRow());
}
}

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
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
@ -15,8 +14,6 @@
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; package org.apache.poi.hssf.record;
@ -33,9 +30,7 @@ import java.util.Stack;
* *
* @author Glen Stampoultzis (glens at apache.org) * @author Glen Stampoultzis (glens at apache.org)
*/ */
public class TestLinkedDataRecord public final class TestLinkedDataRecord extends TestCase {
extends TestCase
{
/* /*
The records below are records that would appear in a simple bar chart The records below are records that would appear in a simple bar chart
@ -160,14 +155,7 @@ recordid = 0x1051, size =8
(byte)0x00,(byte)0x00, // index to last column and relative flags (byte)0x00,(byte)0x00, // index to last column and relative flags
}; };
public TestLinkedDataRecord(String name) public void testLoad() {
{
super(name);
}
public void testLoad()
throws Exception
{
LinkedDataRecord record = new LinkedDataRecord(new TestcaseRecordInputStream((short)0x1051, (short)data.length, data)); LinkedDataRecord record = new LinkedDataRecord(new TestcaseRecordInputStream((short)0x1051, (short)data.length, data));
assertEquals( LinkedDataRecord.LINK_TYPE_VALUES, record.getLinkType()); assertEquals( LinkedDataRecord.LINK_TYPE_VALUES, record.getLinkType());
@ -176,19 +164,11 @@ recordid = 0x1051, size =8
assertEquals( false, record.isCustomNumberFormat() ); assertEquals( false, record.isCustomNumberFormat() );
assertEquals( 0, record.getIndexNumberFmtRecord()); assertEquals( 0, record.getIndexNumberFmtRecord());
Area3DPtg ptg = new Area3DPtg(); Area3DPtg ptgExpected = new Area3DPtg(0, 7936, 0, 0,
ptg.setExternSheetIndex((short)0); false, false, false, false, 0);
ptg.setFirstColumn((short)0);
ptg.setLastColumn((short)0); Object ptgActual = record.getFormulaOfLink().getFormulaTokens().get(0);
ptg.setFirstRow((short)0); assertEquals(ptgExpected.toString(), ptgActual.toString());
ptg.setLastRow((short)7936);
ptg.setFirstColRelative(false);
ptg.setLastColRelative(false);
ptg.setFirstRowRelative(false);
ptg.setLastRowRelative(false);
Stack s = new Stack();
s.push(ptg);
assertEquals( s, record.getFormulaOfLink().getFormulaTokens() );
assertEquals( data.length + 4, record.getRecordSize() ); assertEquals( data.length + 4, record.getRecordSize() );
@ -196,24 +176,15 @@ recordid = 0x1051, size =8
} }
public void testStore() public void testStore() {
{
LinkedDataRecord record = new LinkedDataRecord(); LinkedDataRecord record = new LinkedDataRecord();
record.setLinkType( LinkedDataRecord.LINK_TYPE_VALUES ); record.setLinkType( LinkedDataRecord.LINK_TYPE_VALUES );
record.setReferenceType( LinkedDataRecord.REFERENCE_TYPE_WORKSHEET ); record.setReferenceType( LinkedDataRecord.REFERENCE_TYPE_WORKSHEET );
record.setOptions( (short)0 ); record.setOptions( (short)0 );
record.setCustomNumberFormat( false ); record.setCustomNumberFormat( false );
record.setIndexNumberFmtRecord( (short)0 ); record.setIndexNumberFmtRecord( (short)0 );
Area3DPtg ptg = new Area3DPtg(); Area3DPtg ptg = new Area3DPtg(0, 7936, 0, 0,
ptg.setExternSheetIndex((short)0); false, false, false, false, 0);
ptg.setFirstColumn((short)0);
ptg.setLastColumn((short)0);
ptg.setFirstRow((short)0);
ptg.setLastRow((short)7936);
ptg.setFirstColRelative(false);
ptg.setLastColRelative(false);
ptg.setFirstRowRelative(false);
ptg.setLastRowRelative(false);
Stack s = new Stack(); Stack s = new Stack();
s.push(ptg); s.push(ptg);
LinkedDataFormulaField formulaOfLink = new LinkedDataFormulaField(); LinkedDataFormulaField formulaOfLink = new LinkedDataFormulaField();

View File

@ -20,7 +20,8 @@ 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.Area3DPtg; import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.functions.EvalFactory;
/** /**
* Tests for <tt>AreaEval</tt> * Tests for <tt>AreaEval</tt>
@ -30,8 +31,8 @@ import org.apache.poi.hssf.record.formula.Area3DPtg;
public final class TestAreaEval extends TestCase { public final class TestAreaEval extends TestCase {
public void testGetValue_bug44950() { public void testGetValue_bug44950() {
// TODO - this test probably isn't testing much anymore
Area3DPtg ptg = new Area3DPtg("B2:D3", (short)0); AreaPtg ptg = new AreaPtg("B2:D3");
NumberEval one = new NumberEval(1); NumberEval one = new NumberEval(1);
ValueEval[] values = { ValueEval[] values = {
one, one,
@ -41,7 +42,7 @@ public final class TestAreaEval extends TestCase {
new NumberEval(5), new NumberEval(5),
new NumberEval(6), new NumberEval(6),
}; };
AreaEval ae = new Area3DEval(ptg, values); AreaEval ae = EvalFactory.createAreaEval(ptg, values);
if (one == ae.getValueAt(1, 2)) { if (one == ae.getValueAt(1, 2)) {
throw new AssertionFailedError("Identified bug 44950 a"); throw new AssertionFailedError("Identified bug 44950 a");
} }

View File

@ -18,22 +18,23 @@
package org.apache.poi.hssf.record.formula.eval; package org.apache.poi.hssf.record.formula.eval;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AreaPtg; import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.UnaryPlusPtg; import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
import org.apache.poi.hssf.record.formula.functions.EvalFactory;
import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker; import org.apache.poi.hssf.record.formula.functions.NumericFunctionInvoker;
import junit.framework.TestCase;
/** /**
* Test for unary plus operator evaluator. * Test for unary plus operator evaluator.
* *
* @author Josh Micich * @author Josh Micich
*/ */
public final class TestUnaryPlusEval extends TestCase { public final class TestUnaryPlusEval extends TestCase {
/** /**
* Test for bug observable at svn revision 618865 (5-Feb-2008)<br/> * Test for bug observable at svn revision 618865 (5-Feb-2008)<br/>
* The code for handling column operands had been copy-pasted from the row handling code. * The code for handling column operands had been copy-pasted from the row handling code.
*/ */
public void testColumnOperand() { public void testColumnOperand() {
@ -42,15 +43,14 @@ public final class TestUnaryPlusEval extends TestCase {
short colNum = (short)5; short colNum = (short)5;
AreaPtg areaPtg = new AreaPtg(firstRow, lastRow, colNum, colNum, false, false, false, false); AreaPtg areaPtg = new AreaPtg(firstRow, lastRow, colNum, colNum, false, false, false, false);
ValueEval[] values = { ValueEval[] values = {
new NumberEval(27), new NumberEval(27),
new NumberEval(29), new NumberEval(29),
new NumberEval(35), // value in row 10 new NumberEval(35), // value in row 10
new NumberEval(37), new NumberEval(37),
new NumberEval(38), new NumberEval(38),
}; };
Eval areaEval = new Area2DEval(areaPtg, values); Eval[] args = {
Eval[] args = { EvalFactory.createAreaEval(areaPtg, values),
areaEval,
}; };
double result = NumericFunctionInvoker.invoke(new UnaryPlusEval(UnaryPlusPtg.instance), args, 10, (short)20); double result = NumericFunctionInvoker.invoke(new UnaryPlusEval(UnaryPlusPtg.instance), args, 10, (short)20);

View File

@ -14,17 +14,17 @@
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.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.AreaPtg; import org.apache.poi.hssf.record.formula.AreaPtg;
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.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.AreaEvalBase;
import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.RefEvalBase;
import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
@ -32,7 +32,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
* *
* @author Josh Micich * @author Josh Micich
*/ */
final class EvalFactory { public final class EvalFactory {
private EvalFactory() { private EvalFactory() {
// no instances of this class // no instances of this class
@ -44,6 +44,14 @@ final class EvalFactory {
*/ */
public static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) { public static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
AreaPtg areaPtg = new AreaPtg(areaRefStr); AreaPtg areaPtg = new AreaPtg(areaRefStr);
return createAreaEval(areaPtg, values);
}
/**
* Creates a dummy AreaEval
* @param values empty (<code>null</code>) entries in this array will be converted to NumberEval.ZERO
*/
public static AreaEval createAreaEval(AreaPtg areaPtg, ValueEval[] values) {
int nCols = areaPtg.getLastColumn() - areaPtg.getFirstColumn() + 1; int nCols = areaPtg.getLastColumn() - areaPtg.getFirstColumn() + 1;
int nRows = areaPtg.getLastRow() - areaPtg.getFirstRow() + 1; int nRows = areaPtg.getLastRow() - areaPtg.getFirstRow() + 1;
int nExpected = nRows * nCols; int nExpected = nRows * nCols;
@ -55,13 +63,57 @@ final class EvalFactory {
values[i] = NumberEval.ZERO; values[i] = NumberEval.ZERO;
} }
} }
return new Area2DEval(areaPtg, values); return new MockAreaEval(areaPtg, values);
} }
/** /**
* Creates a single RefEval (with value zero) * Creates a single RefEval (with value zero)
*/ */
public static RefEval createRefEval(String refStr) { public static RefEval createRefEval(String refStr) {
return new Ref2DEval(new RefPtg(refStr), NumberEval.ZERO); return createRefEval(refStr, NumberEval.ZERO);
} }
public static RefEval createRefEval(String refStr, ValueEval value) {
return new MockRefEval(new RefPtg(refStr), value);
}
private static final class MockAreaEval extends AreaEvalBase {
private final ValueEval[] _values;
public MockAreaEval(AreaPtg areaPtg, ValueEval[] values) {
super(areaPtg);
_values = values;
}
public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
if (relativeRowIndex < 0 || relativeRowIndex >=getHeight()) {
throw new IllegalArgumentException("row index out of range");
}
int width = getWidth();
if (relativeColumnIndex < 0 || relativeColumnIndex >=width) {
throw new IllegalArgumentException("column index out of range");
}
int oneDimensionalIndex = relativeRowIndex * width + relativeColumnIndex;
return _values[oneDimensionalIndex];
}
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
throw new RuntimeException("Operation not implemented on this mock object");
}
}
private static final class MockRefEval extends RefEvalBase {
private final ValueEval _value;
public MockRefEval(RefPtg ptg, ValueEval value) {
super(ptg.getRow(), ptg.getColumn());
_value = value;
}
public MockRefEval(Ref3DPtg ptg, ValueEval value) {
super(ptg.getRow(), ptg.getColumn());
_value = value;
}
public ValueEval getInnerValueEval() {
return _value;
}
public AreaEval offset(int relFirstRowIx, int relLastRowIx, int relFirstColIx, int relLastColIx) {
throw new RuntimeException("Operation not implemented on this mock object");
}
}
} }

View File

@ -21,9 +21,7 @@ import junit.framework.AssertionFailedError;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.RefPtg; import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval; import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.BoolEval;
@ -91,7 +89,7 @@ public final class TestCountFuncs extends TestCase {
BoolEval.TRUE, BoolEval.TRUE,
BlankEval.INSTANCE, BlankEval.INSTANCE,
}; };
range = createAreaEval("A1:B3", values); range = EvalFactory.createAreaEval("A1:B3", values);
confirmCountIf(2, range, BoolEval.TRUE); confirmCountIf(2, range, BoolEval.TRUE);
// when criteria is numeric // when criteria is numeric
@ -103,7 +101,7 @@ public final class TestCountFuncs extends TestCase {
new NumberEval(2), new NumberEval(2),
BoolEval.TRUE, BoolEval.TRUE,
}; };
range = createAreaEval("A1:B3", values); range = EvalFactory.createAreaEval("A1:B3", values);
confirmCountIf(3, range, new NumberEval(2)); confirmCountIf(3, range, new NumberEval(2));
// note - same results when criteria is a string that parses as the number with the same value // note - same results when criteria is a string that parses as the number with the same value
confirmCountIf(3, range, new StringEval("2.00")); confirmCountIf(3, range, new StringEval("2.00"));
@ -126,20 +124,15 @@ public final class TestCountFuncs extends TestCase {
new NumberEval(25), new NumberEval(25),
new NumberEval(25), new NumberEval(25),
}; };
Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values); AreaEval arg0 = EvalFactory.createAreaEval("C1:C6", values);
Ref2DEval criteriaArg = new Ref2DEval(new RefPtg("A1"), new NumberEval(25)); ValueEval criteriaArg = EvalFactory.createRefEval("A1", new NumberEval(25));
Eval[] args= { arg0, criteriaArg, }; Eval[] args= { arg0, criteriaArg, };
double actual = NumericFunctionInvoker.invoke(new Countif(), args); double actual = NumericFunctionInvoker.invoke(new Countif(), args);
assertEquals(4, actual, 0D); assertEquals(4, actual, 0D);
} }
private static AreaEval createAreaEval(String areaRefStr, ValueEval[] values) {
return new Area2DEval(new AreaPtg(areaRefStr), values);
}
private static void confirmCountA(int expected, Eval[] args) { private static void confirmCountA(int expected, Eval[] args) {
double result = NumericFunctionInvoker.invoke(new Counta(), args); double result = NumericFunctionInvoker.invoke(new Counta(), args);
assertEquals(expected, result, 0); assertEquals(expected, result, 0);

View File

@ -19,8 +19,7 @@ package org.apache.poi.hssf.record.formula.functions;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AreaPtg; import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
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.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval;
@ -68,7 +67,7 @@ public final class TestIndex extends TestCase {
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
values[i] = new NumberEval(dValues[i]); values[i] = new NumberEval(dValues[i]);
} }
Area2DEval arg0 = new Area2DEval(new AreaPtg(areaRefString), values); AreaEval arg0 = EvalFactory.createAreaEval(areaRefString, values);
Eval[] args; Eval[] args;
if (colNum > 0) { if (colNum > 0) {

View File

@ -19,8 +19,6 @@ package org.apache.poi.hssf.record.formula.functions;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval; 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.ErrorEval;
@ -32,7 +30,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* Test cases for MATCH() * Test cases for MATCH()
* *
* @author Josh Micich * @author Josh Micich
*/ */
public final class TestMatch extends TestCase { public final class TestMatch extends TestCase {
@ -41,8 +39,8 @@ public final class TestMatch extends TestCase {
private static final NumberEval MATCH_EXACT = new NumberEval(0); private static final NumberEval MATCH_EXACT = new NumberEval(0);
/** greater than or equal to */ /** greater than or equal to */
private static final NumberEval MATCH_SMALLEST_GTE = new NumberEval(-1); private static final NumberEval MATCH_SMALLEST_GTE = new NumberEval(-1);
private static Eval invokeMatch(Eval lookup_value, Eval lookup_array, Eval match_type) { private static Eval invokeMatch(Eval lookup_value, Eval lookup_array, Eval match_type) {
Eval[] args = { lookup_value, lookup_array, match_type, }; Eval[] args = { lookup_value, lookup_array, match_type, };
return new Match().evaluate(args, -1, (short)-1); return new Match().evaluate(args, -1, (short)-1);
@ -54,26 +52,19 @@ public final class TestMatch extends TestCase {
NumericValueEval nve = (NumericValueEval)actualEval; NumericValueEval nve = (NumericValueEval)actualEval;
assertEquals(expected, nve.getNumberValue(), 0); assertEquals(expected, nve.getNumberValue(), 0);
} }
/**
* Convenience method
* @return <code>new Area2DEval(new AreaPtg(ref), values)</code>
*/
private static AreaEval createAreaEval(String ref, ValueEval[] values) {
return new Area2DEval(new AreaPtg(ref), values);
}
public void testSimpleNumber() { public void testSimpleNumber() {
ValueEval[] values = { ValueEval[] values = {
new NumberEval(4), new NumberEval(4),
new NumberEval(5), new NumberEval(5),
new NumberEval(10), new NumberEval(10),
new NumberEval(10), new NumberEval(10),
new NumberEval(25), new NumberEval(25),
}; };
AreaEval ae = createAreaEval("A1:A5", values); AreaEval ae = EvalFactory.createAreaEval("A1:A5", values);
confirmInt(2, invokeMatch(new NumberEval(5), ae, MATCH_LARGEST_LTE)); confirmInt(2, invokeMatch(new NumberEval(5), ae, MATCH_LARGEST_LTE));
confirmInt(2, invokeMatch(new NumberEval(5), ae, MATCH_EXACT)); confirmInt(2, invokeMatch(new NumberEval(5), ae, MATCH_EXACT));
confirmInt(4, invokeMatch(new NumberEval(10), ae, MATCH_LARGEST_LTE)); confirmInt(4, invokeMatch(new NumberEval(10), ae, MATCH_LARGEST_LTE));
@ -81,19 +72,19 @@ public final class TestMatch extends TestCase {
confirmInt(4, invokeMatch(new NumberEval(20), ae, MATCH_LARGEST_LTE)); confirmInt(4, invokeMatch(new NumberEval(20), ae, MATCH_LARGEST_LTE));
assertEquals(ErrorEval.NA, invokeMatch(new NumberEval(20), ae, MATCH_EXACT)); assertEquals(ErrorEval.NA, invokeMatch(new NumberEval(20), ae, MATCH_EXACT));
} }
public void testReversedNumber() { public void testReversedNumber() {
ValueEval[] values = { ValueEval[] values = {
new NumberEval(25), new NumberEval(25),
new NumberEval(10), new NumberEval(10),
new NumberEval(10), new NumberEval(10),
new NumberEval(10), new NumberEval(10),
new NumberEval(4), new NumberEval(4),
}; };
AreaEval ae = createAreaEval("A1:A5", values); AreaEval ae = EvalFactory.createAreaEval("A1:A5", values);
confirmInt(2, invokeMatch(new NumberEval(10), ae, MATCH_SMALLEST_GTE)); confirmInt(2, invokeMatch(new NumberEval(10), ae, MATCH_SMALLEST_GTE));
confirmInt(2, invokeMatch(new NumberEval(10), ae, MATCH_EXACT)); confirmInt(2, invokeMatch(new NumberEval(10), ae, MATCH_EXACT));
confirmInt(4, invokeMatch(new NumberEval(9), ae, MATCH_SMALLEST_GTE)); confirmInt(4, invokeMatch(new NumberEval(9), ae, MATCH_SMALLEST_GTE));
@ -101,19 +92,19 @@ public final class TestMatch extends TestCase {
assertEquals(ErrorEval.NA, invokeMatch(new NumberEval(20), ae, MATCH_EXACT)); assertEquals(ErrorEval.NA, invokeMatch(new NumberEval(20), ae, MATCH_EXACT));
assertEquals(ErrorEval.NA, invokeMatch(new NumberEval(26), ae, MATCH_SMALLEST_GTE)); assertEquals(ErrorEval.NA, invokeMatch(new NumberEval(26), ae, MATCH_SMALLEST_GTE));
} }
public void testSimpleString() { public void testSimpleString() {
ValueEval[] values = { ValueEval[] values = {
new StringEval("Albert"), new StringEval("Albert"),
new StringEval("Charles"), new StringEval("Charles"),
new StringEval("Ed"), new StringEval("Ed"),
new StringEval("Greg"), new StringEval("Greg"),
new StringEval("Ian"), new StringEval("Ian"),
}; };
AreaEval ae = createAreaEval("A1:A5", values); AreaEval ae = EvalFactory.createAreaEval("A1:A5", values);
// Note String comparisons are case insensitive // Note String comparisons are case insensitive
confirmInt(3, invokeMatch(new StringEval("Ed"), ae, MATCH_LARGEST_LTE)); confirmInt(3, invokeMatch(new StringEval("Ed"), ae, MATCH_LARGEST_LTE));
confirmInt(3, invokeMatch(new StringEval("eD"), ae, MATCH_LARGEST_LTE)); confirmInt(3, invokeMatch(new StringEval("eD"), ae, MATCH_LARGEST_LTE));
@ -122,90 +113,90 @@ public final class TestMatch extends TestCase {
confirmInt(4, invokeMatch(new StringEval("Hugh"), ae, MATCH_LARGEST_LTE)); confirmInt(4, invokeMatch(new StringEval("Hugh"), ae, MATCH_LARGEST_LTE));
assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Hugh"), ae, MATCH_EXACT)); assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Hugh"), ae, MATCH_EXACT));
} }
public void testSimpleBoolean() { public void testSimpleBoolean() {
ValueEval[] values = { ValueEval[] values = {
BoolEval.FALSE, BoolEval.FALSE,
BoolEval.FALSE, BoolEval.FALSE,
BoolEval.TRUE, BoolEval.TRUE,
BoolEval.TRUE, BoolEval.TRUE,
}; };
AreaEval ae = createAreaEval("A1:A4", values); AreaEval ae = EvalFactory.createAreaEval("A1:A4", values);
// Note String comparisons are case insensitive // Note String comparisons are case insensitive
confirmInt(2, invokeMatch(BoolEval.FALSE, ae, MATCH_LARGEST_LTE)); confirmInt(2, invokeMatch(BoolEval.FALSE, ae, MATCH_LARGEST_LTE));
confirmInt(1, invokeMatch(BoolEval.FALSE, ae, MATCH_EXACT)); confirmInt(1, invokeMatch(BoolEval.FALSE, ae, MATCH_EXACT));
confirmInt(4, invokeMatch(BoolEval.TRUE, ae, MATCH_LARGEST_LTE)); confirmInt(4, invokeMatch(BoolEval.TRUE, ae, MATCH_LARGEST_LTE));
confirmInt(3, invokeMatch(BoolEval.TRUE, ae, MATCH_EXACT)); confirmInt(3, invokeMatch(BoolEval.TRUE, ae, MATCH_EXACT));
} }
public void testHeterogeneous() { public void testHeterogeneous() {
ValueEval[] values = { ValueEval[] values = {
new NumberEval(4), new NumberEval(4),
BoolEval.FALSE, BoolEval.FALSE,
new NumberEval(5), new NumberEval(5),
new StringEval("Albert"), new StringEval("Albert"),
BoolEval.FALSE, BoolEval.FALSE,
BoolEval.TRUE, BoolEval.TRUE,
new NumberEval(10), new NumberEval(10),
new StringEval("Charles"), new StringEval("Charles"),
new StringEval("Ed"), new StringEval("Ed"),
new NumberEval(10), new NumberEval(10),
new NumberEval(25), new NumberEval(25),
BoolEval.TRUE, BoolEval.TRUE,
new StringEval("Ed"), new StringEval("Ed"),
}; };
AreaEval ae = createAreaEval("A1:A13", values); AreaEval ae = EvalFactory.createAreaEval("A1:A13", values);
assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Aaron"), ae, MATCH_LARGEST_LTE)); assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Aaron"), ae, MATCH_LARGEST_LTE));
confirmInt(5, invokeMatch(BoolEval.FALSE, ae, MATCH_LARGEST_LTE)); confirmInt(5, invokeMatch(BoolEval.FALSE, ae, MATCH_LARGEST_LTE));
confirmInt(2, invokeMatch(BoolEval.FALSE, ae, MATCH_EXACT)); confirmInt(2, invokeMatch(BoolEval.FALSE, ae, MATCH_EXACT));
confirmInt(3, invokeMatch(new NumberEval(5), ae, MATCH_LARGEST_LTE)); confirmInt(3, invokeMatch(new NumberEval(5), ae, MATCH_LARGEST_LTE));
confirmInt(3, invokeMatch(new NumberEval(5), ae, MATCH_EXACT)); confirmInt(3, invokeMatch(new NumberEval(5), ae, MATCH_EXACT));
confirmInt(8, invokeMatch(new StringEval("CHARLES"), ae, MATCH_EXACT)); confirmInt(8, invokeMatch(new StringEval("CHARLES"), ae, MATCH_EXACT));
confirmInt(4, invokeMatch(new StringEval("Ben"), ae, MATCH_LARGEST_LTE)); confirmInt(4, invokeMatch(new StringEval("Ben"), ae, MATCH_LARGEST_LTE));
confirmInt(13, invokeMatch(new StringEval("ED"), ae, MATCH_LARGEST_LTE)); confirmInt(13, invokeMatch(new StringEval("ED"), ae, MATCH_LARGEST_LTE));
confirmInt(9, invokeMatch(new StringEval("ED"), ae, MATCH_EXACT)); confirmInt(9, invokeMatch(new StringEval("ED"), ae, MATCH_EXACT));
confirmInt(13, invokeMatch(new StringEval("Hugh"), ae, MATCH_LARGEST_LTE)); confirmInt(13, invokeMatch(new StringEval("Hugh"), ae, MATCH_LARGEST_LTE));
assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Hugh"), ae, MATCH_EXACT)); assertEquals(ErrorEval.NA, invokeMatch(new StringEval("Hugh"), ae, MATCH_EXACT));
confirmInt(11, invokeMatch(new NumberEval(30), ae, MATCH_LARGEST_LTE)); confirmInt(11, invokeMatch(new NumberEval(30), ae, MATCH_LARGEST_LTE));
confirmInt(12, invokeMatch(BoolEval.TRUE, ae, MATCH_LARGEST_LTE)); confirmInt(12, invokeMatch(BoolEval.TRUE, ae, MATCH_LARGEST_LTE));
} }
/** /**
* Ensures that the match_type argument can be an <tt>AreaEval</tt>.<br/> * Ensures that the match_type argument can be an <tt>AreaEval</tt>.<br/>
* Bugzilla 44421 * Bugzilla 44421
*/ */
public void testMatchArgTypeArea() { public void testMatchArgTypeArea() {
ValueEval[] values = {
new NumberEval(4),
new NumberEval(5),
new NumberEval(10),
new NumberEval(10),
new NumberEval(25),
};
AreaEval ae = createAreaEval("A1:A5", values);
AreaEval matchAE = createAreaEval("C1:C1", new ValueEval[] { MATCH_LARGEST_LTE, }); ValueEval[] values = {
new NumberEval(4),
new NumberEval(5),
new NumberEval(10),
new NumberEval(10),
new NumberEval(25),
};
AreaEval ae = EvalFactory.createAreaEval("A1:A5", values);
AreaEval matchAE = EvalFactory.createAreaEval("C1:C1", new ValueEval[] { MATCH_LARGEST_LTE, });
try { try {
confirmInt(4, invokeMatch(new NumberEval(10), ae, matchAE)); confirmInt(4, invokeMatch(new NumberEval(10), ae, matchAE));
} catch (RuntimeException e) { } catch (RuntimeException e) {
if(e.getMessage().startsWith("Unexpected match_type type")) { if(e.getMessage().startsWith("Unexpected match_type type")) {
// identified bug 44421 // identified bug 44421
fail(e.getMessage()); fail(e.getMessage());
} }
// some other error ?? // some other error ??

View File

@ -20,6 +20,8 @@
*/ */
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.functions.XYNumericFunction.Accumulator;
/** /**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
@ -566,99 +568,99 @@ public class TestMathX extends AbstractNumericTestCase {
} }
public void testSumx2my2() { public void testSumx2my2() {
double d = 0;
double[] xarr = null; double[] xarr = null;
double[] yarr = null; double[] yarr = null;
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumx2my2(xarr, yarr); confirmSumx2my2(xarr, yarr, 100);
assertEquals("sumx2my2 ", 100, d);
xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10}; xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumx2my2(xarr, yarr); confirmSumx2my2(xarr, yarr, 100);
assertEquals("sumx2my2 ", 100, d);
xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumx2my2(xarr, yarr); confirmSumx2my2(xarr, yarr, -100);
assertEquals("sumx2my2 ", -100, d);
xarr = new double[]{10}; xarr = new double[]{10};
yarr = new double[]{9}; yarr = new double[]{9};
d = MathX.sumx2my2(xarr, yarr); confirmSumx2my2(xarr, yarr, 19);
assertEquals("sumx2my2 ", 19, d);
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumx2my2(xarr, yarr); confirmSumx2my2(xarr, yarr, 0);
assertEquals("sumx2my2 ", 0, d);
} }
public void testSumx2py2() { public void testSumx2py2() {
double d = 0;
double[] xarr = null; double[] xarr = null;
double[] yarr = null; double[] yarr = null;
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumx2py2(xarr, yarr); confirmSumx2py2(xarr, yarr, 670);
assertEquals("sumx2py2 ", 670, d);
xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10}; xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumx2py2(xarr, yarr); confirmSumx2py2(xarr, yarr, 670);
assertEquals("sumx2py2 ", 670, d);
xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumx2py2(xarr, yarr); confirmSumx2py2(xarr, yarr, 670);
assertEquals("sumx2py2 ", 670, d);
xarr = new double[]{10}; xarr = new double[]{10};
yarr = new double[]{9}; yarr = new double[]{9};
d = MathX.sumx2py2(xarr, yarr); confirmSumx2py2(xarr, yarr, 181);
assertEquals("sumx2py2 ", 181, d);
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumx2py2(xarr, yarr); confirmSumx2py2(xarr, yarr, 770);
assertEquals("sumx2py2 ", 770, d);
} }
public void testSumxmy2() { public void testSumxmy2() {
double d = 0;
double[] xarr = null; double[] xarr = null;
double[] yarr = null; double[] yarr = null;
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumxmy2(xarr, yarr); confirmSumxmy2(xarr, yarr, 10);
assertEquals("sumxmy2 ", 10, d);
xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10}; xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10};
yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
d = MathX.sumxmy2(xarr, yarr); confirmSumxmy2(xarr, yarr, 1330);
assertEquals("sumxmy2 ", 1330, d);
xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumxmy2(xarr, yarr); confirmSumxmy2(xarr, yarr, 10);
assertEquals("sumxmy2 ", 10, d);
xarr = new double[]{10}; xarr = new double[]{10};
yarr = new double[]{9}; yarr = new double[]{9};
d = MathX.sumxmy2(xarr, yarr); confirmSumxmy2(xarr, yarr, 1);
assertEquals("sumxmy2 ", 1, d);
xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
d = MathX.sumxmy2(xarr, yarr); confirmSumxmy2(xarr, yarr, 0);
assertEquals("sumxmy2 ", 0, d);
} }
private static void confirmSumx2my2(double[] xarr, double[] yarr, double expectedResult) {
confirmXY(new Sumx2my2().createAccumulator(), xarr, yarr, expectedResult);
}
private static void confirmSumx2py2(double[] xarr, double[] yarr, double expectedResult) {
confirmXY(new Sumx2py2().createAccumulator(), xarr, yarr, expectedResult);
}
private static void confirmSumxmy2(double[] xarr, double[] yarr, double expectedResult) {
confirmXY(new Sumxmy2().createAccumulator(), xarr, yarr, expectedResult);
}
private static void confirmXY(Accumulator acc, double[] xarr, double[] yarr,
double expectedResult) {
double result = 0.0;
for (int i = 0; i < xarr.length; i++) {
result += acc.accumulate(xarr[i], yarr[i]);
}
assertEquals(expectedResult, result, 0.0);
}
public void testRound() { public void testRound() {
double d = 0; double d = 0;
int p = 0; int p = 0;

View File

@ -17,29 +17,25 @@
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.AreaPtg; import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval; import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval; 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.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.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval;
import junit.framework.TestCase;
/** /**
* Tests for Excel function MID() * Tests for Excel function MID()
* *
* @author Josh Micich * @author Josh Micich
*/ */
public final class TestMid extends TestCase { public final class TestMid extends TestCase {
private static Eval invokeMid(Eval text, Eval startPos, Eval numChars) { private static Eval invokeMid(Eval text, Eval startPos, Eval numChars) {
Eval[] args = new Eval[] { text, startPos, numChars, }; Eval[] args = new Eval[] { text, startPos, numChars, };
return new Mid().evaluate(args, -1, (short)-1); return new Mid().evaluate(args, -1, (short)-1);
@ -56,28 +52,28 @@ public final class TestMid extends TestCase {
assertEquals(ErrorEval.class, result.getClass()); assertEquals(ErrorEval.class, result.getClass());
assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode()); assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode());
} }
public void testBasic() { public void testBasic() {
confirmMid(new StringEval("galactic"), new NumberEval(3), new NumberEval(4), "lact"); confirmMid(new StringEval("galactic"), new NumberEval(3), new NumberEval(4), "lact");
} }
/** /**
* Valid cases where args are not precisely (string, int, int) but can be resolved OK. * Valid cases where args are not precisely (string, int, int) but can be resolved OK.
*/ */
public void testUnusualArgs() { public void testUnusualArgs() {
// startPos with fractional digits // startPos with fractional digits
confirmMid(new StringEval("galactic"), new NumberEval(3.1), new NumberEval(4), "lact"); confirmMid(new StringEval("galactic"), new NumberEval(3.1), new NumberEval(4), "lact");
// string startPos // string startPos
confirmMid(new StringEval("galactic"), new StringEval("3"), new NumberEval(4), "lact"); confirmMid(new StringEval("galactic"), new StringEval("3"), new NumberEval(4), "lact");
// text (first) arg type is number, other args are strings with fractional digits // text (first) arg type is number, other args are strings with fractional digits
confirmMid(new NumberEval(123456), new StringEval("3.1"), new StringEval("2.9"), "34"); confirmMid(new NumberEval(123456), new StringEval("3.1"), new StringEval("2.9"), "34");
// startPos is 1x1 area ref, numChars is cell ref // startPos is 1x1 area ref, numChars is cell ref
AreaEval aeStart = new Area2DEval(new AreaPtg("A1:A1"), new ValueEval[] { new NumberEval(2), } ); AreaEval aeStart = EvalFactory.createAreaEval("A1:A1", new ValueEval[] { new NumberEval(2), } );
RefEval reNumChars = new Ref2DEval(new RefPtg("B1"), new NumberEval(3)); RefEval reNumChars = EvalFactory.createRefEval("B1", new NumberEval(3));
confirmMid(new StringEval("galactic"), aeStart, reNumChars, "ala"); confirmMid(new StringEval("galactic"), aeStart, reNumChars, "ala");
confirmMid(new StringEval("galactic"), new NumberEval(3.1), BlankEval.INSTANCE, ""); confirmMid(new StringEval("galactic"), new NumberEval(3.1), BlankEval.INSTANCE, "");
@ -85,7 +81,7 @@ public final class TestMid extends TestCase {
confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.FALSE, ""); confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.FALSE, "");
confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.TRUE, "l"); confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.TRUE, "l");
confirmMid(BlankEval.INSTANCE, new NumberEval(3), BoolEval.TRUE, ""); confirmMid(BlankEval.INSTANCE, new NumberEval(3), BoolEval.TRUE, "");
} }
/** /**
@ -93,7 +89,7 @@ public final class TestMid extends TestCase {
*/ */
public void testExtremes() { public void testExtremes() {
confirmMid(new StringEval("galactic"), new NumberEval(4), new NumberEval(400), "actic"); confirmMid(new StringEval("galactic"), new NumberEval(4), new NumberEval(400), "actic");
confirmMid(new StringEval("galactic"), new NumberEval(30), new NumberEval(4), ""); confirmMid(new StringEval("galactic"), new NumberEval(30), new NumberEval(4), "");
confirmMid(new StringEval("galactic"), new NumberEval(3), new NumberEval(0), ""); confirmMid(new StringEval("galactic"), new NumberEval(3), new NumberEval(0), "");
} }
@ -106,9 +102,9 @@ public final class TestMid extends TestCase {
confirmMid(new StringEval("galactic"), ErrorEval.NAME_INVALID, new NumberEval(4), ErrorEval.NAME_INVALID); confirmMid(new StringEval("galactic"), ErrorEval.NAME_INVALID, new NumberEval(4), ErrorEval.NAME_INVALID);
confirmMid(new StringEval("galactic"), new NumberEval(3), ErrorEval.NAME_INVALID, ErrorEval.NAME_INVALID); confirmMid(new StringEval("galactic"), new NumberEval(3), ErrorEval.NAME_INVALID, ErrorEval.NAME_INVALID);
confirmMid(new StringEval("galactic"), ErrorEval.DIV_ZERO, ErrorEval.NAME_INVALID, ErrorEval.DIV_ZERO); confirmMid(new StringEval("galactic"), ErrorEval.DIV_ZERO, ErrorEval.NAME_INVALID, ErrorEval.DIV_ZERO);
confirmMid(new StringEval("galactic"), BlankEval.INSTANCE, new NumberEval(3.1), ErrorEval.VALUE_INVALID); confirmMid(new StringEval("galactic"), BlankEval.INSTANCE, new NumberEval(3.1), ErrorEval.VALUE_INVALID);
confirmMid(new StringEval("galactic"), new NumberEval(0), new NumberEval(4), ErrorEval.VALUE_INVALID); confirmMid(new StringEval("galactic"), new NumberEval(0), new NumberEval(4), ErrorEval.VALUE_INVALID);
confirmMid(new StringEval("galactic"), new NumberEval(1), new NumberEval(-1), ErrorEval.VALUE_INVALID); confirmMid(new StringEval("galactic"), new NumberEval(1), new NumberEval(-1), ErrorEval.VALUE_INVALID);
} }

View File

@ -17,18 +17,16 @@
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.RefPtg; import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.AreaEval;
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.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.NumericValueEval; import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval;
import junit.framework.TestCase;
/** /**
* Test cases for SUMPRODUCT() * Test cases for SUMPRODUCT()
* *
@ -50,7 +48,7 @@ public final class TestSumproduct extends TestCase {
public void testScalarSimple() { public void testScalarSimple() {
RefEval refEval = new Ref2DEval(new RefPtg("A1"), new NumberEval(3)); RefEval refEval = EvalFactory.createRefEval("A1", new NumberEval(3));
Eval[] args = { Eval[] args = {
refEval, refEval,
new NumberEval(2), new NumberEval(2),
@ -113,7 +111,6 @@ public final class TestSumproduct extends TestCase {
}; };
AreaEval aeA = EvalFactory.createAreaEval("A1:A2", aValues); AreaEval aeA = EvalFactory.createAreaEval("A1:A2", aValues);
AreaEval aeB = EvalFactory.createAreaEval("B1:B2", new ValueEval[2]); AreaEval aeB = EvalFactory.createAreaEval("B1:B2", new ValueEval[2]);
aeB.getValues()[1] = ErrorEval.REF_INVALID;
Eval[] args = { aeA, aeB, }; Eval[] args = { aeA, aeB, };
assertEquals(ErrorEval.REF_INVALID, invokeSumproduct(args)); assertEquals(ErrorEval.REF_INVALID, invokeSumproduct(args));

View File

@ -17,18 +17,16 @@
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.RefPtg; import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.BlankEval; 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.BoolEval;
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.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
import org.apache.poi.hssf.record.formula.eval.StringEval; 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;
import junit.framework.TestCase;
/** /**
* Test cases for Excel function T() * Test cases for Excel function T()
* *
@ -50,7 +48,7 @@ public final class TestTFunc extends TestCase {
* where cell A1 has the specified innerValue * where cell A1 has the specified innerValue
*/ */
private Eval invokeTWithReference(ValueEval innerValue) { private Eval invokeTWithReference(ValueEval innerValue) {
Eval arg = new Ref2DEval(new RefPtg((short)1, (short)1, false, false), innerValue); Eval arg = EvalFactory.createRefEval("$B$2", innerValue);
return invokeT(arg); return invokeT(arg);
} }

View File

@ -19,8 +19,6 @@ package org.apache.poi.hssf.record.formula.functions;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.eval.Area2DEval;
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.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
@ -100,7 +98,7 @@ public final class TestXYNumericFunction extends TestCase {
private static ValueEval createAreaEval(ValueEval[] values) { private static ValueEval createAreaEval(ValueEval[] values) {
String refStr = "A1:A" + values.length; String refStr = "A1:A" + values.length;
return new Area2DEval(new AreaPtg(refStr), values); return EvalFactory.createAreaEval(refStr, values);
} }
public void testErrors() { public void testErrors() {

View File

@ -24,8 +24,8 @@ import java.io.IOException;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.HSSFTestDataSamples;
import org.apache.poi.hssf.util.AreaReference; import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.hssf.util.CellReference; import org.apache.poi.ss.util.CellReference;
/** /**
* *

View File

@ -34,6 +34,8 @@ import org.apache.poi.hssf.usermodel.HSSFName;
import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellReference;
/** /**
* *
*/ */

View File

@ -20,7 +20,8 @@ package org.apache.poi.hssf.util;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.util.CellReference.NameType; import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.CellReference.NameType;
public final class TestCellReference extends TestCase { public final class TestCellReference extends TestCase {