Big patch from Josh from bug #44504 - lots of formula parser improvements

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@633547 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2008-03-04 16:53:32 +00:00
parent 900f3999f0
commit e685873465
81 changed files with 3824 additions and 2400 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,302 @@
/* ====================================================================
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.model;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.hssf.record.CRNCountRecord;
import org.apache.poi.hssf.record.CRNRecord;
import org.apache.poi.hssf.record.CountryRecord;
import org.apache.poi.hssf.record.ExternSheetRecord;
import org.apache.poi.hssf.record.ExternSheetSubRecord;
import org.apache.poi.hssf.record.ExternalNameRecord;
import org.apache.poi.hssf.record.NameRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.SupBookRecord;
/**
* Link Table (OOO pdf reference: 4.10.3 ) <p/>
*
* The main data of all types of references is stored in the Link Table inside the Workbook Globals
* Substream (4.2.5). The Link Table itself is optional and occurs only, if there are any
* references in the document.
* <p/>
*
* In BIFF8 the Link Table consists of
* <ul>
* <li>one or more EXTERNALBOOK Blocks<p/>
* each consisting of
* <ul>
* <li>exactly one EXTERNALBOOK (0x01AE) record</li>
* <li>zero or more EXTERNALNAME (0x0023) records</li>
* <li>zero or more CRN Blocks<p/>
* each consisting of
* <ul>
* <li>exactly one XCT (0x0059)record</li>
* <li>zero or more CRN (0x005A) records (documentation says one or more)</li>
* </ul>
* </li>
* </ul>
* </li>
* <li>exactly one EXTERNSHEET (0x0017) record</li>
* <li>zero or more DEFINEDNAME (0x0018) records</li>
* </ul>
*
*
* @author Josh Micich
*/
final class LinkTable {
private static final class CRNBlock {
private final CRNCountRecord _countRecord;
private final CRNRecord[] _crns;
public CRNBlock(RecordStream rs) {
_countRecord = (CRNCountRecord) rs.getNext();
int nCRNs = _countRecord.getNumberOfCRNs();
CRNRecord[] crns = new CRNRecord[nCRNs];
for (int i = 0; i < crns.length; i++) {
crns[i] = (CRNRecord) rs.getNext();
}
_crns = crns;
}
public CRNRecord[] getCrns() {
return (CRNRecord[]) _crns.clone();
}
}
private static final class ExternalBookBlock {
private final SupBookRecord _externalBookRecord;
private final ExternalNameRecord[] _externalNameRecords;
private final CRNBlock[] _crnBlocks;
public ExternalBookBlock(RecordStream rs) {
_externalBookRecord = (SupBookRecord) rs.getNext();
List temp = new ArrayList();
while(rs.peekNextClass() == ExternalNameRecord.class) {
temp.add(rs.getNext());
}
_externalNameRecords = new ExternalNameRecord[temp.size()];
temp.toArray(_externalNameRecords);
temp.clear();
while(rs.peekNextClass() == CRNCountRecord.class) {
temp.add(new CRNBlock(rs));
}
_crnBlocks = new CRNBlock[temp.size()];
temp.toArray(_crnBlocks);
}
public ExternalBookBlock(short numberOfSheets) {
_externalBookRecord = SupBookRecord.createInternalReferences(numberOfSheets);
_externalNameRecords = new ExternalNameRecord[0];
_crnBlocks = new CRNBlock[0];
}
public SupBookRecord getExternalBookRecord() {
return _externalBookRecord;
}
public String getNameText(int definedNameIndex) {
return _externalNameRecords[definedNameIndex].getText();
}
}
private final ExternalBookBlock[] _externalBookBlocks;
private final ExternSheetRecord _externSheetRecord;
private final List _definedNames;
private final int _recordCount;
private final WorkbookRecordList _workbookRecordList; // TODO - would be nice to remove this
public LinkTable(List inputList, int startIndex, WorkbookRecordList workbookRecordList) {
_workbookRecordList = workbookRecordList;
RecordStream rs = new RecordStream(inputList, startIndex);
List temp = new ArrayList();
while(rs.peekNextClass() == SupBookRecord.class) {
temp.add(new ExternalBookBlock(rs));
}
if(temp.size() < 1) {
throw new RuntimeException("Need at least one EXTERNALBOOK blocks");
}
_externalBookBlocks = new ExternalBookBlock[temp.size()];
temp.toArray(_externalBookBlocks);
temp.clear();
// If link table is present, there is always 1 of ExternSheetRecord
Record next = rs.getNext();
_externSheetRecord = (ExternSheetRecord)next;
_definedNames = new ArrayList();
// collect zero or more DEFINEDNAMEs id=0x18
while(rs.peekNextClass() == NameRecord.class) {
NameRecord nr = (NameRecord)rs.getNext();
_definedNames.add(nr);
}
_recordCount = rs.getCountRead();
_workbookRecordList.getRecords().addAll(inputList.subList(startIndex, startIndex + _recordCount));
}
public LinkTable(short numberOfSheets, WorkbookRecordList workbookRecordList) {
_workbookRecordList = workbookRecordList;
_definedNames = new ArrayList();
_externalBookBlocks = new ExternalBookBlock[] {
new ExternalBookBlock(numberOfSheets),
};
_externSheetRecord = new ExternSheetRecord();
_recordCount = 2;
// tell _workbookRecordList about the 2 new records
SupBookRecord supbook = _externalBookBlocks[0].getExternalBookRecord();
int idx = findFirstRecordLocBySid(CountryRecord.sid);
if(idx < 0) {
throw new RuntimeException("CountryRecord not found");
}
_workbookRecordList.add(idx+1, _externSheetRecord);
_workbookRecordList.add(idx+1, supbook);
}
/**
* TODO - would not be required if calling code used RecordStream or similar
*/
public int getRecordCount() {
return _recordCount;
}
public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex) {
Iterator iterator = _definedNames.iterator();
while (iterator.hasNext()) {
NameRecord record = ( NameRecord ) iterator.next();
//print areas are one based
if (record.getBuiltInName() == name && record.getIndexToSheet() == sheetIndex) {
return record;
}
}
return null;
}
public void removeBuiltinRecord(byte name, int sheetIndex) {
//the name array is smaller so searching through it should be faster than
//using the findFirstXXXX methods
NameRecord record = getSpecificBuiltinRecord(name, sheetIndex);
if (record != null) {
_definedNames.remove(record);
}
// TODO - do we need "Workbook.records.remove(...);" similar to that in Workbook.removeName(int namenum) {}?
}
public int getNumNames() {
return _definedNames.size();
}
public NameRecord getNameRecord(int index) {
return (NameRecord) _definedNames.get(index);
}
public void addName(NameRecord name) {
_definedNames.add(name);
// TODO - this is messy
// Not the most efficient way but the other way was causing too many bugs
int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid);
if (idx == -1) idx = findFirstRecordLocBySid(CountryRecord.sid);
int countNames = _definedNames.size();
_workbookRecordList.add(idx+countNames, name);
}
public void removeName(int namenum) {
_definedNames.remove(namenum);
}
public short getIndexToSheet(short num) {
return _externSheetRecord.getREFRecordAt(num).getIndexToFirstSupBook();
}
public int getSheetIndexFromExternSheetIndex(int externSheetNumber) {
if (externSheetNumber >= _externSheetRecord.getNumOfREFStructures()) {
return -1;
}
return _externSheetRecord.getREFRecordAt(externSheetNumber).getIndexToFirstSupBook();
}
public short addSheetIndexToExternSheet(short sheetNumber) {
ExternSheetSubRecord record = new ExternSheetSubRecord();
record.setIndexToFirstSupBook(sheetNumber);
record.setIndexToLastSupBook(sheetNumber);
_externSheetRecord.addREFRecord(record);
_externSheetRecord.setNumOfREFStructures((short)(_externSheetRecord.getNumOfREFStructures() + 1));
return (short)(_externSheetRecord.getNumOfREFStructures() - 1);
}
public short checkExternSheet(int sheetNumber) {
//Trying to find reference to this sheet
int nESRs = _externSheetRecord.getNumOfREFStructures();
for(short i=0; i< nESRs; i++) {
ExternSheetSubRecord esr = _externSheetRecord.getREFRecordAt(i);
if (esr.getIndexToFirstSupBook() == sheetNumber
&& esr.getIndexToLastSupBook() == sheetNumber){
return i;
}
}
//We Haven't found reference to this sheet
return addSheetIndexToExternSheet((short) sheetNumber);
}
/**
* copied from Workbook
*/
private int findFirstRecordLocBySid(short sid) {
int index = 0;
for (Iterator iterator = _workbookRecordList.iterator(); iterator.hasNext(); ) {
Record record = ( Record ) iterator.next();
if (record.getSid() == sid) {
return index;
}
index ++;
}
return -1;
}
public int getNumberOfREFStructures() {
return _externSheetRecord.getNumOfREFStructures();
}
public String resolveNameXText(int refIndex, int definedNameIndex) {
short extBookIndex = _externSheetRecord.getREFRecordAt(refIndex).getIndexToSupBook();
return _externalBookBlocks[extBookIndex].getNameText(definedNameIndex);
}
}

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.model;
import java.util.List;
import org.apache.poi.hssf.record.Record;
/**
* Simplifies iteration over a sequence of <tt>Record</tt> objects.
*
* @author Josh Micich
*/
final class RecordStream {
private final List _list;
private int _nextIndex;
private int _countRead;
public RecordStream(List inputList, int startIndex) {
_list = inputList;
_nextIndex = startIndex;
_countRead = 0;
}
public boolean hasNext() {
return _nextIndex < _list.size();
}
public Record getNext() {
if(_nextIndex >= _list.size()) {
throw new RuntimeException("Attempt to read past end of record stream");
}
_countRead ++;
return (Record) _list.get(_nextIndex++);
}
/**
* @return the {@link Class} of the next Record. <code>null</code> if this stream is exhausted.
*/
public Class peekNextClass() {
if(_nextIndex >= _list.size()) {
return null;
}
return _list.get(_nextIndex).getClass();
}
public int getCountRead() {
return _countRead;
}
}

View File

@ -79,10 +79,8 @@ public class Workbook implements Model
*/
protected SSTRecord sst = null;
/**
* Holds the Extern Sheet with references to bound sheets
*/
protected ExternSheetRecord externSheet= null;
private LinkTable linkTable; // optionally occurs if there are references in the document. (4.10.3)
/**
* holds the "boundsheet" records (aka bundlesheet) so that they can have their
@ -92,8 +90,6 @@ public class Workbook implements Model
protected ArrayList formats = new ArrayList();
protected ArrayList names = new ArrayList();
protected ArrayList hyperlinks = new ArrayList();
protected int numxfs = 0; // hold the number of extended format records
@ -134,6 +130,7 @@ public class Workbook implements Model
new Integer(recs.size()));
Workbook retval = new Workbook();
ArrayList records = new ArrayList(recs.size() / 3);
retval.records.setRecords(records);
int k;
for (k = 0; k < recs.size(); k++) {
@ -192,21 +189,16 @@ public class Workbook implements Model
retval.records.setBackuppos( k );
break;
case ExternSheetRecord.sid :
if (log.check( POILogger.DEBUG ))
log.log(DEBUG, "found extern sheet record at " + k);
retval.externSheet = ( ExternSheetRecord ) rec;
break;
throw new RuntimeException("Extern sheet is part of LinkTable");
case NameRecord.sid :
if (log.check( POILogger.DEBUG ))
log.log(DEBUG, "found name record at " + k);
retval.names.add(rec);
// retval.records.namepos = k;
break;
throw new RuntimeException("DEFINEDNAME is part of LinkTable");
case SupBookRecord.sid :
if (log.check( POILogger.DEBUG ))
log.log(DEBUG, "found SupBook record at " + k);
retval.linkTable = new LinkTable(recs, k, retval.records);
// retval.records.supbookpos = k;
break;
k+=retval.linkTable.getRecordCount() - 1;
continue;
case FormatRecord.sid :
if (log.check( POILogger.DEBUG ))
log.log(DEBUG, "found format record at " + k);
@ -262,8 +254,6 @@ public class Workbook implements Model
break;
}
}
retval.records.setRecords(records);
if (retval.windowOne == null) {
retval.windowOne = (WindowOneRecord) retval.createWindowOne();
@ -283,6 +273,7 @@ public class Workbook implements Model
log.log( DEBUG, "creating new workbook from scratch" );
Workbook retval = new Workbook();
ArrayList records = new ArrayList( 30 );
retval.records.setRecords(records);
ArrayList formats = new ArrayList( 8 );
records.add( retval.createBOF() );
@ -339,8 +330,9 @@ public class Workbook implements Model
records.add( retval.createStyle( k ) );
}
records.add( retval.createUseSelFS() );
for ( int k = 0; k < 1; k++ )
{ // now just do 1
int nBoundSheets = 1; // now just do 1
for ( int k = 0; k < nBoundSheets; k++ ) {
BoundSheetRecord bsr =
(BoundSheetRecord) retval.createBoundSheet( k );
@ -351,12 +343,14 @@ public class Workbook implements Model
// retval.records.supbookpos = retval.records.bspos + 1;
// retval.records.namepos = retval.records.supbookpos + 2;
records.add( retval.createCountry() );
for ( int k = 0; k < nBoundSheets; k++ ) {
retval.getOrCreateLinkTable().checkExternSheet(k);
}
retval.sst = (SSTRecord) retval.createSST();
records.add( retval.sst );
records.add( retval.createExtendedSST() );
records.add( retval.createEOF() );
retval.records.setRecords(records);
if (log.check( POILogger.DEBUG ))
log.log( DEBUG, "exit create new workbook from scratch" );
return retval;
@ -369,36 +363,20 @@ public class Workbook implements Model
* @param sheetIndex Index to match
* @return null if no builtin NameRecord matches
*/
public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex)
{
Iterator iterator = names.iterator();
while (iterator.hasNext()) {
NameRecord record = ( NameRecord ) iterator.next();
//print areas are one based
if (record.getBuiltInName() == name && record.getIndexToSheet() == sheetIndex) {
return record;
}
}
return null;
}
public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex)
{
return getOrCreateLinkTable().getSpecificBuiltinRecord(name, sheetIndex);
}
/**
* Removes the specified Builtin NameRecord that matches the name and index
* @param name byte representation of the builtin to match
* @param sheetIndex zero-based sheet reference
*/
public void removeBuiltinRecord(byte name, int sheetIndex) {
//the name array is smaller so searching through it should be faster than
//using the findFirstXXXX methods
NameRecord record = getSpecificBuiltinRecord(name, sheetIndex);
if (record != null) {
names.remove(record);
}
}
public void removeBuiltinRecord(byte name, int sheetIndex) {
linkTable.removeBuiltinRecord(name, sheetIndex);
// TODO - do we need "this.records.remove(...);" similar to that in this.removeName(int namenum) {}?
}
public int getNumRecords() {
return records.size();
@ -614,6 +592,7 @@ public class Workbook implements Model
records.add(records.getBspos()+1, bsr);
records.setBspos( records.getBspos() + 1 );
boundsheets.add(bsr);
getOrCreateLinkTable().checkExternSheet(sheetnum);
fixTabIdRecord();
}
}
@ -1824,14 +1803,26 @@ public class Workbook implements Model
protected Record createEOF() {
return new EOFRecord();
}
/**
* lazy initialization
* Note - creating the link table causes creation of 1 EXTERNALBOOK and 1 EXTERNALSHEET record
*/
private LinkTable getOrCreateLinkTable() {
if(linkTable == null) {
linkTable = new LinkTable((short) getNumSheets(), records);
}
return linkTable;
}
public SheetReferences getSheetReferences() {
SheetReferences refs = new SheetReferences();
if (externSheet != null) {
for (int k = 0; k < externSheet.getNumOfREFStructures(); k++) {
if (linkTable != null) {
int numRefStructures = linkTable.getNumberOfREFStructures();
for (short k = 0; k < numRefStructures; k++) {
String sheetName = findSheetNameFromExternSheet((short)k);
String sheetName = findSheetNameFromExternSheet(k);
refs.addSheetReference(sheetName, k);
}
@ -1846,7 +1837,8 @@ public class Workbook implements Model
public String findSheetNameFromExternSheet(short num){
String result="";
short indexToSheet = externSheet.getREFRecordAt(num).getIndexToFirstSupBook();
short indexToSheet = linkTable.getIndexToSheet(num);
if (indexToSheet>-1) { //error check, bail out gracefully!
result = getSheetName(indexToSheet);
}
@ -1861,10 +1853,7 @@ public class Workbook implements Model
*/
public int getSheetIndexFromExternSheetIndex(int externSheetNumber)
{
if (externSheetNumber >= externSheet.getNumOfREFStructures())
return -1;
else
return externSheet.getREFRecordAt(externSheetNumber).getIndexToFirstSupBook();
return linkTable.getSheetIndexFromExternSheetIndex(externSheetNumber);
}
/** returns the extern sheet number for specific sheet number ,
@ -1873,58 +1862,17 @@ public class Workbook implements Model
* @return index to extern sheet
*/
public short checkExternSheet(int sheetNumber){
int i = 0;
boolean flag = false;
short result = 0;
if (externSheet == null) {
externSheet = createExternSheet();
}
//Trying to find reference to this sheet
while (i < externSheet.getNumOfREFStructures() && !flag){
ExternSheetSubRecord record = externSheet.getREFRecordAt(i);
if (record.getIndexToFirstSupBook() == sheetNumber &&
record.getIndexToLastSupBook() == sheetNumber){
flag = true;
result = (short) i;
}
++i;
}
//We Havent found reference to this sheet
if (!flag) {
result = addSheetIndexToExternSheet((short) sheetNumber);
}
return result;
return getOrCreateLinkTable().checkExternSheet(sheetNumber);
}
private short addSheetIndexToExternSheet(short sheetNumber){
short result;
ExternSheetSubRecord record = new ExternSheetSubRecord();
record.setIndexToFirstSupBook(sheetNumber);
record.setIndexToLastSupBook(sheetNumber);
externSheet.addREFRecord(record);
externSheet.setNumOfREFStructures((short)(externSheet.getNumOfREFStructures() + 1));
result = (short)(externSheet.getNumOfREFStructures() - 1);
return result;
}
/** gets the total number of names
* @return number of names
*/
public int getNumNames(){
int result = names.size();
return result;
if(linkTable == null) {
return 0;
}
return linkTable.getNumNames();
}
/** gets the name record
@ -1932,28 +1880,14 @@ public class Workbook implements Model
* @return name record
*/
public NameRecord getNameRecord(int index){
NameRecord result = (NameRecord) names.get(index);
return result;
return linkTable.getNameRecord(index);
}
/** creates new name
* @return new name record
*/
public NameRecord createName(){
NameRecord name = new NameRecord();
// Not the most efficient way but the other way was causing too many bugs
int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid);
if (idx == -1) idx = findFirstRecordLocBySid(CountryRecord.sid);
records.add(idx+names.size()+1, name);
names.add(name);
return name;
return addName(new NameRecord());
}
@ -1962,67 +1896,41 @@ public class Workbook implements Model
*/
public NameRecord addName(NameRecord name)
{
// Not the most efficient way but the other way was causing too many bugs
int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid);
if (idx == -1) idx = findFirstRecordLocBySid(CountryRecord.sid);
records.add(idx+names.size()+1, name);
names.add(name);
getOrCreateLinkTable().addName(name);
return name;
}
/**Generates a NameRecord to represent a built-in region
* @return a new NameRecord unless the index is invalid
*/
public NameRecord createBuiltInName(byte builtInName, int index)
{
if (index == -1 || index+1 > (int)Short.MAX_VALUE)
throw new IllegalArgumentException("Index is not valid ["+index+"]");
NameRecord name = new NameRecord(builtInName, (short)(index));
addName(name);
return name;
}
/**Generates a NameRecord to represent a built-in region
* @return a new NameRecord unless the index is invalid
*/
public NameRecord createBuiltInName(byte builtInName, int index)
{
if (index == -1 || index+1 > Short.MAX_VALUE)
throw new IllegalArgumentException("Index is not valid ["+index+"]");
NameRecord name = new NameRecord(builtInName, (short)(index));
addName(name);
return name;
}
/** removes the name
* @param namenum name index
*/
public void removeName(int namenum){
if (names.size() > namenum) {
if (linkTable.getNumNames() > namenum) {
int idx = findFirstRecordLocBySid(NameRecord.sid);
records.remove(idx + namenum);
names.remove(namenum);
linkTable.removeName(namenum);
}
}
/** creates a new extern sheet record
* @return the new extern sheet record
*/
protected ExternSheetRecord createExternSheet(){
ExternSheetRecord externSheet = new ExternSheetRecord();
int idx = findFirstRecordLocBySid(CountryRecord.sid);
records.add(idx+1, externSheet);
// records.add(records.supbookpos + 1 , rec);
//We also adds the supBook for internal reference
SupBookRecord supbook = new SupBookRecord();
supbook.setNumberOfSheets((short)getNumSheets());
//supbook.setFlag();
records.add(idx+1, supbook);
// records.add(records.supbookpos + 1 , supbook);
return externSheet;
}
/**
* Returns a format index that matches the passed in format. It does not tie into HSSFDataFormat.
* @param format the format string
@ -2419,5 +2327,14 @@ public class Workbook implements Model
writeProtect = null;
}
/**
* @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
* @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
* @return the string representation of the defined or external name
*/
public String resolveNameXText(int refIndex, int definedNameIndex) {
return linkTable.resolveNameXText(refIndex, definedNameIndex);
}
}

View File

@ -0,0 +1,94 @@
/* ====================================================================
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;
import org.apache.poi.util.LittleEndian;
/**
* XCT CRN Count <P>
*
* REFERENCE: 5.114<P>
*
* @author Josh Micich
*/
public final class CRNCountRecord extends Record {
public final static short sid = 0x59;
private static final short BASE_RECORD_SIZE = 4;
private int field_1_number_crn_records;
private int field_2_sheet_table_index;
public CRNCountRecord() {
throw new RuntimeException("incomplete code");
}
public CRNCountRecord(RecordInputStream in) {
super(in);
}
protected void validateSid(short id) {
if (id != sid) {
throw new RecordFormatException("NOT An XCT RECORD");
}
}
public int getNumberOfCRNs() {
return field_1_number_crn_records;
}
protected void fillFields(RecordInputStream in) {
field_1_number_crn_records = in.readShort();
if(field_1_number_crn_records < 0) {
// TODO - seems like the sign bit of this field might be used for some other purpose
// see example file for test case "TestBugs.test19599()"
field_1_number_crn_records = (short)-field_1_number_crn_records;
}
field_2_sheet_table_index = in.readShort();
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName()).append(" [XCT");
sb.append(" nCRNs=").append(field_1_number_crn_records);
sb.append(" sheetIx=").append(field_2_sheet_table_index);
sb.append("]");
return sb.toString();
}
public int serialize(int offset, byte [] data) {
LittleEndian.putShort(data, 0 + offset, sid);
LittleEndian.putShort(data, 2 + offset, BASE_RECORD_SIZE);
LittleEndian.putShort(data, 4 + offset, (short)field_1_number_crn_records);
LittleEndian.putShort(data, 6 + offset, (short)field_2_sheet_table_index);
return getRecordSize();
}
public int getRecordSize() {
return BASE_RECORD_SIZE + 4;
}
/**
* return the non static version of the id for this record.
*/
public short getSid() {
return sid;
}
}

View File

@ -0,0 +1,99 @@
/* ====================================================================
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;
import org.apache.poi.hssf.record.constant.ConstantValueParser;
import org.apache.poi.util.LittleEndian;
/**
* Title: CRN <P>
* Description: This record stores the contents of an external cell or cell range <P>
* REFERENCE: 5.23<P>
*
* @author josh micich
*/
public final class CRNRecord extends Record {
public final static short sid = 0x5A;
private int field_1_last_column_index;
private int field_2_first_column_index;
private int field_3_row_index;
private Object[] field_4_constant_values;
public CRNRecord() {
throw new RuntimeException("incomplete code");
}
public CRNRecord(RecordInputStream in) {
super(in);
}
protected void validateSid(short id) {
if (id != sid) {
throw new RecordFormatException("NOT An XCT RECORD");
}
}
public int getNumberOfCRNs() {
return field_1_last_column_index;
}
protected void fillFields(RecordInputStream in) {
field_1_last_column_index = in.readByte() & 0x00FF;
field_2_first_column_index = in.readByte() & 0x00FF;
field_3_row_index = in.readShort();
int nValues = field_1_last_column_index - field_2_first_column_index + 1;
field_4_constant_values = ConstantValueParser.parse(in, nValues);
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName()).append(" [CRN");
sb.append(" rowIx=").append(field_3_row_index);
sb.append(" firstColIx=").append(field_2_first_column_index);
sb.append(" lastColIx=").append(field_1_last_column_index);
sb.append("]");
return sb.toString();
}
private int getDataSize() {
return 4 + ConstantValueParser.getEncodedSize(field_4_constant_values);
}
public int serialize(int offset, byte [] data) {
int dataSize = getDataSize();
LittleEndian.putShort(data, 0 + offset, sid);
LittleEndian.putShort(data, 2 + offset, (short) dataSize);
LittleEndian.putByte(data, 4 + offset, field_1_last_column_index);
LittleEndian.putByte(data, 5 + offset, field_2_first_column_index);
LittleEndian.putShort(data, 6 + offset, (short) field_3_row_index);
return getRecordSize();
}
public int getRecordSize() {
return getDataSize() + 4;
}
/**
* return the non static version of the id for this record.
*/
public short getSid() {
return sid;
}
}

View File

@ -0,0 +1,179 @@
/* ====================================================================
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;
import java.util.List;
import java.util.Stack;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
/**
* EXTERNALNAME<p/>
*
* @author josh micich
*/
public final class ExternalNameRecord extends Record {
public final static short sid = 0x23; // as per BIFF8. (some old versions used 0x223)
private static final int OPT_BUILTIN_NAME = 0x0001;
private static final int OPT_AUTOMATIC_LINK = 0x0002;
private static final int OPT_PICTURE_LINK = 0x0004;
private static final int OPT_STD_DOCUMENT_NAME = 0x0008;
private static final int OPT_OLE_LINK = 0x0010;
// private static final int OPT_CLIP_FORMAT_MASK = 0x7FE0;
private static final int OPT_ICONIFIED_PICTURE_LINK = 0x8000;
private short field_1_option_flag;
private short field_2_index;
private short field_3_not_used;
private String field_4_name;
private Stack field_5_name_definition;
public ExternalNameRecord(RecordInputStream in) {
super(in);
}
/**
* Convenience Function to determine if the name is a built-in name
*/
public boolean isBuiltInName() {
return (field_1_option_flag & OPT_BUILTIN_NAME) != 0;
}
/**
* For OLE and DDE, links can be either 'automatic' or 'manual'
*/
public boolean isAutomaticLink() {
return (field_1_option_flag & OPT_AUTOMATIC_LINK) != 0;
}
/**
* only for OLE and DDE
*/
public boolean isPicureLink() {
return (field_1_option_flag & OPT_PICTURE_LINK) != 0;
}
/**
* DDE links only. If <code>true</code>, this denotes the 'StdDocumentName'
*/
public boolean isStdDocumentNameIdentifier() {
return (field_1_option_flag & OPT_STD_DOCUMENT_NAME) != 0;
}
public boolean isOLELink() {
return (field_1_option_flag & OPT_OLE_LINK) != 0;
}
public boolean isIconifiedPictureLink() {
return (field_1_option_flag & OPT_ICONIFIED_PICTURE_LINK) != 0;
}
/**
* @return the standard String representation of this name
*/
public String getText() {
return field_4_name;
}
/**
* called by constructor, should throw runtime exception in the event of a
* record passed with a differing ID.
*
* @param id alleged id for this record
*/
protected void validateSid(short id) {
if (id != sid) {
throw new RecordFormatException("NOT A valid ExternalName RECORD");
}
}
private int getDataSize(){
return 2 + 2 + field_4_name.length() + 2 + getNameDefinitionSize();
}
/**
* called by the class that is responsible for writing this sucker.
* Subclasses should implement this so that their data is passed back in a
* byte array.
*
* @param offset to begin writing at
* @param data byte array containing instance data
* @return number of bytes written
*/
public int serialize( int offset, byte[] data ) {
// TODO - junit tests
int dataSize = getDataSize();
LittleEndian.putShort( data, 0 + offset, sid );
LittleEndian.putShort( data, 2 + offset, (short) dataSize );
LittleEndian.putShort( data, 4 + offset, field_1_option_flag );
LittleEndian.putShort( data, 6 + offset, field_2_index );
LittleEndian.putShort( data, 8 + offset, field_3_not_used );
short nameLen = (short) field_4_name.length();
LittleEndian.putShort( data, 10 + offset, nameLen );
StringUtil.putCompressedUnicode( field_4_name, data, 10 + offset );
short defLen = (short) getNameDefinitionSize();
LittleEndian.putShort( data, 12 + nameLen + offset, defLen );
Ptg.serializePtgStack(field_5_name_definition, data, 12 + nameLen + offset );
return dataSize + 4;
}
private int getNameDefinitionSize() {
int result = 0;
List list = field_5_name_definition;
for (int k = 0; k < list.size(); k++)
{
Ptg ptg = ( Ptg ) list.get(k);
result += ptg.getSize();
}
return result;
}
public int getRecordSize(){
return 6 + 2 + field_4_name.length() + 2 + getNameDefinitionSize();
}
protected void fillFields(RecordInputStream in) {
field_1_option_flag = in.readShort();
field_2_index = in.readShort();
field_3_not_used = in.readShort();
short nameLength = in.readShort();
field_4_name = in.readCompressedUnicode(nameLength);
short formulaLen = in.readShort();
field_5_name_definition = Ptg.createParsedExpressionTokens(formulaLen, in);
}
public short getSid() {
return sid;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName()).append(" [EXTERNALNAME ");
sb.append(" ").append(field_4_name);
sb.append(" ix=").append(field_2_index);
sb.append("]");
return sb.toString();
}
}

View File

@ -77,7 +77,11 @@ public class RecordFactory
NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class,
FileSharingRecord.class, ChartTitleFormatRecord.class,
DVRecord.class, DVALRecord.class, UncalcedRecord.class,
HyperlinkRecord.class
HyperlinkRecord.class,
ExternalNameRecord.class, // TODO - same changes in non-@deprecated version of this class
SupBookRecord.class,
CRNCountRecord.class,
CRNRecord.class,
};
}
private static Map recordsMap = recordsToMap(records);

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -15,72 +14,159 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record;
import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats;
import org.apache.poi.util.LittleEndian;
/**
* Title: Sup Book <P>
* Description: A Extrenal Workbook Deciption (Sup Book)
* Title: Sup Book (EXTERNALBOOK) <P>
* Description: A External Workbook Description (Suplemental Book)
* Its only a dummy record for making new ExternSheet Record <P>
* REFERENCE: <P>
* REFERENCE: 5.38<P>
* @author Libin Roman (Vista Portal LDT. Developer)
* @author Andrew C. Oliver (acoliver@apache.org)
*
*/
public class SupBookRecord extends Record
{
public final class SupBookRecord extends Record {
public final static short sid = 0x1AE;
private static final short SMALL_RECORD_SIZE = 4;
private static final short TAG_INTERNAL_REFERENCES = 0x0401;
private static final short TAG_ADD_IN_FUNCTIONS = 0x3A01;
private short field_1_number_of_sheets;
private short field_2_flag;
private UnicodeString field_2_encoded_url;
private UnicodeString[] field_3_sheet_names;
private boolean _isAddInFunctions;
public SupBookRecord()
{
setFlag((short)0x401);
public static SupBookRecord createInternalReferences(short numberOfSheets) {
return new SupBookRecord(false, numberOfSheets);
}
public static SupBookRecord createAddInFunctions() {
return new SupBookRecord(true, (short)0);
}
public static SupBookRecord createExternalReferences(UnicodeString url, UnicodeString[] sheetNames) {
return new SupBookRecord(url, sheetNames);
}
private SupBookRecord(boolean isAddInFuncs, short numberOfSheets) {
// else not 'External References'
field_1_number_of_sheets = numberOfSheets;
field_2_encoded_url = null;
field_3_sheet_names = null;
_isAddInFunctions = isAddInFuncs;
}
public SupBookRecord(UnicodeString url, UnicodeString[] sheetNames) {
field_1_number_of_sheets = (short) sheetNames.length;
field_2_encoded_url = url;
field_3_sheet_names = sheetNames;
_isAddInFunctions = false;
}
/**
* Constructs a Extern Sheet record and sets its fields appropriately.
*
* @param in the RecordInputstream to read the record from
* @param id id must be 0x16 or an exception will be throw upon validation
* @param size the size of the data area of the record
* @param data data of the record (should not contain sid/len)
*/
public SupBookRecord(RecordInputStream in)
{
public SupBookRecord(RecordInputStream in) {
super(in);
}
protected void validateSid(short id)
{
if (id != sid)
{
throw new RecordFormatException("NOT An Supbook RECORD");
protected void validateSid(short id) {
if (id != sid) {
throw new RecordFormatException("NOT An ExternSheet RECORD");
}
}
/**
* @param in the RecordInputstream to read the record from
*/
protected void fillFields(RecordInputStream in)
{
//For now We use it only for one case
//When we need to add an named range when no named ranges was
//before it
field_1_number_of_sheets = in.readShort();
field_2_flag = in.readShort();
public boolean isExternalReferences() {
return field_3_sheet_names != null;
}
public boolean isInternalReferences() {
return field_3_sheet_names == null && !_isAddInFunctions;
}
public boolean isAddInFunctions() {
return field_3_sheet_names == null && _isAddInFunctions;
}
/**
* called by the constructor, should set class level fields. Should throw
* runtime exception for bad/incomplete data.
*
* @param data raw data
* @param size size of data
* @param offset of the record's data (provided a big array of the file)
*/
protected void fillFields(RecordInputStream in) {
field_1_number_of_sheets = in.readShort();
if(in.getLength() > SMALL_RECORD_SIZE) {
// 5.38.1 External References
_isAddInFunctions = false;
field_2_encoded_url = in.readUnicodeString();
UnicodeString[] sheetNames = new UnicodeString[field_1_number_of_sheets];
for (int i = 0; i < sheetNames.length; i++) {
sheetNames[i] = in.readUnicodeString();
}
field_3_sheet_names = sheetNames;
return;
}
// else not 'External References'
field_2_encoded_url = null;
field_3_sheet_names = null;
short nextShort = in.readShort();
if(nextShort == TAG_INTERNAL_REFERENCES) {
// 5.38.2 'Internal References'
_isAddInFunctions = false;
} else if(nextShort == TAG_ADD_IN_FUNCTIONS) {
// 5.38.3 'Add-In Functions'
_isAddInFunctions = true;
if(field_1_number_of_sheets != 1) {
throw new RuntimeException("Expected 0x0001 for number of sheets field in 'Add-In Functions' but got ("
+ field_1_number_of_sheets + ")");
}
} else {
throw new RuntimeException("invalid EXTERNALBOOK code ("
+ Integer.toHexString(nextShort) + ")");
}
}
public String toString()
{
StringBuffer buffer = new StringBuffer();
buffer.append("[SUPBOOK]\n");
buffer.append("numberosheets = ").append(getNumberOfSheets()).append('\n');
buffer.append("flag = ").append(getFlag()).append('\n');
buffer.append("[/SUPBOOK]\n");
return buffer.toString();
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName()).append(" [SUPBOOK ");
if(isExternalReferences()) {
sb.append("External References");
sb.append(" nSheets=").append(field_1_number_of_sheets);
sb.append(" url=").append(field_2_encoded_url);
} else if(_isAddInFunctions) {
sb.append("Add-In Functions");
} else {
sb.append("Internal References ");
sb.append(" nSheets= ").append(field_1_number_of_sheets);
}
return sb.toString();
}
private int getDataSize() {
if(!isExternalReferences()) {
return SMALL_RECORD_SIZE;
}
int sum = 2; // u16 number of sheets
UnicodeRecordStats urs = new UnicodeRecordStats();
field_2_encoded_url.getRecordSize(urs);
sum += urs.recordSize;
for(int i=0; i<field_3_sheet_names.length; i++) {
urs = new UnicodeRecordStats();
field_3_sheet_names[i].getRecordSize(urs);
sum += urs.recordSize;
}
return sum;
}
/**
@ -92,14 +178,30 @@ public class SupBookRecord extends Record
* @param data byte array containing instance data
* @return number of bytes written
*/
public int serialize(int offset, byte [] data)
{
public int serialize(int offset, byte [] data) {
LittleEndian.putShort(data, 0 + offset, sid);
LittleEndian.putShort(data, 2 + offset, (short) 4);
int dataSize = getDataSize();
LittleEndian.putShort(data, 2 + offset, (short) dataSize);
LittleEndian.putShort(data, 4 + offset, field_1_number_of_sheets);
LittleEndian.putShort(data, 6 + offset, field_2_flag);
return getRecordSize();
if(isExternalReferences()) {
int currentOffset = 6 + offset;
UnicodeRecordStats urs = new UnicodeRecordStats();
field_2_encoded_url.serialize(urs, currentOffset, data);
currentOffset += urs.recordSize;
for(int i=0; i<field_3_sheet_names.length; i++) {
urs = new UnicodeRecordStats();
field_3_sheet_names[i].serialize(urs, currentOffset, data);
currentOffset += urs.recordSize;
}
} else {
short field2val = _isAddInFunctions ? TAG_ADD_IN_FUNCTIONS : TAG_INTERNAL_REFERENCES;
LittleEndian.putShort(data, 6 + offset, field2val);
}
return dataSize + 4;
}
public void setNumberOfSheets(short number){
@ -110,21 +212,18 @@ public class SupBookRecord extends Record
return field_1_number_of_sheets;
}
public void setFlag(short flag){
field_2_flag = flag;
}
public short getFlag() {
return field_2_flag;
}
public int getRecordSize()
{
return 4 + 4;
public int getRecordSize() {
return getDataSize() + 4;
}
public short getSid()
{
return sid;
}
public UnicodeString getURL() {
return field_2_encoded_url;
}
public UnicodeString[] getSheetNames() {
return (UnicodeString[]) field_3_sheet_names.clone();
}
}

View File

@ -0,0 +1,111 @@
/* ====================================================================
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.constant;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.record.UnicodeString;
import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats;
/**
* To support Constant Values (2.5.7) as required by the CRN record.
* This class should probably also be used for two dimensional arrays which are encoded by
* EXTERNALNAME (5.39) records and Array tokens.<p/>
* TODO - code in ArrayPtg should be merged with this code. It currently supports only 2 of the constant types
*
* @author Josh Micich
*/
public final class ConstantValueParser {
// note - value 3 seems to be unused
private static final int TYPE_EMPTY = 0;
private static final int TYPE_NUMBER = 1;
private static final int TYPE_STRING = 2;
private static final int TYPE_BOOLEAN = 4;
private static final int TRUE_ENCODING = 1;
private static final int FALSE_ENCODING = 0;
// TODO - is this the best way to represent 'EMPTY'?
private static final Object EMPTY_REPRESENTATION = null;
private ConstantValueParser() {
// no instances of this class
}
public static Object[] parse(RecordInputStream in, int nValues) {
Object[] result = new Object[nValues];
for (int i = 0; i < result.length; i++) {
result[i] = readAConstantValue(in);
}
return result;
}
private static Object readAConstantValue(RecordInputStream in) {
byte grbit = in.readByte();
switch(grbit) {
case TYPE_EMPTY:
in.readLong(); // 8 byte 'not used' field
return EMPTY_REPRESENTATION;
case TYPE_NUMBER:
return new Double(in.readDouble());
case TYPE_STRING:
return in.readUnicodeString();
case TYPE_BOOLEAN:
return readBoolean(in);
}
return null;
}
private static Object readBoolean(RecordInputStream in) {
byte val = in.readByte();
in.readLong(); // 8 byte 'not used' field
switch(val) {
case FALSE_ENCODING:
return Boolean.FALSE;
case TRUE_ENCODING:
return Boolean.TRUE;
}
// Don't tolerate unusual boolean encoded values (unless it becomes evident that they occur)
throw new RuntimeException("unexpected boolean encoding (" + val + ")");
}
public static int getEncodedSize(Object[] values) {
// start with one byte 'type' code for each value
int result = values.length * 1;
for (int i = 0; i < values.length; i++) {
result += getEncodedSize(values[i]);
}
return 0;
}
/**
* @return encoded size without the 'type' code byte
*/
private static int getEncodedSize(Object object) {
if(object == EMPTY_REPRESENTATION) {
return 8;
}
Class cls = object.getClass();
if(cls == Boolean.class || cls == Double.class) {
return 8;
}
UnicodeString strVal = (UnicodeString)object;
UnicodeRecordStats urs = new UnicodeRecordStats();
strVal.getRecordSize(urs);
return urs.recordSize;
}
}

View File

@ -29,10 +29,13 @@ import org.apache.poi.hssf.model.Workbook;
* @author Andrew C. Oliver (acoliver at apache dot org)
*/
public abstract class AbstractFunctionPtg extends OperationPtg {
//constant used allow a ptgAttr to be mapped properly for its functionPtg
public static final String ATTR_NAME = "specialflag";
public static final short INDEX_EXTERNAL = 255;
/**
* The name of the IF function (i.e. "IF"). Extracted as a constant for clarity.
*/
public static final String FUNCTION_NAME_IF = "IF";
/** All external functions have function index 255 */
private static final short FUNCTION_INDEX_EXTERNAL = 255;
private static BinaryTree map = produceHash();
protected static Object[][] functionData = produceFunctionData();
@ -66,6 +69,13 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
public String getName() {
return lookupName(field_2_fnc_index);
}
/**
* external functions get some special processing
* @return <code>true</code> if this is an external function
*/
public boolean isExternalFunction() {
return field_2_fnc_index == FUNCTION_INDEX_EXTERNAL;
}
public String toFormulaString(Workbook book) {
return getName();
@ -73,39 +83,57 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
public String toFormulaString(String[] operands) {
StringBuffer buf = new StringBuffer();
if (field_2_fnc_index != 1) {
buf.append(getName());
buf.append('(');
}
if (operands.length >0) {
for (int i=0;i<operands.length;i++) {
buf.append(operands[i]);
buf.append(',');
}
buf.deleteCharAt(buf.length()-1);
}
if (field_2_fnc_index != 1) {
buf.append(")");
}
if(isExternalFunction()) {
buf.append(operands[0]); // first operand is actually the function name
appendArgs(buf, 1, operands);
} else {
buf.append(getName());
appendArgs(buf, 0, operands);
}
return buf.toString();
}
private static void appendArgs(StringBuffer buf, int firstArgIx, String[] operands) {
buf.append('(');
for (int i=firstArgIx;i<operands.length;i++) {
if (i>firstArgIx) {
buf.append(',');
}
buf.append(operands[i]);
}
buf.append(")");
}
public abstract void writeBytes(byte[] array, int offset);
public abstract int getSize();
/**
* Used to detect whether a function name found in a formula is one of the standard excel functions
* <p>
* The name matching is case insensitive.
* @return <code>true</code> if the name specifies a standard worksheet function,
* <code>false</code> if the name should be assumed to be an external function.
*/
public static final boolean isInternalFunctionName(String name) {
return map.containsValue(name.toUpperCase());
}
protected String lookupName(short index) {
return ((String)map.get(new Integer(index)));
}
protected short lookupIndex(String name) {
Integer index = (Integer) map.getKeyForValue(name);
/**
* Resolves internal function names into function indexes.
* <p>
* The name matching is case insensitive.
* @return the standard worksheet function index if found, otherwise <tt>FUNCTION_INDEX_EXTERNAL</tt>
*/
protected static short lookupIndex(String name) {
Integer index = (Integer) map.getKeyForValue(name.toUpperCase());
if (index != null) return index.shortValue();
return INDEX_EXTERNAL;
return FUNCTION_INDEX_EXTERNAL;
}
/**
@ -115,7 +143,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
BinaryTree dmap = new BinaryTree();
dmap.put(new Integer(0),"COUNT");
dmap.put(new Integer(1),"specialflag");
dmap.put(new Integer(1),FUNCTION_NAME_IF);
dmap.put(new Integer(2),"ISNA");
dmap.put(new Integer(3),"ISERROR");
dmap.put(new Integer(4),"SUM");
@ -354,7 +382,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
dmap.put(new Integer(252),"FREQUENCY");
dmap.put(new Integer(253),"ADDTOOLBAR");
dmap.put(new Integer(254),"DELETETOOLBAR");
dmap.put(new Integer(255),"externalflag");
dmap.put(new Integer(FUNCTION_INDEX_EXTERNAL),"externalflag");
dmap.put(new Integer(256),"RESETTOOLBAR");
dmap.put(new Integer(257),"EVALUATE");
dmap.put(new Integer(258),"GETTOOLBAR");

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -16,12 +15,6 @@
limitations under the License.
==================================================================== */
/*
* IntPtg.java
*
* Created on October 29, 2001, 7:37 PM
*/
package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndian;
@ -29,64 +22,45 @@ import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.RecordInputStream;
/**
* Integer (unsigned short intger)
* Integer (unsigned short integer)
* Stores an unsigned short value (java int) in a formula
* @author Andrew C. Oliver (acoliver at apache dot org)
* @author Jason Height (jheight at chariot dot net dot au)
*/
public final class IntPtg extends Ptg {
// 16 bit unsigned integer
private static final int MIN_VALUE = 0x0000;
private static final int MAX_VALUE = 0xFFFF;
/**
* Excel represents integers 0..65535 with the tInt token.
* @return <code>true</code> if the specified value is within the range of values
* <tt>IntPtg</tt> can represent.
*/
public static boolean isInRange(int i) {
return i>=MIN_VALUE && i <=MAX_VALUE;
}
public class IntPtg
extends Ptg
{
public final static int SIZE = 3;
public final static byte sid = 0x1e;
private int field_1_value;
private IntPtg() {
//Required for clone methods
public IntPtg(RecordInputStream in) {
this(in.readUShort());
}
public IntPtg(RecordInputStream in)
{
setValue(in.readUShort());
}
// IntPtg should be able to create itself, shouldnt have to call setValue
public IntPtg(String formulaToken) {
setValue(Integer.parseInt(formulaToken));
}
/**
* Sets the wrapped value.
* Normally you should call with a positive int.
*/
public void setValue(int value)
{
if(value < 0 || value > (Short.MAX_VALUE + 1)*2 )
throw new IllegalArgumentException("Unsigned short is out of range: " + value);
public IntPtg(int value) {
if(!isInRange(value)) {
throw new IllegalArgumentException("value is out of range: " + value);
}
field_1_value = value;
}
/**
* Returns the value as a short, which may have
* been wrapped into negative numbers
*/
public int getValue()
{
public int getValue() {
return field_1_value;
}
/**
* Returns the value as an unsigned positive int.
*/
public int getValueAsInt()
{
if(field_1_value < 0) {
return (Short.MAX_VALUE + 1)*2 + field_1_value;
}
return field_1_value;
}
public void writeBytes(byte [] array, int offset)
{
@ -94,20 +68,25 @@ public class IntPtg
LittleEndian.putUShort(array, offset + 1, getValue());
}
public int getSize()
{
public int getSize() {
return SIZE;
}
public String toFormulaString(Workbook book)
{
return "" + getValue();
public String toFormulaString(Workbook book) {
return String.valueOf(getValue());
}
public byte getDefaultOperandClass() {
return Ptg.CLASS_VALUE;
}
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
public Object clone() {
IntPtg ptg = new IntPtg();
ptg.field_1_value = field_1_value;
return ptg;
}
public Object clone() {
return new IntPtg(field_1_value);
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");
sb.append(field_1_value);
sb.append("]");
return sb.toString();
}
}

View File

@ -33,6 +33,7 @@ public class NamePtg
{
public final static short sid = 0x23;
private final static int SIZE = 5;
/** one-based index to defined name record */
private short field_1_label_index;
private short field_2_zero; // reserved must be 0
boolean xtra=false;
@ -42,24 +43,32 @@ public class NamePtg
//Required for clone methods
}
/** Creates new NamePtg */
public NamePtg(String name, Workbook book)
{
final short n = (short) (book.getNumNames() + 1);
/**
* Creates new NamePtg and sets its name index to that of the corresponding defined name record
* in the workbook. The search for the name record is case insensitive. If it is not found,
* it gets created.
*/
public NamePtg(String name, Workbook book) {
field_1_label_index = (short)(1+getOrCreateNameRecord(book, name)); // convert to 1-based
}
/**
* @return zero based index of the found or newly created defined name record.
*/
private static final int getOrCreateNameRecord(Workbook book, String name) {
// perhaps this logic belongs in Workbook
int countNames = book.getNumNames();
NameRecord rec;
for (short i = 1; i < n; i++) {
rec = book.getNameRecord(i - 1);
for (int i = 0; i < countNames; i++) {
rec = book.getNameRecord(i);
if (name.equalsIgnoreCase(rec.getNameText())) {
field_1_label_index = i;
return;
return i;
}
}
rec = new NameRecord();
rec.setNameText(name);
rec.setNameTextLength((byte) name.length());
book.addName(rec);
field_1_label_index = n;
return countNames;
}
/** Creates new NamePtg */
@ -71,6 +80,13 @@ public class NamePtg
field_2_zero = in.readShort();
//if (data[offset+6]==0) xtra=true;
}
/**
* @return zero based index to a defined name record in the LinkTable
*/
public int getIndex() {
return field_1_label_index-1; // convert to zero based
}
public void writeBytes(byte [] array, int offset)
{

View File

@ -25,13 +25,11 @@ import org.apache.poi.hssf.record.RecordInputStream;
*
* @author aviks
*/
public class NameXPtg extends Ptg
{
public final class NameXPtg extends Ptg {
public final static short sid = 0x39;
private final static int SIZE = 7;
private short field_1_ixals; // index to externsheet record
private short field_2_ilbl; //index to name or externname table(1 based)
private short field_1_ixals; // index to REF entry in externsheet record
private short field_2_ilbl; //index to defined name or externname table(1 based)
private short field_3_reserved; // reserved must be 0
@ -41,13 +39,6 @@ public class NameXPtg extends Ptg
/** Creates new NamePtg */
public NameXPtg(String name)
{
//TODO
}
/** Creates new NamePtg */
public NameXPtg(RecordInputStream in)
{
field_1_ixals = in.readShort();
@ -72,7 +63,8 @@ public class NameXPtg extends Ptg
public String toFormulaString(Workbook book)
{
return "NO IDEA - NAME";
// -1 to convert definedNameIndex from 1-based to zero-based
return book.resolveNameXText(field_1_ixals, field_2_ilbl-1);
}
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}

View File

@ -639,7 +639,7 @@ public class HSSFCell
//only set to default if there is no extended format index already set
if (rec.getXFIndex() == (short)0) rec.setXFIndex(( short ) 0x0f);
FormulaParser fp = new FormulaParser(formula+";",book);
FormulaParser fp = new FormulaParser(formula, book);
fp.parse();
Ptg[] ptg = fp.getRPNPtg();
int size = 0;

View File

@ -400,7 +400,7 @@ public class HSSFSheet
//formula fields ( size and data )
String str_formula = obj_validation.getFirstFormula();
FormulaParser fp = new FormulaParser(str_formula+";",book);
FormulaParser fp = new FormulaParser(str_formula, book);
fp.parse();
Stack ptg_arr = new Stack();
Ptg[] ptg = fp.getRPNPtg();
@ -424,7 +424,7 @@ public class HSSFSheet
if ( obj_validation.getSecondFormula() != null )
{
str_formula = obj_validation.getSecondFormula();
fp = new FormulaParser(str_formula+";",book);
fp = new FormulaParser(str_formula, book);
fp.parse();
ptg_arr = new Stack();
ptg = fp.getRPNPtg();

View File

@ -245,6 +245,16 @@ public class LittleEndian
putNumber(data, offset, value, SHORT_SIZE);
}
/**
* executes:<p/>
* <code>
* data[offset] = (byte)value;
* </code></p>
* Added for consistency with other put~() methods
*/
public static void putByte(byte[] data, int offset, int value) {
putNumber(data, offset, value, LittleEndianConsts.BYTE_SIZE);
}
/**
* put a array of shorts into a byte array

View File

@ -43,8 +43,6 @@ public class AddEval extends NumericOperationEval {
private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@ -59,33 +57,31 @@ public class AddEval extends NumericOperationEval {
}
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
Eval retval = null;
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
if(args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
double d = 0;
switch (operands.length) {
default: // will rarely happen. currently the parser itself fails.
retval = ErrorEval.UNKNOWN_ERROR;
break;
case 2:
for (int i = 0, iSize = 2; retval==null && i < iSize; i++) {
ValueEval ve = singleOperandEvaluate(operands[i], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d += ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
retval = ErrorEval.VALUE_INVALID;
}
} // end for inside case
} // end switch
if (retval == null) {
retval = Double.isNaN(d) ? (ValueEval) ErrorEval.VALUE_INVALID : new NumberEval(d);
for (int i = 0; i < 2; i++) {
ValueEval ve = singleOperandEvaluate(args[i], srcRow, srcCol);
if(ve instanceof ErrorEval) {
return ve;
}
if (ve instanceof NumericValueEval) {
d += ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
return ErrorEval.VALUE_INVALID;
}
}
return retval;
if(Double.isNaN(d) || Double.isInfinite(d)) {
return ErrorEval.NUM_ERROR;
}
return new NumberEval(d);
}
public int getNumberOfOperands() {

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.ConcatPtg;
@ -27,7 +24,7 @@ import org.apache.poi.hssf.record.formula.Ptg;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class ConcatEval extends StringOperationEval {
public final class ConcatEval extends StringOperationEval {
private ConcatPtg delegate;
@ -35,36 +32,27 @@ public class ConcatEval extends StringOperationEval {
this.delegate = (ConcatPtg) ptg;
}
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
Eval retval = null;
StringBuffer sb = null;
switch (operands.length) {
default: // paranoid check :)
retval = ErrorEval.UNKNOWN_ERROR;
break;
case 2:
sb = new StringBuffer();
for (int i = 0, iSize = 2; retval == null && i < iSize; i++) {
ValueEval ve = singleOperandEvaluate(operands[i], srcRow, srcCol);
if (ve instanceof StringValueEval) {
StringValueEval sve = (StringValueEval) ve;
sb.append(sve.getStringValue());
}
else if (ve instanceof BlankEval) {
// do nothing
}
else { // must be an error eval
retval = ve;
}
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
if(args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 2; i++) {
ValueEval ve = singleOperandEvaluate(args[i], srcRow, srcCol);
if (ve instanceof StringValueEval) {
StringValueEval sve = (StringValueEval) ve;
sb.append(sve.getStringValue());
}
else if (ve instanceof BlankEval) {
// do nothing
}
else { // must be an error eval
return ve;
}
}
if (retval == null) {
retval = new StringEval(sb.toString());
}
return retval;
return new StringEval(sb.toString());
}
public int getNumberOfOperands() {

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
@ -27,15 +24,13 @@ import org.apache.poi.hssf.record.formula.DividePtg;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class DivideEval extends NumericOperationEval {
public final class DivideEval extends NumericOperationEval {
private DividePtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@ -49,18 +44,28 @@ public class DivideEval extends NumericOperationEval {
return NUM_XLATOR;
}
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
if(args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
Eval retval = null;
double d0 = 0;
double d1 = 0;
switch (operands.length) {
default: // will rarely happen. currently the parser itself fails.
retval = ErrorEval.UNKNOWN_ERROR;
break;
case 2:
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d0 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
retval = ErrorEval.VALUE_INVALID;
}
if (retval == null) { // no error yet
ve = singleOperandEvaluate(args[1], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d0 = ((NumericValueEval) ve).getNumberValue();
d1 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
@ -68,20 +73,7 @@ public class DivideEval extends NumericOperationEval {
else {
retval = ErrorEval.VALUE_INVALID;
}
if (retval == null) { // no error yet
ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d1 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
retval = ErrorEval.VALUE_INVALID;
}
}
} // end switch
}
if (retval == null) {
retval = (d1 == 0)

View File

@ -48,12 +48,6 @@ public final class ErrorEval implements ValueEval {
private static final int CIRCULAR_REF_ERROR_CODE = 0xFFFFFFC4;
private static final int FUNCTION_NOT_IMPLEMENTED_CODE = 0xFFFFFFE2;
/**
* @deprecated do not use this error code. For conditions that should never occur, throw an
* unchecked exception. For all other situations use the error code that corresponds to the
* error Excel would have raised under the same circumstances.
*/
public static final ErrorEval UNKNOWN_ERROR = new ErrorEval(-20);
public static final ErrorEval FUNCTION_NOT_IMPLEMENTED = new ErrorEval(FUNCTION_NOT_IMPLEMENTED_CODE);
// Note - Excel does not seem to represent this condition with an error code
public static final ErrorEval CIRCULAR_REF_ERROR = new ErrorEval(CIRCULAR_REF_ERROR_CODE);

View File

@ -0,0 +1,81 @@
/* ====================================================================
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.functions.FreeRefFunction;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
*
* Common entry point for all external functions (where
* <tt>AbstractFunctionPtg.field_2_fnc_index</tt> == 255)
*
* @author Josh Micich
*/
final class ExternalFunction implements FreeRefFunction {
public ValueEval evaluate(Eval[] args, int srcCellRow, short srcCellCol, HSSFWorkbook workbook, HSSFSheet sheet) {
int nIncomingArgs = args.length;
if(nIncomingArgs < 1) {
throw new RuntimeException("function name argument missing");
}
if (!(args[0] instanceof NameEval)) {
throw new RuntimeException("First argument should be a NameEval, but got ("
+ args[0].getClass().getName() + ")");
}
NameEval functionNameEval = (NameEval) args[0];
int nOutGoingArgs = nIncomingArgs -1;
Eval[] outGoingArgs = new Eval[nOutGoingArgs];
System.arraycopy(args, 1, outGoingArgs, 0, nOutGoingArgs);
FreeRefFunction targetFunc;
try {
targetFunc = findTargetFunction(workbook, functionNameEval);
} catch (EvaluationException e) {
return e.getErrorEval();
}
return targetFunc.evaluate(outGoingArgs, srcCellRow, srcCellCol, workbook, sheet);
}
private FreeRefFunction findTargetFunction(HSSFWorkbook workbook, NameEval functionNameEval) throws EvaluationException {
int numberOfNames = workbook.getNumberOfNames();
int nameIndex = functionNameEval.getIndex();
if(nameIndex < 0 || nameIndex >= numberOfNames) {
throw new RuntimeException("Bad name index (" + nameIndex
+ "). Allowed range is (0.." + (numberOfNames-1) + ")");
}
String functionName = workbook.getNameName(nameIndex);
if(false) {
System.out.println("received call to external function index (" + functionName + ")");
}
// TODO - detect if the NameRecord corresponds to a named range, function, or something undefined
// throw the right errors in these cases
// TODO find the implementation for the external function e.g. "YEARFRAC" or "ISEVEN"
throw new EvaluationException(ErrorEval.FUNCTION_NOT_IMPLEMENTED);
}
}

View File

@ -38,7 +38,8 @@ public abstract class FunctionEval implements OperationEval {
public static final int OFFSET = 78;
/** 148 */
public static final int INDIRECT = 148;
/** 255 */
public static final int EXTERNAL_FUNC = 255;
}
// convenient access to namespace
private static final FunctionID ID = null;
@ -51,6 +52,7 @@ public abstract class FunctionEval implements OperationEval {
Map m = new HashMap();
addMapping(m, ID.OFFSET, new Offset());
addMapping(m, ID.INDIRECT, new Indirect());
addMapping(m, ID.EXTERNAL_FUNC, new ExternalFunction());
freeRefFunctionsByIdMap = m;
}
private static void addMapping(Map m, int offset, FreeRefFunction frf) {
@ -316,7 +318,7 @@ public abstract class FunctionEval implements OperationEval {
retval[252] = new Frequency(); // FREQUENCY
retval[253] = new NotImplementedFunction(); // ADDTOOLBAR
retval[254] = new NotImplementedFunction(); // DELETETOOLBAR
retval[255] = new NotImplementedFunction(); // EXTERNALFLAG
retval[ID.EXTERNAL_FUNC] = null; // ExternalFunction is a FreeREfFunction
retval[256] = new NotImplementedFunction(); // RESETTOOLBAR
retval[257] = new Evaluate(); // EVALUATE
retval[258] = new NotImplementedFunction(); // GETTOOLBAR

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
@ -27,15 +24,13 @@ import org.apache.poi.hssf.record.formula.MultiplyPtg;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class MultiplyEval extends NumericOperationEval {
public final class MultiplyEval extends NumericOperationEval {
private MultiplyPtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@ -49,46 +44,39 @@ public class MultiplyEval extends NumericOperationEval {
return NUM_XLATOR;
}
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
Eval retval = null;
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
if(args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
double d0 = 0;
double d1 = 0;
switch (operands.length) {
default: // will rarely happen. currently the parser itself fails.
retval = ErrorEval.UNKNOWN_ERROR;
break;
case 2:
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d0 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
retval = ErrorEval.VALUE_INVALID;
}
if (retval == null) { // no error yet
ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d1 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
retval = ErrorEval.VALUE_INVALID;
}
}
} // end switch
if (retval == null) {
retval = (Double.isNaN(d0) || Double.isNaN(d1))
? (ValueEval) ErrorEval.VALUE_INVALID
: new NumberEval(d0 * d1);
ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d0 = ((NumericValueEval) ve).getNumberValue();
}
return retval;
else if (ve instanceof BlankEval) {
// do nothing
}
else {
return ErrorEval.VALUE_INVALID;
}
ve = singleOperandEvaluate(args[1], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d1 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
return ErrorEval.VALUE_INVALID;
}
if (Double.isNaN(d0) || Double.isNaN(d1)) {
return ErrorEval.NUM_ERROR;
}
return new NumberEval(d0 * d1);
}
public int getNumberOfOperands() {

View File

@ -0,0 +1,48 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.eval;
/**
* @author Josh Micich
*/
public final class NameEval implements Eval {
private final int _index;
/**
* @param index zero based index to a defined name record
*/
public NameEval(int index) {
_index = index;
}
/**
* @return zero based index to a defined name record
*/
public int getIndex() {
return _index;
}
public String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");
sb.append(_index);
sb.append("]");
return sb.toString();
}
}

View File

@ -17,7 +17,6 @@
package org.apache.poi.hssf.record.formula.eval;
/**
* Provides functionality for evaluating arguments to functions and operators.
*
@ -43,12 +42,10 @@ public final class OperandResolver {
*/
public static ValueEval getSingleValue(Eval arg, int srcCellRow, short srcCellCol)
throws EvaluationException {
if (arg instanceof RefEval) {
RefEval re = (RefEval) arg;
return re.getInnerValueEval();
}
Eval result;
if (arg instanceof AreaEval) {
if (arg instanceof RefEval) {
result = ((RefEval) arg).getInnerValueEval();
} else if (arg instanceof AreaEval) {
result = chooseSingleElementFromArea((AreaEval) arg, srcCellRow, srcCellCol);
} else {
result = arg;
@ -223,13 +220,26 @@ public final class OperandResolver {
* Some examples:<br/>
* " 123 " -&gt; 123.0<br/>
* ".123" -&gt; 0.123<br/>
* These not supported yet:<br/>
* " $ 1,000.00 " -&gt; 1000.0<br/>
* "$1.25E4" -&gt; 12500.0<br/>
* "5**2" -&gt; 500<br/>
* "250%" -&gt; 2.5<br/>
*
* @param text
* @return <code>null</code> if the specified text cannot be parsed as a number
*/
public static Double parseDouble(String text) {
public static Double parseDouble(String pText) {
String text = pText.trim();
if(text.length() < 1) {
return null;
}
boolean isPositive = true;
if(text.charAt(0) == '-') {
isPositive = false;
text= text.substring(1).trim();
}
if(!Character.isDigit(text.charAt(0))) {
// avoid using NumberFormatException to tell when string is not a number
return null;
@ -242,8 +252,26 @@ public final class OperandResolver {
} catch (NumberFormatException e) {
return null;
}
return new Double(val);
return new Double(isPositive ? +val : -val);
}
/**
* @param ve must be a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>, or <tt>BlankEval</tt>
* @return the converted string value. never <code>null</code>
*/
public static String coerceValueToString(ValueEval ve) {
if (ve instanceof StringValueEval) {
StringValueEval sve = (StringValueEval) ve;
return sve.getStringValue();
}
if (ve instanceof NumberEval) {
NumberEval neval = (NumberEval) ve;
return neval.getStringValue();
}
if (ve instanceof BlankEval) {
return "";
}
throw new IllegalArgumentException("Unexpected eval class (" + ve.getClass().getName() + ")");
}
}

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
@ -27,15 +24,13 @@ import org.apache.poi.hssf.record.formula.PowerPtg;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class PowerEval extends NumericOperationEval {
public final class PowerEval extends NumericOperationEval {
private PowerPtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@ -49,48 +44,40 @@ public class PowerEval extends NumericOperationEval {
return NUM_XLATOR;
}
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
Eval retval = null;
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
if(args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
double d0 = 0;
double d1 = 0;
switch (operands.length) {
default: // will rarely happen. currently the parser itself fails.
retval = ErrorEval.UNKNOWN_ERROR;
break;
case 2:
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d0 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
retval = ErrorEval.VALUE_INVALID;
}
if (retval == null) { // no error yet
ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d1 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
retval = ErrorEval.VALUE_INVALID;
}
}
} // end switch
if (retval == null) {
double p = Math.pow(d0, d1);
retval = (Double.isNaN(p))
? (ValueEval) ErrorEval.VALUE_INVALID
: new NumberEval(p);
ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d0 = ((NumericValueEval) ve).getNumberValue();
}
return retval;
else if (ve instanceof BlankEval) {
// do nothing
}
else {
return ErrorEval.VALUE_INVALID;
}
ve = singleOperandEvaluate(args[1], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d1 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
return ErrorEval.VALUE_INVALID;
}
double p = Math.pow(d0, d1);
if (Double.isNaN(p)) {
return ErrorEval.VALUE_INVALID;
}
return new NumberEval(p);
}
public int getNumberOfOperands() {

View File

@ -14,47 +14,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 9, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.ReferencePtg;
/**
* @author adeshmukh
*
*/
public class Ref2DEval implements RefEval {
public final class Ref2DEval implements RefEval {
private ValueEval value;
private ReferencePtg delegate;
private final ValueEval value;
private final ReferencePtg delegate;
private boolean evaluated;
public Ref2DEval(Ptg ptg, ValueEval value, boolean evaluated) {
this.value = value;
this.delegate = (ReferencePtg) ptg;
this.evaluated = evaluated;
public Ref2DEval(ReferencePtg ptg, ValueEval ve) {
value = ve;
delegate = ptg;
}
public ValueEval getInnerValueEval() {
return value;
}
public short getRow() {
return delegate.getRow();
}
public short getColumn() {
return delegate.getColumn();
}
public boolean isEvaluated() {
return evaluated;
}
}

View File

@ -14,13 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 9, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
/**
@ -29,36 +25,23 @@ import org.apache.poi.hssf.record.formula.Ref3DPtg;
*/
public final class Ref3DEval implements RefEval {
private ValueEval value;
private final ValueEval value;
private final Ref3DPtg delegate;
private Ref3DPtg delegate;
private boolean evaluated;
public Ref3DEval(Ptg ptg, ValueEval value, boolean evaluated) {
this.value = value;
this.delegate = (Ref3DPtg) ptg;
this.evaluated = evaluated;
public Ref3DEval(Ref3DPtg ptg, ValueEval ve) {
value = ve;
delegate = ptg;
}
public ValueEval getInnerValueEval() {
return value;
}
public short getRow() {
return delegate.getRow();
}
public short getColumn() {
return delegate.getColumn();
}
public boolean isEvaluated() {
return evaluated;
}
public int getExternSheetIndex() {
return delegate.getExternSheetIndex();
}
}

View File

@ -14,11 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 9, 2005
*
*
*/
package org.apache.poi.hssf.record.formula.eval;
/**
@ -52,18 +48,4 @@ public interface RefEval extends ValueEval {
* returns the row index.
*/
public short getRow();
/**
* returns true if this RefEval contains an
* evaluated value instead of a direct value.
* eg. say cell A1 has the value: ="test"
* Then the RefEval representing A1 will return
* isEvaluated() equal to false. On the other
* hand, say cell A1 has the value: =B1 and
* B1 has the value "test", then the RefEval
* representing A1 will return isEvaluated()
* equal to true.
*/
public boolean isEvaluated();
}

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
@ -27,15 +24,13 @@ import org.apache.poi.hssf.record.formula.SubtractPtg;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class SubtractEval extends NumericOperationEval {
public final class SubtractEval extends NumericOperationEval {
private SubtractPtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@ -49,18 +44,28 @@ public class SubtractEval extends NumericOperationEval {
return NUM_XLATOR;
}
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
if(args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
Eval retval = null;
double d0 = 0;
double d1 = 0;
switch (operands.length) {
default: // will rarely happen. currently the parser itself fails.
retval = ErrorEval.UNKNOWN_ERROR;
break;
case 2:
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d0 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
retval = ErrorEval.VALUE_INVALID;
}
if (retval == null) { // no error yet
ve = singleOperandEvaluate(args[1], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d0 = ((NumericValueEval) ve).getNumberValue();
d1 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
@ -68,21 +73,8 @@ public class SubtractEval extends NumericOperationEval {
else {
retval = ErrorEval.VALUE_INVALID;
}
if (retval == null) { // no error yet
ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d1 = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else {
retval = ErrorEval.VALUE_INVALID;
}
}
} // end switch
}
if (retval == null) {
retval = (Double.isNaN(d0) || Double.isNaN(d1))
? (ValueEval) ErrorEval.VALUE_INVALID

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
@ -27,14 +24,12 @@ import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class UnaryMinusEval extends NumericOperationEval {
public final class UnaryMinusEval extends NumericOperationEval {
private UnaryMinusPtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@ -49,32 +44,24 @@ public class UnaryMinusEval extends NumericOperationEval {
return NUM_XLATOR;
}
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
ValueEval retval = null;
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
if(args.length != 1) {
return ErrorEval.VALUE_INVALID;
}
double d = 0;
switch (operands.length) {
default:
retval = ErrorEval.UNKNOWN_ERROR;
break;
case 1:
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else if (ve instanceof ErrorEval) {
retval = ve;
}
ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
if (ve instanceof NumericValueEval) {
d = ((NumericValueEval) ve).getNumberValue();
}
else if (ve instanceof BlankEval) {
// do nothing
}
else if (ve instanceof ErrorEval) {
return ve;
}
if (retval == null) {
retval = new NumberEval(-d);
}
return retval;
return new NumberEval(-d);
}
public int getNumberOfOperands() {

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg;
@ -27,114 +24,38 @@ import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class UnaryPlusEval implements OperationEval /*extends NumericOperationEval*/ {
public final class UnaryPlusEval implements OperationEval {
private UnaryPlusPtg delegate;
/*
* COMMENT FOR COMMENTED CODE IN THIS FILE
*
* In excel the programmer seems to not have cared to
* think about how strings were handled in other numeric
* operations when he/she was implementing this operation :P
*
* Here's what I mean:
*
* Q. If the formula -"hello" evaluates to #VALUE! in excel, what should
* the formula +"hello" evaluate to?
*
* A. +"hello" evaluates to "hello" (what the...?)
*
*/
// private static final ValueEvalToNumericXlator NUM_XLATOR =
// new ValueEvalToNumericXlator((short)
// ( ValueEvalToNumericXlator.BOOL_IS_PARSED
// | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
// | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
// | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
// | ValueEvalToNumericXlator.STRING_IS_PARSED
// ));
/**
* called by reflection
*/
public UnaryPlusEval(Ptg ptg) {
this.delegate = (UnaryPlusPtg) ptg;
}
// protected ValueEvalToNumericXlator getXlator() {
// return NUM_XLATOR;
// }
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
ValueEval retval = null;
switch (operands.length) {
default:
retval = ErrorEval.UNKNOWN_ERROR;
break;
case 1:
// ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
// if (ve instanceof NumericValueEval) {
// d = ((NumericValueEval) ve).getNumberValue();
// }
// else if (ve instanceof BlankEval) {
// // do nothing
// }
// else if (ve instanceof ErrorEval) {
// retval = ve;
// }
if (operands[0] instanceof RefEval) {
RefEval re = (RefEval) operands[0];
retval = re.getInnerValueEval();
}
else if (operands[0] instanceof AreaEval) {
AreaEval ae = (AreaEval) operands[0];
if (ae.contains(srcRow, srcCol)) { // circular ref!
retval = ErrorEval.CIRCULAR_REF_ERROR;
}
else if (ae.isRow()) {
if (ae.containsColumn(srcCol)) {
ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
if (ve instanceof RefEval) {
ve = ((RefEval) ve).getInnerValueEval();
}
retval = ve;
}
else {
retval = ErrorEval.VALUE_INVALID;
}
}
else if (ae.isColumn()) {
if (ae.containsRow(srcRow)) {
ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
if (ve instanceof RefEval) {
ve = ((RefEval) ve).getInnerValueEval();
}
retval = ve;
}
else {
retval = ErrorEval.VALUE_INVALID;
}
}
else {
retval = ErrorEval.VALUE_INVALID;
}
}
else {
retval = (ValueEval) operands[0];
}
}
if (retval instanceof BlankEval) {
retval = new NumberEval(0);
}
return retval;
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
if(args.length != 1) {
return ErrorEval.VALUE_INVALID;
}
double d;
try {
ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
if(ve instanceof BlankEval) {
return NumberEval.ZERO;
}
if(ve instanceof StringEval) {
// Note - asymmetric with UnaryMinus
// -"hello" evaluates to #VALUE!
// but +"hello" evaluates to "hello"
return ve;
}
d = OperandResolver.coerceValueToDouble(ve);
} catch (EvaluationException e) {
return e.getErrorEval();
}
return new NumberEval(+d);
}
public int getNumberOfOperands() {
@ -144,5 +65,4 @@ public class UnaryPlusEval implements OperationEval /*extends NumericOperationEv
public int getType() {
return delegate.getType();
}
}

View File

@ -24,7 +24,7 @@ package org.apache.poi.hssf.record.formula.eval;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class ValueEvalToNumericXlator {
public final class ValueEvalToNumericXlator {
public static final int STRING_IS_PARSED = 0x0001;
public static final int BOOL_IS_PARSED = 0x0002;
@ -34,26 +34,18 @@ public class ValueEvalToNumericXlator {
public static final int REF_BOOL_IS_PARSED = 0x0010;
public static final int REF_BLANK_IS_PARSED = 0x0020;
public static final int EVALUATED_REF_STRING_IS_PARSED = 0x0040;
public static final int EVALUATED_REF_BOOL_IS_PARSED = 0x0080;
public static final int EVALUATED_REF_BLANK_IS_PARSED = 0x0100;
public static final int STRING_TO_BOOL_IS_PARSED = 0x0200;
public static final int REF_STRING_TO_BOOL_IS_PARSED = 0x0400;
public static final int STRING_IS_INVALID_VALUE = 0x0800;
public static final int REF_STRING_IS_INVALID_VALUE = 0x1000;
// public static final int BOOL_IS_BLANK = 0x2000;
// public static final int REF_BOOL_IS_BLANK = 0x4000;
// public static final int STRING_IS_BLANK = 0x8000;
// public static final int REF_STRING_IS_BLANK = 0x10000;
private final int flags;
public ValueEvalToNumericXlator(int flags) {
this.flags = flags;
if (false) { // uncomment to see who is using this class
System.err.println(new Throwable().getStackTrace()[1].getClassName() + "\t0x"
+ Integer.toHexString(flags).toUpperCase());
}
this.flags = flags;
}
/**
@ -71,7 +63,7 @@ public class ValueEvalToNumericXlator {
// most common case - least worries :)
else if (eval instanceof NumberEval) {
retval = (NumberEval) eval;
retval = eval;
}
// booleval
@ -125,50 +117,33 @@ public class ValueEvalToNumericXlator {
* @param eval
*/
private ValueEval xlateRefEval(RefEval reval) {
ValueEval retval = null;
ValueEval eval = (ValueEval) reval.getInnerValueEval();
ValueEval eval = reval.getInnerValueEval();
// most common case - least worries :)
if (eval instanceof NumberEval) {
retval = (NumberEval) eval;
return eval;
}
// booleval
else if (eval instanceof BoolEval) {
retval = ((flags & REF_BOOL_IS_PARSED) > 0)
if (eval instanceof BoolEval) {
return ((flags & REF_BOOL_IS_PARSED) > 0)
? (ValueEval) eval
: BlankEval.INSTANCE;
}
// stringeval
else if (eval instanceof StringEval) {
retval = xlateRefStringEval((StringEval) eval);
if (eval instanceof StringEval) {
return xlateRefStringEval((StringEval) eval);
}
// erroreval
else if (eval instanceof ErrorEval) {
retval = eval;
if (eval instanceof ErrorEval) {
return eval;
}
// refeval
else if (eval instanceof RefEval) {
RefEval re = (RefEval) eval;
retval = xlateRefEval(re);
if (eval instanceof BlankEval) {
return xlateBlankEval(REF_BLANK_IS_PARSED);
}
else if (eval instanceof BlankEval) {
retval = xlateBlankEval(reval.isEvaluated() ? EVALUATED_REF_BLANK_IS_PARSED : REF_BLANK_IS_PARSED);
}
// probably AreaEval ? then not acceptable.
else {
throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass());
}
return retval;
throw new RuntimeException("Invalid ValueEval type passed for conversion: ("
+ eval.getClass().getName() + ")");
}
/**
@ -176,93 +151,38 @@ public class ValueEvalToNumericXlator {
* @param eval
*/
private ValueEval xlateStringEval(StringEval eval) {
ValueEval retval = null;
if ((flags & STRING_IS_PARSED) > 0) {
String s = eval.getStringValue();
try {
double d = Double.parseDouble(s);
retval = new NumberEval(d);
}
catch (Exception e) {
if ((flags & STRING_TO_BOOL_IS_PARSED) > 0) {
try {
boolean b = Boolean.getBoolean(s);
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
}
catch (Exception e2) { retval = ErrorEval.VALUE_INVALID; }
}
else {
retval = ErrorEval.VALUE_INVALID;
}
Double d = OperandResolver.parseDouble(s);
if(d == null) {
return ErrorEval.VALUE_INVALID;
}
return new NumberEval(d.doubleValue());
}
else if ((flags & STRING_TO_BOOL_IS_PARSED) > 0) {
String s = eval.getStringValue();
try {
boolean b = Boolean.getBoolean(s);
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
}
catch (Exception e) { retval = ErrorEval.VALUE_INVALID; }
}
// strings are errors?
else if ((flags & STRING_IS_INVALID_VALUE) > 0) {
retval = ErrorEval.VALUE_INVALID;
if ((flags & STRING_IS_INVALID_VALUE) > 0) {
return ErrorEval.VALUE_INVALID;
}
// ignore strings
else {
retval = xlateBlankEval(BLANK_IS_PARSED);
}
return retval;
return xlateBlankEval(BLANK_IS_PARSED);
}
/**
* uses the relevant flags to decode the StringEval
* @param eval
*/
private ValueEval xlateRefStringEval(StringEval eval) {
ValueEval retval = null;
private ValueEval xlateRefStringEval(StringEval sve) {
if ((flags & REF_STRING_IS_PARSED) > 0) {
StringEval sve = (StringEval) eval;
String s = sve.getStringValue();
try {
double d = Double.parseDouble(s);
retval = new NumberEval(d);
}
catch (Exception e) {
if ((flags & REF_STRING_TO_BOOL_IS_PARSED) > 0) {
try {
boolean b = Boolean.getBoolean(s);
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
}
catch (Exception e2) { retval = ErrorEval.VALUE_INVALID; }
}
else {
retval = ErrorEval.VALUE_INVALID;
}
Double d = OperandResolver.parseDouble(s);
if(d == null) {
return ErrorEval.VALUE_INVALID;
}
return new NumberEval(d.doubleValue());
}
else if ((flags & REF_STRING_TO_BOOL_IS_PARSED) > 0) {
StringEval sve = (StringEval) eval;
String s = sve.getStringValue();
try {
boolean b = Boolean.getBoolean(s);
retval = b ? BoolEval.TRUE : BoolEval.FALSE;;
}
catch (Exception e) { retval = ErrorEval.VALUE_INVALID; }
}
// strings are errors?
else if ((flags & REF_STRING_IS_INVALID_VALUE) > 0) {
retval = ErrorEval.VALUE_INVALID;
}
// strings are blanks
else {
retval = BlankEval.INSTANCE;
}
return retval;
return BlankEval.INSTANCE;
}
}

View File

@ -33,8 +33,8 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
public class Avedev extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (0
// ValueEvalToNumericXlator.BOOL_IS_PARSED
new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
@ -44,7 +44,6 @@ public class Avedev extends MultiOperandNumericFunction {
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
));
/**

View File

@ -33,8 +33,8 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
public class Average extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (0
// ValueEvalToNumericXlator.BOOL_IS_PARSED
new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
@ -44,7 +44,6 @@ public class Average extends MultiOperandNumericFunction {
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
));
/**

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on Jun 20, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
@ -38,13 +35,10 @@ public abstract class FinanceFunction extends NumericFunction {
new ValueEvalToNumericXlator((short) (0
| ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.BLANK_IS_PARSED
| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
@ -56,11 +50,11 @@ public abstract class FinanceFunction extends NumericFunction {
* if they desire to return a different ValueEvalToNumericXlator instance
* than the default.
*/
protected ValueEvalToNumericXlator getXlator() {
protected final ValueEvalToNumericXlator getXlator() {
return DEFAULT_NUM_XLATOR;
}
protected ValueEval singleOperandNumericAsBoolean(Eval eval, int srcRow, short srcCol) {
protected final ValueEval singleOperandNumericAsBoolean(Eval eval, int srcRow, short srcCol) {
ValueEval retval = null;
retval = singleOperandEvaluate(eval, srcRow, srcCol);
if (retval instanceof NumericValueEval) {
@ -74,5 +68,4 @@ public abstract class FinanceFunction extends NumericFunction {
}
return retval;
}
}

View File

@ -28,28 +28,22 @@ import org.apache.poi.hssf.record.formula.eval.Eval;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class If implements Function {
public final class If implements Function {
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
public Eval evaluate(Eval[] evals, int srcCellRow, short srcCellCol) {
Eval retval = null;
Eval evalWhenFalse = BoolEval.FALSE;
switch (evals.length) {
switch (args.length) {
case 3:
evalWhenFalse = evals[2];
evalWhenFalse = args[2];
case 2:
BoolEval beval = (BoolEval) evals[0];
BoolEval beval = (BoolEval) args[0]; // TODO - class cast exception
if (beval.getBooleanValue()) {
retval = evals[1];
return args[1];
}
else {
retval = evalWhenFalse;
}
break;
return evalWhenFalse;
default:
retval = ErrorEval.UNKNOWN_ERROR;
return ErrorEval.VALUE_INVALID;
}
return retval;
}
}

View File

@ -14,79 +14,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 15, 2005
*
*/
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.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.RefEval;
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;
*
*/
public class Isblank implements Function {
public final class Isblank implements Function {
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
ValueEval retval = null;
boolean b = false;
switch (operands.length) {
default:
retval = ErrorEval.VALUE_INVALID;
break;
case 1:
if (operands[0] instanceof BlankEval) {
b = true;
}
else if (operands[0] instanceof AreaEval) {
AreaEval ae = (AreaEval) operands[0];
if (ae.contains(srcCellRow, srcCellCol)) { // circular ref!
retval = ErrorEval.CIRCULAR_REF_ERROR;
}
else if (ae.isRow()) {
if (ae.containsColumn(srcCellCol)) {
ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCellCol);
b = (ve instanceof BlankEval);
}
else {
b = false;
}
}
else if (ae.isColumn()) {
if (ae.containsRow(srcCellRow)) {
ValueEval ve = ae.getValueAt(srcCellRow, ae.getFirstColumn());
b = (ve instanceof BlankEval);
}
else {
b = false;
}
}
else {
b = false;
}
}
else if (operands[0] instanceof RefEval) {
RefEval re = (RefEval) operands[0];
b = (!re.isEvaluated()) && re.getInnerValueEval() instanceof BlankEval;
}
else {
b = false;
}
}
if (retval == null) {
retval = b
? BoolEval.TRUE
: BoolEval.FALSE;
}
return retval;
}
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
if(args.length != 1) {
return ErrorEval.VALUE_INVALID;
}
Eval arg = args[0];
ValueEval singleCellValue;
try {
singleCellValue = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
} catch (EvaluationException e) {
return BoolEval.FALSE;
}
return BoolEval.valueOf(singleCellValue instanceof BlankEval);
}
}

View File

@ -14,125 +14,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 15, 2005
*
*/
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.BlankEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
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.StringValueEval;
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;
*
*/
public class Len extends TextFunction {
public final class Len extends TextFunction {
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
if(args.length != 1) {
return ErrorEval.VALUE_INVALID;
}
try {
ValueEval veval = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
ValueEval retval = null;
String s = null;
switch (operands.length) {
default:
retval = ErrorEval.VALUE_INVALID;
break;
case 1:
ValueEval ve = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
if (ve instanceof StringValueEval) {
StringValueEval sve = (StringValueEval) ve;
s = sve.getStringValue();
}
else if (ve instanceof RefEval) {
RefEval re = (RefEval) ve;
ValueEval ive = re.getInnerValueEval();
if (ive instanceof BlankEval) {
s = re.isEvaluated() ? "0" : null;
}
else if (ive instanceof StringValueEval) {
s = ((StringValueEval) ive).getStringValue();
}
else if (ive instanceof BlankEval) {}
else {
retval = ErrorEval.VALUE_INVALID;
}
}
else if (ve instanceof BlankEval) {}
else {
retval = ErrorEval.VALUE_INVALID;
break;
}
}
if (retval == null) {
s = (s == null) ? EMPTY_STRING : s;
retval = new NumberEval(s.length());
}
return retval;
}
protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) {
ValueEval retval;
if (eval instanceof AreaEval) {
AreaEval ae = (AreaEval) eval;
if (ae.contains(srcRow, srcCol)) { // circular ref!
retval = ErrorEval.CIRCULAR_REF_ERROR;
}
else if (ae.isRow()) {
if (ae.containsColumn(srcCol)) {
ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
retval = attemptXlateToText(ve);
}
else {
retval = ErrorEval.VALUE_INVALID;
}
}
else if (ae.isColumn()) {
if (ae.containsRow(srcRow)) {
ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
retval = attemptXlateToText(ve);
}
else {
retval = ErrorEval.VALUE_INVALID;
}
}
else {
retval = ErrorEval.VALUE_INVALID;
}
}
else {
retval = attemptXlateToText((ValueEval) eval);
}
return retval;
}
/**
* converts from Different ValueEval types to StringEval.
* Note: AreaEvals are not handled, if arg is an AreaEval,
* the returned value is ErrorEval.VALUE_INVALID
* @param ve
*/
protected ValueEval attemptXlateToText(ValueEval ve) {
ValueEval retval;
if (ve instanceof StringValueEval || ve instanceof RefEval) {
retval = ve;
}
else if (ve instanceof BlankEval) {
retval = ve;
}
else {
retval = ErrorEval.VALUE_INVALID;
}
return retval;
}
String str = OperandResolver.coerceValueToString(veval);
return new NumberEval(str.length());
} catch (EvaluationException e) {
return e.getErrorEval();
}
}
}

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
@ -30,12 +27,11 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class Maxa extends MultiOperandNumericFunction {
public final class Maxa extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED

View File

@ -23,7 +23,6 @@ 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.StringEval;
import org.apache.poi.hssf.record.formula.eval.StringValueEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
@ -35,7 +34,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
*
* @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
*/
public class Mid extends TextFunction {
public class Mid implements Function {
/**
* Returns a specific number of characters from a text string, starting at
* the position you specify, based on the number of characters you specify.
@ -52,7 +51,8 @@ public class Mid extends TextFunction {
int numChars;
try {
text = evaluateTextArg(args[0], srcCellRow, srcCellCol);
ValueEval evText = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
text = OperandResolver.coerceValueToString(evText);
int startCharNum = evaluateNumberArg(args[1], srcCellRow, srcCellCol);
numChars = evaluateNumberArg(args[2], srcCellRow, srcCellCol);
startIx = startCharNum - 1; // convert to zero-based
@ -79,7 +79,7 @@ public class Mid extends TextFunction {
}
public static int evaluateNumberArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
private static int evaluateNumberArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
if (ev instanceof BlankEval) {
// Note - for start_num arg, blank causes error(#VALUE!),
@ -89,12 +89,4 @@ public class Mid extends TextFunction {
return OperandResolver.coerceValueToInt(ev);
}
private static String evaluateTextArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
if (ev instanceof StringValueEval) {
return ((StringValueEval) ev).getStringValue();
}
throw EvaluationException.invalidValue();
}
}

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
@ -35,7 +32,6 @@ public class Mina extends MultiOperandNumericFunction {
new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED

View File

@ -76,32 +76,9 @@ public abstract class MultiOperandNumericFunction extends NumericFunction {
}
}
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
));
private static final int DEFAULT_MAX_NUM_OPERANDS = 30;
/**
* this is the default impl for the factory method getXlator
* of the super class NumericFunction. Subclasses can override this method
* if they desire to return a different ValueEvalToNumericXlator instance
* than the default.
*/
protected ValueEvalToNumericXlator getXlator() {
return DEFAULT_NUM_XLATOR;
}
protected abstract ValueEvalToNumericXlator getXlator();
/**
* Maximum number of operands accepted by this function.
@ -160,9 +137,7 @@ public abstract class MultiOperandNumericFunction extends NumericFunction {
* HSSFFormulaEvaluator where we store an array
* of RefEvals as the "values" array.
*/
RefEval re = (values[j] instanceof RefEval)
? new Ref2DEval(null, ((RefEval) values[j]).getInnerValueEval(), true)
: new Ref2DEval(null, values[j], false);
RefEval re = new Ref2DEval(null, values[j]);
ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol);
if (ve instanceof NumericValueEval) {

View File

@ -39,10 +39,8 @@ public abstract class NumericFunction implements Function {
new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE

View File

@ -33,8 +33,8 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
public class Stdev extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (0
// ValueEvalToNumericXlator.BOOL_IS_PARSED
new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
@ -44,7 +44,6 @@ public class Stdev extends MultiOperandNumericFunction {
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
));
/**

View File

@ -33,18 +33,18 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
public class Sumsq extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (
// ValueEvalToNumericXlator.BOOL_IS_PARSED
ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
| ValueEvalToNumericXlator.BLANK_IS_PARSED
//| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
//| ValueEvalToNumericXlator.BLANK_IS_PARSED
));
protected ValueEvalToNumericXlator getXlator() {

View File

@ -14,50 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions;
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.NumberEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* Implementation of Excel function SUMX2MY2()<p/>
*
* Calculates the sum of differences of squares in two arrays of the same size.<br/>
* <b>Syntax</b>:<br/>
* <b>SUMX2MY2</b>(<b>arrayX</b>, <b>arrayY</b>)<p/>
*
* result = &Sigma;<sub>i: 0..n</sub>(x<sub>i</sub><sup>2</sup>-y<sub>i</sub><sup>2</sup>)
*
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class Sumx2my2 extends XYNumericFunction {
public final class Sumx2my2 extends XYNumericFunction {
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
ValueEval retval = null;
double[][] values = null;
int checkLen = 0; // check to see that all array lengths are equal
switch (operands.length) {
default:
retval = ErrorEval.VALUE_INVALID;
break;
case 2:
values = getValues(operands, srcCellRow, srcCellCol);
if (values==null
|| values[X] == null || values[Y] == null
|| values[X].length == 0 || values[Y].length == 0
|| values[X].length != values[Y].length) {
retval = ErrorEval.VALUE_INVALID;
}
}
if (retval == null) {
double d = MathX.sumx2my2(values[X], values[Y]);
retval = (Double.isNaN(d) || Double.isInfinite(d))
? (ValueEval) ErrorEval.NUM_ERROR
: new NumberEval(d);
}
return retval;
protected double evaluate(double[] xArray, double[] yArray) {
return MathX.sumx2my2(xArray, yArray);
}
}

View File

@ -14,50 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions;
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.NumberEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* Implementation of Excel function SUMX2PY2()<p/>
*
* Calculates the sum of squares in two arrays of the same size.<br/>
* <b>Syntax</b>:<br/>
* <b>SUMX2PY2</b>(<b>arrayX</b>, <b>arrayY</b>)<p/>
*
* result = &Sigma;<sub>i: 0..n</sub>(x<sub>i</sub><sup>2</sup>+y<sub>i</sub><sup>2</sup>)
*
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class Sumx2py2 extends XYNumericFunction {
public final class Sumx2py2 extends XYNumericFunction {
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
ValueEval retval = null;
double[][] values = null;
int checkLen = 0; // check to see that all array lengths are equal
switch (operands.length) {
default:
retval = ErrorEval.VALUE_INVALID;
break;
case 2:
values = getValues(operands, srcCellRow, srcCellCol);
if (values==null
|| values[X] == null || values[Y] == null
|| values[X].length == 0 || values[Y].length == 0
|| values[X].length != values[Y].length) {
retval = ErrorEval.VALUE_INVALID;
}
}
if (retval == null) {
double d = MathX.sumx2py2(values[X], values[Y]);
retval = (Double.isNaN(d) || Double.isInfinite(d))
? (ValueEval) ErrorEval.NUM_ERROR
: new NumberEval(d);
}
return retval;
protected double evaluate(double[] xArray, double[] yArray) {
return MathX.sumx2py2(xArray, yArray);
}
}

View File

@ -14,50 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions;
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.NumberEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* Implementation of Excel function SUMXMY2()<p/>
*
* Calculates the sum of squares of differences between two arrays of the same size.<br/>
* <b>Syntax</b>:<br/>
* <b>SUMXMY2</b>(<b>arrayX</b>, <b>arrayY</b>)<p/>
*
* result = &Sigma;<sub>i: 0..n</sub>(x<sub>i</sub>-y<sub>i</sub>)<sup>2</sup>
*
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public class Sumxmy2 extends XYNumericFunction {
public final class Sumxmy2 extends XYNumericFunction {
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
ValueEval retval = null;
double[][] values = null;
int checkLen = 0; // check to see that all array lengths are equal
switch (operands.length) {
default:
retval = ErrorEval.VALUE_INVALID;
break;
case 2:
values = getValues(operands, srcCellRow, srcCellCol);
if (values==null
|| values[X] == null || values[Y] == null
|| values[X].length == 0 || values[Y].length == 0
|| values[X].length != values[Y].length) {
retval = ErrorEval.VALUE_INVALID;
}
}
if (retval == null) {
double d = MathX.sumxmy2(values[X], values[Y]);
retval = (Double.isNaN(d) || Double.isInfinite(d))
? (ValueEval) ErrorEval.NUM_ERROR
: new NumberEval(d);
}
return retval;
protected double evaluate(double[] xArray, double[] yArray) {
return MathX.sumxmy2(xArray, yArray);
}
}

View File

@ -16,12 +16,11 @@
*/
package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
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.StringEval;
import org.apache.poi.hssf.record.formula.eval.StringValueEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
@ -30,46 +29,25 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
* value is string.
* @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt;
*/
public class Trim extends TextFunction {
public final class Trim extends TextFunction {
/**
* Removes leading and trailing spaces from value if evaluated
* operand value is string.
* Returns StringEval only if evaluated operand is of type string
* (and is not blank or null) or number. If evaluated operand is
* of type string and is blank or null, or if evaluated operand is
* of type blank, returns BlankEval. Otherwise returns ErrorEval.
*
* @see org.apache.poi.hssf.record.formula.eval.Eval
*/
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
Eval retval = ErrorEval.VALUE_INVALID;
String str = null;
switch (operands.length) {
default:
break;
case 1:
ValueEval veval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
if (veval instanceof StringValueEval) {
StringValueEval sve = (StringValueEval) veval;
str = sve.getStringValue();
if (str == null || str.trim().equals("")) {
return BlankEval.INSTANCE;
}
}
else if (veval instanceof NumberEval) {
NumberEval neval = (NumberEval) veval;
str = neval.getStringValue();
}
else if (veval instanceof BlankEval) {
return BlankEval.INSTANCE;
}
}
if (str != null) {
retval = new StringEval(str.trim());
}
return retval;
}
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
if(args.length != 1) {
return ErrorEval.VALUE_INVALID;
}
try {
ValueEval veval = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
String str = OperandResolver.coerceValueToString(veval);
str = str.trim();
if(str.length() < 1) {
return StringEval.EMPTY_INSTANCE;
}
return new StringEval(str);
} catch (EvaluationException e) {
return e.getErrorEval();
}
}
}

View File

@ -14,154 +14,151 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Created on May 29, 2005
*
*/
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.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
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.ValueEvalToNumericXlator;
/**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/
public abstract class XYNumericFunction extends NumericFunction {
public abstract class XYNumericFunction implements Function {
protected static final int X = 0;
protected static final int Y = 1;
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
));
/**
* this is the default impl for the factory method getXlator
* of the super class NumericFunction. Subclasses can override this method
* if they desire to return a different ValueEvalToNumericXlator instance
* than the default.
*/
protected ValueEvalToNumericXlator getXlator() {
return DEFAULT_NUM_XLATOR;
}
protected int getMaxNumOperands() {
return 30;
protected static final class DoubleArrayPair {
private final double[] _xArray;
private final double[] _yArray;
public DoubleArrayPair(double[] xArray, double[] yArray) {
_xArray = xArray;
_yArray = yArray;
}
public double[] getXArray() {
return _xArray;
}
public double[] getYArray() {
return _yArray;
}
}
public final Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
if(args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
double[][] values;
try {
values = getValues(args[0], args[1]);
} catch (EvaluationException e) {
return e.getErrorEval();
}
if (values==null
|| 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 new NumberEval(d);
}
protected abstract double evaluate(double[] xArray, double[] yArray);
/**
* Returns a double array that contains values for the numeric cells
* from among the list of operands. Blanks and Blank equivalent cells
* are ignored. Error operands or cells containing operands of type
* that are considered invalid and would result in #VALUE! error in
* excel cause this function to return null.
*
* @param xops
* @param yops
* @param srcRow
* @param srcCol
*/
protected double[][] getNumberArray(Eval[] xops, Eval[] yops, int srcRow, short srcCol) {
double[][] retval = new double[2][30];
private static double[][] getNumberArray(Eval[] xops, Eval[] yops) throws EvaluationException {
// check for errors first: size mismatch, value errors in x, value errors in y
int nArrayItems = xops.length;
if(nArrayItems != yops.length) {
throw new EvaluationException(ErrorEval.NA);
}
for (int i = 0; i < xops.length; i++) {
Eval eval = xops[i];
if (eval instanceof ErrorEval) {
throw new EvaluationException((ErrorEval) eval);
}
}
for (int i = 0; i < yops.length; i++) {
Eval eval = yops[i];
if (eval instanceof ErrorEval) {
throw new EvaluationException((ErrorEval) eval);
}
}
double[] xResult = new double[nArrayItems];
double[] yResult = new double[nArrayItems];
int count = 0;
if (xops.length > getMaxNumOperands()
|| yops.length > getMaxNumOperands()
|| xops.length != yops.length) {
retval = null;
}
else {
for (int i=0, iSize=xops.length; i<iSize; i++) {
Eval xEval = xops[i];
Eval yEval = yops[i];
if (isNumberEval(xEval) && isNumberEval(yEval)) {
retval[X] = ensureCapacity(retval[X], count);
retval[Y] = ensureCapacity(retval[Y], count);
retval[X][count] = getDoubleValue(xEval);
retval[Y][count] = getDoubleValue(yEval);
if (Double.isNaN(retval[X][count]) || Double.isNaN(retval[Y][count])) {
retval = null;
break;
}
count++;
}
}
}
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 (retval != null) {
double[][] temp = retval;
retval[X] = trimToSize(retval[X], count);
retval[Y] = trimToSize(retval[Y], count);
}
return retval;
return new double[][] {
trimToSize(xResult, count),
trimToSize(yResult, count),
};
}
protected double[][] getValues(Eval[] operands, int srcCellRow, short srcCellCol) {
double[][] retval = null;
outer: do {
if (operands.length == 2) {
Eval[] xEvals = new Eval[1];
Eval[] yEvals = new Eval[1];
if (operands[X] instanceof AreaEval) {
AreaEval ae = (AreaEval) operands[0];
xEvals = ae.getValues();
}
else if (operands[X] instanceof ErrorEval) {
break outer;
}
else {
xEvals[0] = operands[X];
}
if (operands[Y] instanceof AreaEval) {
AreaEval ae = (AreaEval) operands[Y];
yEvals = ae.getValues();
}
else if (operands[Y] instanceof ErrorEval) {
break outer;
}
else {
yEvals[0] = operands[Y];
}
retval = getNumberArray(xEvals, yEvals, srcCellRow, srcCellCol);
}
} while (false);
return retval;
private static double[][] getValues(Eval argX, Eval argY) throws EvaluationException {
if (argX instanceof ErrorEval) {
throw new EvaluationException((ErrorEval) argX);
}
if (argY instanceof ErrorEval) {
throw new EvaluationException((ErrorEval) argY);
}
Eval[] xEvals;
Eval[] yEvals;
if (argX instanceof AreaEval) {
AreaEval ae = (AreaEval) argX;
xEvals = ae.getValues();
} else {
xEvals = new Eval[] { argX, };
}
if (argY instanceof AreaEval) {
AreaEval ae = (AreaEval) argY;
yEvals = ae.getValues();
} else {
yEvals = new Eval[] { argY, };
}
return getNumberArray(xEvals, yEvals);
}
protected static double[] ensureCapacity(double[] arr, int pos) {
double[] temp = arr;
while (pos >= arr.length) {
arr = new double[arr.length << 2];
}
if (temp.length != arr.length)
System.arraycopy(temp, 0, arr, 0, temp.length);
return arr;
}
protected static double[] trimToSize(double[] arr, int len) {
private static double[] trimToSize(double[] arr, int len) {
double[] tarr = arr;
if (arr.length > len) {
tarr = new double[len];
@ -170,7 +167,7 @@ public abstract class XYNumericFunction extends NumericFunction {
return tarr;
}
protected static boolean isNumberEval(Eval eval) {
private static boolean isNumberEval(Eval eval) {
boolean retval = false;
if (eval instanceof NumberEval) {
@ -185,7 +182,7 @@ public abstract class XYNumericFunction extends NumericFunction {
return retval;
}
protected static double getDoubleValue(Eval eval) {
private static double getDoubleValue(Eval eval) {
double retval = 0;
if (eval instanceof NumberEval) {
NumberEval ne = (NumberEval) eval;

View File

@ -80,6 +80,7 @@ import org.apache.poi.hssf.record.formula.eval.GreaterThanEval;
import org.apache.poi.hssf.record.formula.eval.LessEqualEval;
import org.apache.poi.hssf.record.formula.eval.LessThanEval;
import org.apache.poi.hssf.record.formula.eval.MultiplyEval;
import org.apache.poi.hssf.record.formula.eval.NameEval;
import org.apache.poi.hssf.record.formula.eval.NotEqualEval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.OperationEval;
@ -360,16 +361,16 @@ public class HSSFFormulaEvaluator {
EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) {
return ErrorEval.CIRCULAR_REF_ERROR;
return ErrorEval.CIRCULAR_REF_ERROR;
}
try {
return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula());
return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula());
} finally {
tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
}
}
private static ValueEval evaluateCell(HSSFWorkbook workbook, HSSFSheet sheet,
int srcRowNum, short srcColNum, String cellFormulaText) {
int srcRowNum, short srcColNum, String cellFormulaText) {
FormulaParser parser = new FormulaParser(cellFormulaText, workbook.getWorkbook());
parser.parse();
Ptg[] ptgs = parser.getRPNPtg();
@ -380,15 +381,24 @@ public class HSSFFormulaEvaluator {
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
// since we don't know how to handle these yet :(
if (ptgs[i] instanceof ControlPtg) { continue; }
if (ptgs[i] instanceof MemErrPtg) { continue; }
if (ptgs[i] instanceof MissingArgPtg) { continue; }
if (ptgs[i] instanceof NamePtg) { continue; }
if (ptgs[i] instanceof NameXPtg) { continue; }
if (ptgs[i] instanceof UnknownPtg) { continue; }
Ptg ptg = ptgs[i];
if (ptg instanceof ControlPtg) { continue; }
if (ptg instanceof MemErrPtg) { continue; }
if (ptg instanceof MissingArgPtg) { continue; }
if (ptg instanceof NamePtg) {
// named ranges, macro functions
NamePtg namePtg = (NamePtg) ptg;
stack.push(new NameEval(namePtg.getIndex()));
continue;
}
if (ptg instanceof NameXPtg) {
// TODO - external functions
continue;
}
if (ptg instanceof UnknownPtg) { continue; }
if (ptgs[i] instanceof OperationPtg) {
OperationPtg optg = (OperationPtg) ptgs[i];
if (ptg instanceof OperationPtg) {
OperationPtg optg = (OperationPtg) ptg;
// parens can be ignored since we have RPN tokens
if (optg instanceof ParenthesisPtg) { continue; }
@ -408,47 +418,67 @@ public class HSSFFormulaEvaluator {
Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
stack.push(opresult);
}
else if (ptgs[i] instanceof ReferencePtg) {
ReferencePtg ptg = (ReferencePtg) ptgs[i];
short colnum = ptg.getColumn();
short rownum = ptg.getRow();
else if (ptg instanceof ReferencePtg) {
ReferencePtg refPtg = (ReferencePtg) ptg;
short colnum = refPtg.getColumn();
short rownum = refPtg.getRow();
HSSFRow row = sheet.getRow(rownum);
HSSFCell cell = (row != null) ? row.getCell(colnum) : null;
stack.push(createRef2DEval(ptg, cell, row, sheet, workbook));
stack.push(createRef2DEval(refPtg, cell, row, sheet, workbook));
}
else if (ptgs[i] instanceof Ref3DPtg) {
Ref3DPtg ptg = (Ref3DPtg) ptgs[i];
short colnum = ptg.getColumn();
short rownum = ptg.getRow();
else if (ptg instanceof Ref3DPtg) {
Ref3DPtg refPtg = (Ref3DPtg) ptg;
short colnum = refPtg.getColumn();
short rownum = refPtg.getRow();
Workbook wb = workbook.getWorkbook();
HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(ptg.getExternSheetIndex()));
HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex()));
HSSFRow row = xsheet.getRow(rownum);
HSSFCell cell = (row != null) ? row.getCell(colnum) : null;
stack.push(createRef3DEval(ptg, cell, row, xsheet, workbook));
stack.push(createRef3DEval(refPtg, cell, row, xsheet, workbook));
}
else if (ptgs[i] instanceof AreaPtg) {
AreaPtg ap = (AreaPtg) ptgs[i];
else if (ptg instanceof AreaPtg) {
AreaPtg ap = (AreaPtg) ptg;
AreaEval ae = evaluateAreaPtg(sheet, workbook, ap);
stack.push(ae);
}
else if (ptgs[i] instanceof Area3DPtg) {
Area3DPtg a3dp = (Area3DPtg) ptgs[i];
else if (ptg instanceof Area3DPtg) {
Area3DPtg a3dp = (Area3DPtg) ptg;
AreaEval ae = evaluateArea3dPtg(workbook, a3dp);
stack.push(ae);
}
else {
Eval ptgEval = getEvalForPtg(ptgs[i]);
Eval ptgEval = getEvalForPtg(ptg);
stack.push(ptgEval);
}
}
ValueEval value = ((ValueEval) stack.pop());
if (value instanceof RefEval) {
RefEval rv = (RefEval) value;
if (!stack.isEmpty()) {
throw new IllegalStateException("evaluation stack not empty");
}
value = dereferenceValue(value, srcRowNum, srcColNum);
if (value instanceof BlankEval) {
// Note Excel behaviour here. A blank final final value is converted to zero.
return NumberEval.ZERO;
// 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().
}
return value;
}
/**
* 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.
* @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
* <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
*/
private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) {
if (evaluationResult instanceof RefEval) {
RefEval rv = (RefEval) evaluationResult;
return rv.getInnerValueEval();
}
if (value instanceof AreaEval) {
AreaEval ae = (AreaEval) value;
if (evaluationResult instanceof AreaEval) {
AreaEval ae = (AreaEval) evaluationResult;
if (ae.isRow()) {
if(ae.isColumn()) {
return ae.getValues()[0];
@ -460,7 +490,7 @@ public class HSSFFormulaEvaluator {
}
return ErrorEval.VALUE_INVALID;
}
return value;
return evaluationResult;
}
private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum,
@ -486,7 +516,7 @@ public class HSSFFormulaEvaluator {
// (eg C:C)
// TODO: Handle whole column ranges properly
if(row1 == -1 && row0 >= 0) {
row1 = (short)sheet.getLastRowNum();
row1 = (short)sheet.getLastRowNum();
}
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
@ -514,7 +544,7 @@ public class HSSFFormulaEvaluator {
// (eg C:C)
// TODO: Handle whole column ranges properly
if(row1 == -1 && row0 >= 0) {
row1 = (short)xsheet.getLastRowNum();
row1 = (short)xsheet.getLastRowNum();
}
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
@ -631,22 +661,22 @@ public class HSSFFormulaEvaluator {
private static Ref2DEval createRef2DEval(ReferencePtg ptg, HSSFCell cell,
HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
if (cell == null) {
return new Ref2DEval(ptg, BlankEval.INSTANCE, false);
return new Ref2DEval(ptg, BlankEval.INSTANCE);
}
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_NUMERIC:
return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()), false);
return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()));
case HSSFCell.CELL_TYPE_STRING:
return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()), false);
return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
case HSSFCell.CELL_TYPE_FORMULA:
return new Ref2DEval(ptg, internalEvaluate(cell, row, sheet, workbook), true);
return new Ref2DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
case HSSFCell.CELL_TYPE_BOOLEAN:
return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()), false);
return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
case HSSFCell.CELL_TYPE_BLANK:
return new Ref2DEval(ptg, BlankEval.INSTANCE, false);
return new Ref2DEval(ptg, BlankEval.INSTANCE);
case HSSFCell.CELL_TYPE_ERROR:
return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()), false);
return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
}
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
}
@ -657,21 +687,21 @@ public class HSSFFormulaEvaluator {
private static Ref3DEval createRef3DEval(Ref3DPtg ptg, HSSFCell cell,
HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
if (cell == null) {
return new Ref3DEval(ptg, BlankEval.INSTANCE, false);
return new Ref3DEval(ptg, BlankEval.INSTANCE);
}
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_NUMERIC:
return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()), false);
return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()));
case HSSFCell.CELL_TYPE_STRING:
return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()), false);
return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
case HSSFCell.CELL_TYPE_FORMULA:
return new Ref3DEval(ptg, internalEvaluate(cell, row, sheet, workbook), true);
return new Ref3DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
case HSSFCell.CELL_TYPE_BOOLEAN:
return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()), false);
return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
case HSSFCell.CELL_TYPE_BLANK:
return new Ref3DEval(ptg, BlankEval.INSTANCE, false);
return new Ref3DEval(ptg, BlankEval.INSTANCE);
case HSSFCell.CELL_TYPE_ERROR:
return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()), false);
return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
}
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
}

View File

@ -16,14 +16,14 @@
==================================================================== */
package org.apache.poi.hssf.eventusermodel;
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
import org.apache.poi.hssf.eventusermodel.HSSFListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord;
@ -31,31 +31,33 @@ import org.apache.poi.hssf.record.LabelSSTRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import junit.framework.TestCase;
public class TestMissingRecordAwareHSSFListener extends TestCase {
private String dirname;
/**
* Tests for MissingRecordAwareHSSFListener
*/
public final class TestMissingRecordAwareHSSFListener extends TestCase {
public TestMissingRecordAwareHSSFListener() {
dirname = System.getProperty("HSSF.testdata.path");
}
public void testMissingRowRecords() throws Exception {
private Record[] r;
public void setUp() {
String dirname = System.getProperty("HSSF.testdata.path");
File f = new File(dirname + "/MissingBits.xls");
HSSFRequest req = new HSSFRequest();
MockHSSFListener mockListen = new MockHSSFListener();
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
req.addListenerForAllRecords(listener);
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
HSSFEventFactory factory = new HSSFEventFactory();
factory.processWorkbookEvents(req, fs);
// Check we got the dummy records
Record[] r = (Record[])
mockListen.records.toArray(new Record[mockListen.records.size()]);
try {
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
factory.processWorkbookEvents(req, fs);
} catch (IOException e) {
throw new RuntimeException(e);
}
r = mockListen.getRecords();
}
public void testMissingRowRecords() throws Exception {
// We have rows 0, 1, 2, 20 and 21
int row0 = -1;
@ -105,20 +107,6 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
}
public void testEndOfRowRecords() throws Exception {
File f = new File(dirname + "/MissingBits.xls");
HSSFRequest req = new HSSFRequest();
MockHSSFListener mockListen = new MockHSSFListener();
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
req.addListenerForAllRecords(listener);
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
HSSFEventFactory factory = new HSSFEventFactory();
factory.processWorkbookEvents(req, fs);
// Check we got the dummy records
Record[] r = (Record[])
mockListen.records.toArray(new Record[mockListen.records.size()]);
// Find the cell at 0,0
int cell00 = -1;
@ -240,20 +228,6 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
public void testMissingCellRecords() throws Exception {
File f = new File(dirname + "/MissingBits.xls");
HSSFRequest req = new HSSFRequest();
MockHSSFListener mockListen = new MockHSSFListener();
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
req.addListenerForAllRecords(listener);
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
HSSFEventFactory factory = new HSSFEventFactory();
factory.processWorkbookEvents(req, fs);
// Check we got the dummy records
Record[] r = (Record[])
mockListen.records.toArray(new Record[mockListen.records.size()]);
// Find the cell at 0,0
int cell00 = -1;
@ -352,25 +326,35 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
assertEquals(10, mc.getColumn());
}
private static class MockHSSFListener implements HSSFListener {
private MockHSSFListener() {}
private ArrayList records = new ArrayList();
private static final class MockHSSFListener implements HSSFListener {
public MockHSSFListener() {}
private final List _records = new ArrayList();
public void processRecord(Record record) {
records.add(record);
_records.add(record);
if(record instanceof MissingRowDummyRecord) {
MissingRowDummyRecord mr = (MissingRowDummyRecord)record;
System.out.println("Got dummy row " + mr.getRowNumber());
log("Got dummy row " + mr.getRowNumber());
}
if(record instanceof MissingCellDummyRecord) {
MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
System.out.println("Got dummy cell " + mc.getRow() + " " + mc.getColumn());
log("Got dummy cell " + mc.getRow() + " " + mc.getColumn());
}
if(record instanceof LastCellOfRowDummyRecord) {
LastCellOfRowDummyRecord lc = (LastCellOfRowDummyRecord)record;
System.out.println("Got end-of row, row was " + lc.getRow() + ", last column was " + lc.getLastColumnNumber());
log("Got end-of row, row was " + lc.getRow() + ", last column was " + lc.getLastColumnNumber());
}
}
private static void log(String msg) {
if(false) { // successful tests should be quiet
System.out.println(msg);
}
}
public Record[] getRecords() {
Record[] result = new Record[_records.size()];
_records.toArray(result);
return result;
}
}
}

View File

@ -0,0 +1,38 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.eval;
import junit.framework.Test;
import junit.framework.TestSuite;
/**
* Collects all tests the package <tt>org.apache.poi.hssf.record.formula.eval</tt>.
*
* @author Josh Micich
*/
public class AllFormulaEvalTests {
public static Test suite() {
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula.eval");
result.addTestSuite(TestCircularReferences.class);
result.addTestSuite(TestExternalFunction.class);
result.addTestSuite(TestFormulasFromSpreadsheet.class);
result.addTestSuite(TestUnaryPlusEval.class);
return result;
}
}

View File

@ -0,0 +1,61 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.eval;
import junit.framework.TestCase;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFName;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
/**
*
* @author Josh Micich
*/
public final class TestExternalFunction extends TestCase {
/**
* Checks that an external function can get invoked from the formula evaluator.
*/
public void testInvoke() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
wb.setSheetName(0, "Sheet1");
HSSFRow row = sheet.createRow(0);
HSSFCell cell = row.createCell((short)0);
HSSFName hssfName = wb.createName();
hssfName.setNameName("myFunc");
cell.setCellFormula("myFunc()");
String actualFormula=cell.getCellFormula();
assertEquals("myFunc()", actualFormula);
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet, wb);
fe.setCurrentRow(row);
CellValue evalResult = fe.evaluate(cell);
// Check the return value from ExternalFunction.evaluate()
// TODO - make this test assert something more interesting as soon as ExternalFunction works a bit better
assertEquals(HSSFCell.CELL_TYPE_ERROR, evalResult.getCellType());
assertEquals(ErrorEval.FUNCTION_NOT_IMPLEMENTED.getErrorCode(), evalResult.getErrorValue());
}
}

View File

@ -31,10 +31,13 @@ public final class AllIndividualFunctionEvaluationTests {
// TODO - have this suite incorporated into a higher level one
public static Test suite() {
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula.functions");
result.addTestSuite(TestAverage.class);
result.addTestSuite(TestCountFuncs.class);
result.addTestSuite(TestDate.class);
result.addTestSuite(TestFinanceLib.class);
result.addTestSuite(TestIndex.class);
result.addTestSuite(TestIsBlank.class);
result.addTestSuite(TestLen.class);
result.addTestSuite(TestMid.class);
result.addTestSuite(TestMathX.class);
result.addTestSuite(TestMatch.class);
@ -43,6 +46,8 @@ public final class AllIndividualFunctionEvaluationTests {
result.addTestSuite(TestSumproduct.class);
result.addTestSuite(TestStatsLib.class);
result.addTestSuite(TestTFunc.class);
result.addTestSuite(TestTrim.class);
result.addTestSuite(TestXYNumericFunction.class);
return result;
}

View File

@ -58,6 +58,6 @@ final class EvalFactory {
* Creates a single RefEval (with value zero)
*/
public static RefEval createRefEval(String refStr) {
return new Ref2DEval(new ReferencePtg(refStr), ZERO, true);
return new Ref2DEval(new ReferencePtg(refStr), ZERO);
}
}

View File

@ -111,9 +111,6 @@ public final class NumericFunctionInvoker {
if(errorCodesAreEqual(ee, ErrorEval.FUNCTION_NOT_IMPLEMENTED)) {
return "Function not implemented";
}
if(errorCodesAreEqual(ee, ErrorEval.UNKNOWN_ERROR)) {
return "Unknown error";
}
if(errorCodesAreEqual(ee, ErrorEval.VALUE_INVALID)) {
return "Error code: #VALUE! (invalid value)";
}

View File

@ -0,0 +1,103 @@
/* ====================================================================
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.functions;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* Tests for Excel function AVERAGE()
*
* @author Josh Micich
*/
public final class TestAverage extends TestCase {
private static Eval invokeAverage(Eval[] args) {
return new Average().evaluate(args, -1, (short)-1);
}
private void confirmAverage(Eval[] args, double expected) {
Eval result = invokeAverage(args);
assertEquals(NumberEval.class, result.getClass());
assertEquals(expected, ((NumberEval)result).getNumberValue(), 0);
}
private void confirmAverage(Eval[] args, ErrorEval expectedError) {
Eval result = invokeAverage(args);
assertEquals(ErrorEval.class, result.getClass());
assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode());
}
public void testBasic() {
ValueEval[] values = {
new NumberEval(1),
new NumberEval(2),
new NumberEval(3),
new NumberEval(4),
};
confirmAverage(values, 2.5);
values = new ValueEval[] {
new NumberEval(1),
new NumberEval(2),
BlankEval.INSTANCE,
new NumberEval(3),
BlankEval.INSTANCE,
new NumberEval(4),
BlankEval.INSTANCE,
};
confirmAverage(values, 2.5);
}
/**
* Valid cases where values are not pure numbers
*/
public void testUnusualArgs() {
ValueEval[] values = {
new NumberEval(1),
new NumberEval(2),
BoolEval.TRUE,
BoolEval.FALSE,
};
confirmAverage(values, 1.0);
}
// currently disabled because MultiOperandNumericFunction.getNumberArray(Eval[], int, short)
// does not handle error values properly yet
public void XtestErrors() {
ValueEval[] values = {
new NumberEval(1),
ErrorEval.NAME_INVALID,
new NumberEval(3),
ErrorEval.DIV_ZERO,
};
confirmAverage(values, ErrorEval.NAME_INVALID);
}
}

View File

@ -125,7 +125,7 @@ public final class TestCountFuncs extends TestCase {
};
Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values);
Ref2DEval criteriaArg = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(25), true);
Ref2DEval criteriaArg = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(25));
Eval[] args= { arg0, criteriaArg, };
double actual = NumericFunctionInvoker.invoke(new Countif(), args);

View File

@ -0,0 +1,62 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula.functions;
import junit.framework.TestCase;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.CellValue;
/**
* Tests for Excel function ISBLANK()
*
* @author Josh Micich
*/
public final class TestIsBlank extends TestCase {
public void test3DArea() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet1 = wb.createSheet();
wb.setSheetName(0, "Sheet1");
wb.createSheet();
wb.setSheetName(1, "Sheet2");
HSSFRow row = sheet1.createRow(0);
HSSFCell cell = row.createCell((short)0);
cell.setCellFormula("isblank(Sheet2!A1:A1)");
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(sheet1, wb);
fe.setCurrentRow(row);
CellValue result = fe.evaluate(cell);
assertEquals(HSSFCell.CELL_TYPE_BOOLEAN, result.getCellType());
assertEquals(true, result.getBooleanValue());
cell.setCellFormula("isblank(D7:D7)");
result = fe.evaluate(cell);
assertEquals(HSSFCell.CELL_TYPE_BOOLEAN, result.getCellType());
assertEquals(true, result.getBooleanValue());
}
}

View File

@ -0,0 +1,73 @@
/* ====================================================================
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.functions;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
/**
* Tests for Excel function LEN()
*
* @author Josh Micich
*/
public final class TestLen extends TestCase {
private static Eval invokeLen(Eval text) {
Eval[] args = new Eval[] { text, };
return new Len().evaluate(args, -1, (short)-1);
}
private void confirmLen(Eval text, int expected) {
Eval result = invokeLen(text);
assertEquals(NumberEval.class, result.getClass());
assertEquals(expected, ((NumberEval)result).getNumberValue(), 0);
}
private void confirmLen(Eval text, ErrorEval expectedError) {
Eval result = invokeLen(text);
assertEquals(ErrorEval.class, result.getClass());
assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode());
}
public void testBasic() {
confirmLen(new StringEval("galactic"), 8);
}
/**
* Valid cases where text arg is not exactly a string
*/
public void testUnusualArgs() {
// text (first) arg type is number, other args are strings with fractional digits
confirmLen(new NumberEval(123456), 6);
confirmLen(BoolEval.FALSE, 5);
confirmLen(BoolEval.TRUE, 4);
confirmLen(BlankEval.INSTANCE, 0);
}
public void testErrors() {
confirmLen(ErrorEval.NAME_INVALID, ErrorEval.NAME_INVALID);
}
}

View File

@ -77,13 +77,14 @@ public final class TestMid extends TestCase {
// startPos is 1x1 area ref, numChars is cell ref
AreaEval aeStart = new Area2DEval(new AreaPtg("A1:A1"), new ValueEval[] { new NumberEval(2), } );
RefEval reNumChars = new Ref2DEval(new ReferencePtg("B1"), new NumberEval(3),false);
RefEval reNumChars = new Ref2DEval(new ReferencePtg("B1"), new NumberEval(3));
confirmMid(new StringEval("galactic"), aeStart, reNumChars, "ala");
confirmMid(new StringEval("galactic"), new NumberEval(3.1), BlankEval.INSTANCE, "");
confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.FALSE, "");
confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.TRUE, "l");
confirmMid(BlankEval.INSTANCE, new NumberEval(3), BoolEval.TRUE, "");
}

View File

@ -50,7 +50,7 @@ public final class TestSumproduct extends TestCase {
public void testScalarSimple() {
RefEval refEval = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(3), true);
RefEval refEval = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(3));
Eval[] args = {
refEval,
new NumberEval(2),

View File

@ -50,7 +50,7 @@ public final class TestTFunc extends TestCase {
* where cell A1 has the specified innerValue
*/
private Eval invokeTWithReference(ValueEval innerValue) {
Eval arg = new Ref2DEval(new ReferencePtg((short)1, (short)1, false, false), innerValue, true);
Eval arg = new Ref2DEval(new ReferencePtg((short)1, (short)1, false, false), innerValue);
return invokeT(arg);
}

View File

@ -0,0 +1,78 @@
/* ====================================================================
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.functions;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.StringEval;
/**
* Tests for Excel function TRIM()
*
* @author Josh Micich
*/
public final class TestTrim extends TestCase {
private static Eval invokeTrim(Eval text) {
Eval[] args = new Eval[] { text, };
return new Trim().evaluate(args, -1, (short)-1);
}
private void confirmTrim(Eval text, String expected) {
Eval result = invokeTrim(text);
assertEquals(StringEval.class, result.getClass());
assertEquals(expected, ((StringEval)result).getStringValue());
}
private void confirmTrim(Eval text, ErrorEval expectedError) {
Eval result = invokeTrim(text);
assertEquals(ErrorEval.class, result.getClass());
assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode());
}
public void testBasic() {
confirmTrim(new StringEval(" hi "), "hi");
confirmTrim(new StringEval("hi "), "hi");
confirmTrim(new StringEval(" hi"), "hi");
confirmTrim(new StringEval(" hi there "), "hi there");
confirmTrim(new StringEval(""), "");
confirmTrim(new StringEval(" "), "");
}
/**
* Valid cases where text arg is not exactly a string
*/
public void testUnusualArgs() {
// text (first) arg type is number, other args are strings with fractional digits
confirmTrim(new NumberEval(123456), "123456");
confirmTrim(BoolEval.FALSE, "FALSE");
confirmTrim(BoolEval.TRUE, "TRUE");
confirmTrim(BlankEval.INSTANCE, "");
}
public void testErrors() {
confirmTrim(ErrorEval.NAME_INVALID, ErrorEval.NAME_INVALID);
}
}

View File

@ -0,0 +1,139 @@
/* ====================================================================
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.functions;
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.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
/**
* Tests for Excel functions SUMX2MY2(), SUMX2PY2(), SUMXMY2()
*
* @author Josh Micich
*/
public final class TestXYNumericFunction extends TestCase {
private static final Function SUM_SQUARES = new Sumx2py2();
private static final Function DIFF_SQUARES = new Sumx2my2();
private static final Function SUM_SQUARES_OF_DIFFS = new Sumxmy2();
private static Eval invoke(Function function, Eval xArray, Eval yArray) {
Eval[] args = new Eval[] { xArray, yArray, };
return function.evaluate(args, -1, (short)-1);
}
private void confirm(Function function, Eval xArray, Eval yArray, double expected) {
Eval result = invoke(function, xArray, yArray);
assertEquals(NumberEval.class, result.getClass());
assertEquals(expected, ((NumberEval)result).getNumberValue(), 0);
}
private void confirmError(Function function, Eval xArray, Eval yArray, ErrorEval expectedError) {
Eval result = invoke(function, xArray, yArray);
assertEquals(ErrorEval.class, result.getClass());
assertEquals(expectedError.getErrorCode(), ((ErrorEval)result).getErrorCode());
}
private void confirmError(Eval xArray, Eval yArray, ErrorEval expectedError) {
confirmError(SUM_SQUARES, xArray, yArray, expectedError);
confirmError(DIFF_SQUARES, xArray, yArray, expectedError);
confirmError(SUM_SQUARES_OF_DIFFS, xArray, yArray, expectedError);
}
public void testBasic() {
ValueEval[] xValues = {
new NumberEval(1),
new NumberEval(2),
};
ValueEval areaEvalX = createAreaEval(xValues);
confirm(SUM_SQUARES, areaEvalX, areaEvalX, 10.0);
confirm(DIFF_SQUARES, areaEvalX, areaEvalX, 0.0);
confirm(SUM_SQUARES_OF_DIFFS, areaEvalX, areaEvalX, 0.0);
ValueEval[] yValues = {
new NumberEval(3),
new NumberEval(4),
};
ValueEval areaEvalY = createAreaEval(yValues);
confirm(SUM_SQUARES, areaEvalX, areaEvalY, 30.0);
confirm(DIFF_SQUARES, areaEvalX, areaEvalY, -20.0);
confirm(SUM_SQUARES_OF_DIFFS, areaEvalX, areaEvalY, 8.0);
}
/**
* number of items in array is not limited to 30
*/
public void testLargeArrays() {
ValueEval[] xValues = createMockNumberArray(100, 3);
ValueEval[] yValues = createMockNumberArray(100, 2);
confirm(SUM_SQUARES, createAreaEval(xValues), createAreaEval(yValues), 1300.0);
confirm(DIFF_SQUARES, createAreaEval(xValues), createAreaEval(yValues), 500.0);
confirm(SUM_SQUARES_OF_DIFFS, createAreaEval(xValues), createAreaEval(yValues), 100.0);
}
private ValueEval[] createMockNumberArray(int size, double value) {
ValueEval[] result = new ValueEval[size];
for (int i = 0; i < result.length; i++) {
result[i] = new NumberEval(value);
}
return result;
}
private static ValueEval createAreaEval(ValueEval[] values) {
String refStr = "A1:A" + values.length;
return new Area2DEval(new AreaPtg(refStr), values);
}
public void testErrors() {
ValueEval[] xValues = {
ErrorEval.REF_INVALID,
new NumberEval(2),
};
ValueEval areaEvalX = createAreaEval(xValues);
ValueEval[] yValues = {
new NumberEval(2),
ErrorEval.NULL_INTERSECTION,
};
ValueEval areaEvalY = createAreaEval(yValues);
ValueEval[] zValues = { // wrong size
new NumberEval(2),
};
ValueEval areaEvalZ = createAreaEval(zValues);
// if either arg is an error, that error propagates
confirmError(ErrorEval.REF_INVALID, ErrorEval.NAME_INVALID, ErrorEval.REF_INVALID);
confirmError(areaEvalX, ErrorEval.NAME_INVALID, ErrorEval.NAME_INVALID);
confirmError(ErrorEval.NAME_INVALID, areaEvalX, ErrorEval.NAME_INVALID);
// array sizes must match
confirmError(areaEvalX, areaEvalZ, ErrorEval.NA);
confirmError(areaEvalZ, areaEvalY, ErrorEval.NA);
// any error in an array item propagates up
confirmError(areaEvalX, areaEvalX, ErrorEval.REF_INVALID);
// search for errors array by array, not pair by pair
confirmError(areaEvalX, areaEvalY, ErrorEval.REF_INVALID);
confirmError(areaEvalY, areaEvalX, ErrorEval.NULL_INTERSECTION);
}
}

View File

@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf;
import junit.framework.Test;
@ -25,80 +25,8 @@ import org.apache.poi.hssf.eventmodel.TestModelFactory;
import org.apache.poi.hssf.model.TestDrawingManager;
import org.apache.poi.hssf.model.TestFormulaParser;
import org.apache.poi.hssf.model.TestSheet;
import org.apache.poi.hssf.record.TestAreaFormatRecord;
import org.apache.poi.hssf.record.TestAreaRecord;
import org.apache.poi.hssf.record.TestAxisLineFormatRecord;
import org.apache.poi.hssf.record.TestAxisOptionsRecord;
import org.apache.poi.hssf.record.TestAxisParentRecord;
import org.apache.poi.hssf.record.TestAxisRecord;
import org.apache.poi.hssf.record.TestAxisUsedRecord;
import org.apache.poi.hssf.record.TestBarRecord;
import org.apache.poi.hssf.record.TestBoundSheetRecord;
import org.apache.poi.hssf.record.TestCategorySeriesAxisRecord;
import org.apache.poi.hssf.record.TestChartRecord;
import org.apache.poi.hssf.record.TestDatRecord;
import org.apache.poi.hssf.record.TestDataFormatRecord;
import org.apache.poi.hssf.record.TestDefaultDataLabelTextPropertiesRecord;
import org.apache.poi.hssf.record.TestFontBasisRecord;
import org.apache.poi.hssf.record.TestFontIndexRecord;
import org.apache.poi.hssf.record.TestFormulaRecord;
import org.apache.poi.hssf.record.TestFrameRecord;
import org.apache.poi.hssf.record.TestLegendRecord;
import org.apache.poi.hssf.record.TestLineFormatRecord;
import org.apache.poi.hssf.record.TestLinkedDataRecord;
import org.apache.poi.hssf.record.TestNameRecord;
import org.apache.poi.hssf.record.TestNumberFormatIndexRecord;
import org.apache.poi.hssf.record.TestObjectLinkRecord;
import org.apache.poi.hssf.record.TestPaletteRecord;
import org.apache.poi.hssf.record.TestPlotAreaRecord;
import org.apache.poi.hssf.record.TestPlotGrowthRecord;
import org.apache.poi.hssf.record.TestRecordFactory;
import org.apache.poi.hssf.record.TestSCLRecord;
import org.apache.poi.hssf.record.TestSSTDeserializer;
import org.apache.poi.hssf.record.TestSSTRecord;
import org.apache.poi.hssf.record.TestSSTRecordSizeCalculator;
import org.apache.poi.hssf.record.TestSeriesChartGroupIndexRecord;
import org.apache.poi.hssf.record.TestSeriesIndexRecord;
import org.apache.poi.hssf.record.TestSeriesLabelsRecord;
import org.apache.poi.hssf.record.TestSeriesListRecord;
import org.apache.poi.hssf.record.TestSeriesRecord;
import org.apache.poi.hssf.record.TestSeriesTextRecord;
import org.apache.poi.hssf.record.TestSeriesToChartGroupRecord;
import org.apache.poi.hssf.record.TestSheetPropertiesRecord;
import org.apache.poi.hssf.record.TestStringRecord;
import org.apache.poi.hssf.record.TestSupBookRecord;
import org.apache.poi.hssf.record.TestTextRecord;
import org.apache.poi.hssf.record.TestTickRecord;
import org.apache.poi.hssf.record.TestUnicodeString;
import org.apache.poi.hssf.record.TestUnitsRecord;
import org.apache.poi.hssf.record.TestValueRangeRecord;
import org.apache.poi.hssf.record.aggregates.TestRowRecordsAggregate;
import org.apache.poi.hssf.record.aggregates.TestValueRecordsAggregate;
import org.apache.poi.hssf.record.formula.AllFormulaTests;
import org.apache.poi.hssf.usermodel.TestBugs;
import org.apache.poi.hssf.usermodel.TestCellStyle;
import org.apache.poi.hssf.usermodel.TestCloneSheet;
import org.apache.poi.hssf.usermodel.TestEscherGraphics;
import org.apache.poi.hssf.usermodel.TestEscherGraphics2d;
import org.apache.poi.hssf.usermodel.TestFontDetails;
import org.apache.poi.hssf.usermodel.TestFormulas;
import org.apache.poi.hssf.usermodel.TestHSSFCell;
import org.apache.poi.hssf.usermodel.TestHSSFClientAnchor;
import org.apache.poi.hssf.usermodel.TestHSSFComment;
import org.apache.poi.hssf.usermodel.TestHSSFDateUtil;
import org.apache.poi.hssf.usermodel.TestHSSFHeaderFooter;
import org.apache.poi.hssf.usermodel.TestHSSFPalette;
import org.apache.poi.hssf.usermodel.TestHSSFRichTextString;
import org.apache.poi.hssf.usermodel.TestHSSFRow;
import org.apache.poi.hssf.usermodel.TestHSSFSheet;
import org.apache.poi.hssf.usermodel.TestHSSFSheetOrder;
import org.apache.poi.hssf.usermodel.TestHSSFSheetSetOrder;
import org.apache.poi.hssf.usermodel.TestHSSFWorkbook;
import org.apache.poi.hssf.usermodel.TestNamedRange;
import org.apache.poi.hssf.usermodel.TestReadWriteChart;
import org.apache.poi.hssf.usermodel.TestSanityChecker;
import org.apache.poi.hssf.usermodel.TestSheetShiftRows;
import org.apache.poi.hssf.usermodel.TestWorkbook;
import org.apache.poi.hssf.record.AllRecordTests;
import org.apache.poi.hssf.usermodel.AllUserModelTests;
import org.apache.poi.hssf.util.TestAreaReference;
import org.apache.poi.hssf.util.TestCellReference;
import org.apache.poi.hssf.util.TestRKUtil;
@ -112,113 +40,30 @@ import org.apache.poi.hssf.util.TestSheetReferences;
*
* @author Andrew C. Oliver acoliver@apache.org
*/
public final class HSSFTests
{
public final class HSSFTests {
public static void main(String[] args)
{
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
public static Test suite()
{
TestSuite suite =
new TestSuite("Tests for org.apache.poi.hssf");
//$JUnit-BEGIN$
suite.addTest(new TestSuite(TestBugs.class));
suite.addTest(new TestSuite(TestCloneSheet.class));
suite.addTest(new TestSuite(TestEscherGraphics.class));
suite.addTest(new TestSuite(TestEscherGraphics2d.class));
suite.addTest(new TestSuite(TestFontDetails.class));
suite.addTest(new TestSuite(TestHSSFClientAnchor.class));
suite.addTest(new TestSuite(TestHSSFHeaderFooter.class));
suite.addTest(new TestSuite(TestHSSFRichTextString.class));
suite.addTest(new TestSuite(TestHSSFSheetOrder.class));
suite.addTest(new TestSuite(TestHSSFSheetSetOrder.class));
suite.addTest(new TestSuite(TestHSSFWorkbook.class));
suite.addTest(new TestSuite(TestSanityChecker.class));
suite.addTest(new TestSuite(TestSheetShiftRows.class));
suite.addTest(new TestSuite(TestCellStyle.class));
suite.addTest(new TestSuite(TestFormulas.class));
suite.addTest(new TestSuite(TestHSSFCell.class));
suite.addTest(new TestSuite(TestHSSFDateUtil.class));
suite.addTest(new TestSuite(TestHSSFPalette.class));
suite.addTest(new TestSuite(TestHSSFRow.class));
suite.addTest(new TestSuite(TestHSSFSheet.class));
suite.addTest(new TestSuite(TestNamedRange.class));
suite.addTest(new TestSuite(TestReadWriteChart.class));
suite.addTest(new TestSuite(TestWorkbook.class));
public static Test suite() {
TestSuite suite = new TestSuite("Tests for org.apache.poi.hssf");
// $JUnit-BEGIN$
suite.addTest(AllUserModelTests.suite());
suite.addTest(AllRecordTests.suite());
suite.addTest(new TestSuite(TestFormulaParser.class));
suite.addTest(new TestSuite(TestAreaFormatRecord.class));
suite.addTest(new TestSuite(TestAreaRecord.class));
suite.addTest(new TestSuite(TestAxisLineFormatRecord.class));
suite.addTest(new TestSuite(TestAxisOptionsRecord.class));
suite.addTest(new TestSuite(TestAxisParentRecord.class));
suite.addTest(new TestSuite(TestAxisRecord.class));
suite.addTest(new TestSuite(TestAxisUsedRecord.class));
suite.addTest(new TestSuite(TestBarRecord.class));
suite.addTest(new TestSuite(TestBoundSheetRecord.class));
suite.addTest(new TestSuite(TestCategorySeriesAxisRecord.class));
suite.addTest(new TestSuite(TestChartRecord.class));
suite.addTest(new TestSuite(TestDatRecord.class));
suite.addTest(new TestSuite(TestDataFormatRecord.class));
suite.addTest(
new TestSuite(TestDefaultDataLabelTextPropertiesRecord.class));
suite.addTest(new TestSuite(TestFontBasisRecord.class));
suite.addTest(new TestSuite(TestFontIndexRecord.class));
suite.addTest(new TestSuite(TestFormulaRecord.class));
suite.addTest(new TestSuite(TestFrameRecord.class));
suite.addTest(new TestSuite(TestLegendRecord.class));
suite.addTest(new TestSuite(TestLineFormatRecord.class));
suite.addTest(new TestSuite(TestLinkedDataRecord.class));
suite.addTest(new TestSuite(TestNumberFormatIndexRecord.class));
suite.addTest(new TestSuite(TestObjectLinkRecord.class));
suite.addTest(new TestSuite(TestPaletteRecord.class));
suite.addTest(new TestSuite(TestPlotAreaRecord.class));
suite.addTest(new TestSuite(TestPlotGrowthRecord.class));
suite.addTest(new TestSuite(TestRecordFactory.class));
suite.addTest(new TestSuite(TestSCLRecord.class));
suite.addTest(new TestSuite(TestSSTDeserializer.class));
suite.addTest(new TestSuite(TestSSTRecord.class));
suite.addTest(new TestSuite(TestSSTRecordSizeCalculator.class));
suite.addTest(new TestSuite(TestSeriesChartGroupIndexRecord.class));
suite.addTest(new TestSuite(TestSeriesIndexRecord.class));
suite.addTest(new TestSuite(TestSeriesLabelsRecord.class));
suite.addTest(new TestSuite(TestSeriesListRecord.class));
suite.addTest(new TestSuite(TestSeriesRecord.class));
suite.addTest(new TestSuite(TestSeriesTextRecord.class));
suite.addTest(new TestSuite(TestSeriesToChartGroupRecord.class));
suite.addTest(new TestSuite(TestSheetPropertiesRecord.class));
suite.addTest(new TestSuite(TestStringRecord.class));
suite.addTest(new TestSuite(TestSupBookRecord.class));
suite.addTest(new TestSuite(TestTextRecord.class));
suite.addTest(new TestSuite(TestTickRecord.class));
suite.addTest(new TestSuite(TestUnicodeString.class));
suite.addTest(new TestSuite(TestUnitsRecord.class));
suite.addTest(new TestSuite(TestValueRangeRecord.class));
suite.addTest(new TestSuite(TestRowRecordsAggregate.class));
suite.addTest(new TestSuite(TestAreaReference.class));
suite.addTest(new TestSuite(TestCellReference.class));
suite.addTest(new TestSuite(TestRangeAddress.class));
suite.addTest(new TestSuite(TestRangeAddress.class));
suite.addTest(new TestSuite(TestRKUtil.class));
suite.addTest(new TestSuite(TestSheetReferences.class));
suite.addTest(AllFormulaTests.suite());
suite.addTest(new TestSuite(TestValueRecordsAggregate.class));
suite.addTest(new TestSuite(TestNameRecord.class));
suite.addTest(new TestSuite(TestEventRecordFactory.class));
suite.addTest(new TestSuite(TestModelFactory.class));
suite.addTest(new TestSuite(TestDrawingManager.class));
suite.addTest(new TestSuite(TestSheet.class));
suite.addTest(new TestSuite(TestHSSFComment.class));
//$JUnit-END$
suite.addTest(new TestSuite(TestEventRecordFactory.class));
suite.addTest(new TestSuite(TestModelFactory.class));
suite.addTest(new TestSuite(TestDrawingManager.class));
suite.addTest(new TestSuite(TestSheet.class));
// $JUnit-END$
return suite;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -18,27 +18,34 @@
package org.apache.poi.hssf.model;
import java.util.List;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
import org.apache.poi.hssf.record.formula.AddPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.BoolPtg;
import org.apache.poi.hssf.record.formula.ConcatPtg;
import org.apache.poi.hssf.record.formula.DividePtg;
import org.apache.poi.hssf.record.formula.EqualPtg;
import org.apache.poi.hssf.record.formula.ErrPtg;
import org.apache.poi.hssf.record.formula.FuncPtg;
import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.IntPtg;
import org.apache.poi.hssf.record.formula.LessEqualPtg;
import org.apache.poi.hssf.record.formula.LessThanPtg;
import org.apache.poi.hssf.record.formula.MissingArgPtg;
import org.apache.poi.hssf.record.formula.MultiplyPtg;
import org.apache.poi.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.NotEqualPtg;
import org.apache.poi.hssf.record.formula.NumberPtg;
import org.apache.poi.hssf.record.formula.PercentPtg;
import org.apache.poi.hssf.record.formula.PowerPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.ReferencePtg;
import org.apache.poi.hssf.record.formula.StringPtg;
import org.apache.poi.hssf.record.formula.SubtractPtg;
import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
import org.apache.poi.hssf.usermodel.HSSFCell;
@ -64,15 +71,25 @@ public class TestFormulaParser extends TestCase {
public void tearDown() {
}
/**
* @return parsed token array already confirmed not <code>null</code>
*/
private static Ptg[] parseFormula(String s) {
FormulaParser fp = new FormulaParser(s, null);
fp.parse();
Ptg[] result = fp.getRPNPtg();
assertNotNull("Ptg array should not be null", result);
return result;
}
public void testSimpleFormula() {
FormulaParser fp = new FormulaParser("2+2;",null);
FormulaParser fp = new FormulaParser("2+2",null);
fp.parse();
Ptg[] ptgs = fp.getRPNPtg();
assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
}
public void testFormulaWithSpace1() {
FormulaParser fp = new FormulaParser(" 2 + 2 ;",null);
FormulaParser fp = new FormulaParser(" 2 + 2 ",null);
fp.parse();
Ptg[] ptgs = fp.getRPNPtg();
assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
@ -85,7 +102,7 @@ public class TestFormulaParser extends TestCase {
public void testFormulaWithSpace2() {
Ptg[] ptgs;
FormulaParser fp;
fp = new FormulaParser("2+ sum( 3 , 4) ;",null);
fp = new FormulaParser("2+ sum( 3 , 4) ",null);
fp.parse();
ptgs = fp.getRPNPtg();
assertTrue("five tokens expected, got "+ptgs.length,ptgs.length == 5);
@ -94,7 +111,7 @@ public class TestFormulaParser extends TestCase {
public void testFormulaWithSpaceNRef() {
Ptg[] ptgs;
FormulaParser fp;
fp = new FormulaParser("sum( A2:A3 );",null);
fp = new FormulaParser("sum( A2:A3 )",null);
fp.parse();
ptgs = fp.getRPNPtg();
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
@ -103,7 +120,7 @@ public class TestFormulaParser extends TestCase {
public void testFormulaWithString() {
Ptg[] ptgs;
FormulaParser fp;
fp = new FormulaParser("\"hello\" & \"world\" ;",null);
fp = new FormulaParser("\"hello\" & \"world\" ",null);
fp.parse();
ptgs = fp.getRPNPtg();
assertTrue("three token expected, got " + ptgs.length, ptgs.length == 3);
@ -276,20 +293,21 @@ public class TestFormulaParser extends TestCase {
}
public void testMacroFunction() {
Workbook w = new Workbook();
Workbook w = Workbook.createWorkbook();
FormulaParser fp = new FormulaParser("FOO()", w);
fp.parse();
Ptg[] ptg = fp.getRPNPtg();
AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[0];
assertEquals("externalflag", tfunc.getName());
NamePtg tname = (NamePtg) ptg[1];
// the name gets encoded as the first arg
NamePtg tname = (NamePtg) ptg[0];
assertEquals("FOO", tname.toFormulaString(w));
AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1];
assertEquals("externalflag", tfunc.getName());
}
public void testEmbeddedSlash() {
FormulaParser fp = new FormulaParser("HYPERLINK(\"http://www.jakarta.org\",\"Jakarta\");",null);
FormulaParser fp = new FormulaParser("HYPERLINK(\"http://www.jakarta.org\",\"Jakarta\")",null);
fp.parse();
Ptg[] ptg = fp.getRPNPtg();
assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
@ -589,4 +607,234 @@ public class TestFormulaParser extends TestCase {
};
assertEquals("NA()", FormulaParser.toFormulaString(book, ptgs));
}
public void testPercent() {
Ptg[] ptgs;
ptgs = parseFormula("5%");
assertEquals(2, ptgs.length);
assertEquals(ptgs[0].getClass(), IntPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class);
// spaces OK
ptgs = parseFormula(" 250 % ");
assertEquals(2, ptgs.length);
assertEquals(ptgs[0].getClass(), IntPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class);
// double percent OK
ptgs = parseFormula("12345.678%%");
assertEquals(3, ptgs.length);
assertEquals(ptgs[0].getClass(), NumberPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class);
assertEquals(ptgs[2].getClass(), PercentPtg.class);
// percent of a bracketed expression
ptgs = parseFormula("(A1+35)%*B1%");
assertEquals(8, ptgs.length);
assertEquals(ptgs[4].getClass(), PercentPtg.class);
assertEquals(ptgs[6].getClass(), PercentPtg.class);
// percent of a text quantity
ptgs = parseFormula("\"8.75\"%");
assertEquals(2, ptgs.length);
assertEquals(ptgs[0].getClass(), StringPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class);
// percent to the power of
ptgs = parseFormula("50%^3");
assertEquals(4, ptgs.length);
assertEquals(ptgs[0].getClass(), IntPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class);
assertEquals(ptgs[2].getClass(), IntPtg.class);
assertEquals(ptgs[3].getClass(), PowerPtg.class);
//
// things that parse OK but would *evaluate* to an error
ptgs = parseFormula("\"abc\"%");
assertEquals(2, ptgs.length);
assertEquals(ptgs[0].getClass(), StringPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class);
ptgs = parseFormula("#N/A%");
assertEquals(2, ptgs.length);
assertEquals(ptgs[0].getClass(), ErrPtg.class);
assertEquals(ptgs[1].getClass(), PercentPtg.class);
}
/**
* Tests combinations of various operators in the absence of brackets
*/
public void testPrecedenceAndAssociativity() {
Class[] expClss;
// TRUE=TRUE=2=2 evaluates to FALSE
expClss = new Class[] { BoolPtg.class, BoolPtg.class, EqualPtg.class,
IntPtg.class, EqualPtg.class, IntPtg.class, EqualPtg.class, };
confirmTokenClasses("TRUE=TRUE=2=2", expClss);
// 2^3^2 evaluates to 64 not 512
expClss = new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class,
IntPtg.class, PowerPtg.class, };
confirmTokenClasses("2^3^2", expClss);
// "abc" & 2 + 3 & "def" evaluates to "abc5def"
expClss = new Class[] { StringPtg.class, IntPtg.class, IntPtg.class,
AddPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class, };
confirmTokenClasses("\"abc\"&2+3&\"def\"", expClss);
// (1 / 2) - (3 * 4)
expClss = new Class[] { IntPtg.class, IntPtg.class, DividePtg.class,
IntPtg.class, IntPtg.class, MultiplyPtg.class, SubtractPtg.class, };
confirmTokenClasses("1/2-3*4", expClss);
// 2 * (2^2)
expClss = new Class[] { IntPtg.class, IntPtg.class, IntPtg.class, PowerPtg.class, MultiplyPtg.class, };
// NOT: (2 *2) ^ 2 -> int int multiply int power
confirmTokenClasses("2*2^2", expClss);
// 2^200% -> 2 not 1.6E58
expClss = new Class[] { IntPtg.class, IntPtg.class, PercentPtg.class, PowerPtg.class, };
confirmTokenClasses("2^200%", expClss);
}
private static void confirmTokenClasses(String formula, Class[] expectedClasses) {
Ptg[] ptgs = parseFormula(formula);
assertEquals(expectedClasses.length, ptgs.length);
for (int i = 0; i < expectedClasses.length; i++) {
if(expectedClasses[i] != ptgs[i].getClass()) {
fail("difference at token[" + i + "]: expected ("
+ expectedClasses[i].getName() + ") but got ("
+ ptgs[i].getClass().getName() + ")");
}
}
}
public void testPower() {
confirmTokenClasses("2^5", new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class, });
}
private static Ptg parseSingleToken(String formula, Class ptgClass) {
Ptg[] ptgs = parseFormula(formula);
assertEquals(1, ptgs.length);
Ptg result = ptgs[0];
assertEquals(ptgClass, result.getClass());
return result;
}
public void testParseNumber() {
IntPtg ip;
// bug 33160
ip = (IntPtg) parseSingleToken("40", IntPtg.class);
assertEquals(40, ip.getValue());
ip = (IntPtg) parseSingleToken("40000", IntPtg.class);
assertEquals(40000, ip.getValue());
// check the upper edge of the IntPtg range:
ip = (IntPtg) parseSingleToken("65535", IntPtg.class);
assertEquals(65535, ip.getValue());
NumberPtg np = (NumberPtg) parseSingleToken("65536", NumberPtg.class);
assertEquals(65536, np.getValue(), 0);
np = (NumberPtg) parseSingleToken("65534.6", NumberPtg.class);
assertEquals(65534.6, np.getValue(), 0);
}
public void testMissingArgs() {
Class[] expClss;
expClss = new Class[] { ReferencePtg.class, MissingArgPtg.class, ReferencePtg.class,
FuncVarPtg.class, };
confirmTokenClasses("if(A1, ,C1)", expClss);
expClss = new Class[] { MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class,
FuncVarPtg.class, };
confirmTokenClasses("counta( , A1:B2, )", expClss);
}
public void testParseErrorLiterals() {
confirmParseErrorLiteral(ErrPtg.NULL_INTERSECTION, "#NULL!");
confirmParseErrorLiteral(ErrPtg.DIV_ZERO, "#DIV/0!");
confirmParseErrorLiteral(ErrPtg.VALUE_INVALID, "#VALUE!");
confirmParseErrorLiteral(ErrPtg.REF_INVALID, "#REF!");
confirmParseErrorLiteral(ErrPtg.NAME_INVALID, "#NAME?");
confirmParseErrorLiteral(ErrPtg.NUM_ERROR, "#NUM!");
confirmParseErrorLiteral(ErrPtg.N_A, "#N/A");
}
private static void confirmParseErrorLiteral(ErrPtg expectedToken, String formula) {
assertEquals(expectedToken, parseSingleToken(formula, ErrPtg.class));
}
/**
* To aid readability the parameters have been encoded with single quotes instead of double
* quotes. This method converts single quotes to double quotes before performing the parse
* and result check.
*/
private static void confirmStringParse(String singleQuotedValue) {
// formula: internal quotes become double double, surround with double quotes
String formula = '"' + singleQuotedValue.replaceAll("'", "\"\"") + '"';
String expectedValue = singleQuotedValue.replace('\'', '"');
StringPtg sp = (StringPtg) parseSingleToken(formula, StringPtg.class);
assertEquals(expectedValue, sp.getValue());
}
public void testPaseStringLiterals() {
confirmStringParse("goto considered harmful");
confirmStringParse("goto 'considered' harmful");
confirmStringParse("");
confirmStringParse("'");
confirmStringParse("''");
confirmStringParse("' '");
confirmStringParse(" ' ");
}
public void testParseSumIfSum() {
String formulaString;
Ptg[] ptgs;
ptgs = parseFormula("sum(5, 2, if(3>2, sum(A1:A2), 6))");
formulaString = FormulaParser.toFormulaString(null, ptgs);
assertEquals("SUM(5,2,IF(3>2,SUM(A1:A2),6))", formulaString);
ptgs = parseFormula("if(1<2,sum(5, 2, if(3>2, sum(A1:A2), 6)),4)");
formulaString = FormulaParser.toFormulaString(null, ptgs);
assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString);
}
public void testParserErrors() {
parseExpectedException("1 2");
parseExpectedException(" 12 . 345 ");
parseExpectedException("1 .23 ");
parseExpectedException("sum(#NAME)");
parseExpectedException("1 + #N / A * 2");
parseExpectedException("#value?");
parseExpectedException("#DIV/ 0+2");
if (false) { // TODO - add functionality to detect func arg count mismatch
parseExpectedException("IF(TRUE)");
parseExpectedException("countif(A1:B5, C1, D1)");
}
}
private static void parseExpectedException(String formula) {
try {
parseFormula(formula);
throw new AssertionFailedError("expected parse exception");
} catch (RuntimeException e) {
// TODO - catch more specific exception
// expected during successful test
return;
}
}
}

View File

@ -0,0 +1,104 @@
/* ====================================================================
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;
import org.apache.poi.hssf.record.formula.AllFormulaTests;
import junit.framework.Test;
import junit.framework.TestSuite;
/**
* Collects all tests for package <tt>org.apache.poi.hssf.record</tt>.
*
* @author Josh Micich
*/
public class AllRecordTests {
public static Test suite() {
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record");
result.addTest(AllFormulaTests.suite());
result.addTestSuite(TestAreaFormatRecord.class);
result.addTestSuite(TestAreaRecord.class);
result.addTestSuite(TestAxisLineFormatRecord.class);
result.addTestSuite(TestAxisOptionsRecord.class);
result.addTestSuite(TestAxisParentRecord.class);
result.addTestSuite(TestAxisRecord.class);
result.addTestSuite(TestAxisUsedRecord.class);
result.addTestSuite(TestBOFRecord.class);
result.addTestSuite(TestBarRecord.class);
result.addTestSuite(TestBoundSheetRecord.class);
result.addTestSuite(TestCategorySeriesAxisRecord.class);
result.addTestSuite(TestChartRecord.class);
result.addTestSuite(TestChartTitleFormatRecord.class);
result.addTestSuite(TestCommonObjectDataSubRecord.class);
result.addTestSuite(TestDatRecord.class);
result.addTestSuite(TestDataFormatRecord.class);
result.addTestSuite(TestDefaultDataLabelTextPropertiesRecord.class);
result.addTestSuite(TestDrawingGroupRecord.class);
result.addTestSuite(TestEmbeddedObjectRefSubRecord.class);
result.addTestSuite(TestEndSubRecord.class);
result.addTestSuite(TestEscherAggregate.class);
result.addTestSuite(TestFontBasisRecord.class);
result.addTestSuite(TestFontIndexRecord.class);
result.addTestSuite(TestFormulaRecord.class);
result.addTestSuite(TestFrameRecord.class);
result.addTestSuite(TestHyperlinkRecord.class);
result.addTestSuite(TestLegendRecord.class);
result.addTestSuite(TestLineFormatRecord.class);
result.addTestSuite(TestLinkedDataRecord.class);
result.addTestSuite(TestMergeCellsRecord.class);
result.addTestSuite(TestNameRecord.class);
result.addTestSuite(TestNoteRecord.class);
result.addTestSuite(TestNoteStructureSubRecord.class);
result.addTestSuite(TestNumberFormatIndexRecord.class);
result.addTestSuite(TestObjRecord.class);
result.addTestSuite(TestObjectLinkRecord.class);
result.addTestSuite(TestPaletteRecord.class);
result.addTestSuite(TestPaneRecord.class);
result.addTestSuite(TestPlotAreaRecord.class);
result.addTestSuite(TestPlotGrowthRecord.class);
result.addTestSuite(TestRecordFactory.class);
result.addTestSuite(TestSCLRecord.class);
result.addTestSuite(TestSSTDeserializer.class);
result.addTestSuite(TestSSTRecord.class);
result.addTestSuite(TestSSTRecordSizeCalculator.class);
result.addTestSuite(TestSeriesChartGroupIndexRecord.class);
result.addTestSuite(TestSeriesIndexRecord.class);
result.addTestSuite(TestSeriesLabelsRecord.class);
result.addTestSuite(TestSeriesListRecord.class);
result.addTestSuite(TestSeriesRecord.class);
result.addTestSuite(TestSeriesTextRecord.class);
result.addTestSuite(TestSeriesToChartGroupRecord.class);
result.addTestSuite(TestSheetPropertiesRecord.class);
result.addTestSuite(TestStringRecord.class);
result.addTestSuite(TestSubRecord.class);
result.addTestSuite(TestSupBookRecord.class);
result.addTestSuite(TestTextObjectBaseRecord.class);
result.addTestSuite(TestTextObjectRecord.class);
result.addTestSuite(TestTextRecord.class);
result.addTestSuite(TestTickRecord.class);
result.addTestSuite(TestUnicodeNameRecord.class);
result.addTestSuite(TestUnicodeString.class);
result.addTestSuite(TestUnitsRecord.class);
result.addTestSuite(TestValueRangeRecord.class);
return result;
}
}

View File

@ -29,15 +29,25 @@ import junit.framework.TestCase;
*
* @author Andrew C. Oliver (acoliver at apache dot org)
*/
public class TestSupBookRecord
extends TestCase
{
public final class TestSupBookRecord extends TestCase {
/**
* This contains a fake data section of a SubBookRecord
*/
byte[] data = new byte[] {
(byte)0x04,(byte)0x00,(byte)0x01,(byte)0x04
byte[] dataIR = new byte[] {
(byte)0x04,(byte)0x00,(byte)0x01,(byte)0x04,
};
byte[] dataAIF = new byte[] {
(byte)0x01,(byte)0x00,(byte)0x01,(byte)0x3A,
};
byte[] dataER = new byte[] {
(byte)0x02,(byte)0x00,
(byte)0x07,(byte)0x00, (byte)0x00,
(byte)'t', (byte)'e', (byte)'s', (byte)'t', (byte)'U', (byte)'R', (byte)'L',
(byte)0x06,(byte)0x00, (byte)0x00,
(byte)'S', (byte)'h', (byte)'e', (byte)'e', (byte)'t', (byte)'1',
(byte)0x06,(byte)0x00, (byte)0x00,
(byte)'S', (byte)'h', (byte)'e', (byte)'e', (byte)'t', (byte)'2',
};
public TestSupBookRecord(String name)
{
@ -47,36 +57,67 @@ public class TestSupBookRecord
/**
* tests that we can load the record
*/
public void testLoad()
throws Exception
{
public void testLoadIR() {
SupBookRecord record = new SupBookRecord(new TestcaseRecordInputStream((short)0x01AE, (short)data.length, data));
assertEquals( 0x401, record.getFlag()); //expected flag
SupBookRecord record = new SupBookRecord(new TestcaseRecordInputStream((short)0x01AE, dataIR));
assertTrue( record.isInternalReferences() ); //expected flag
assertEquals( 0x4, record.getNumberOfSheets() ); //expected # of sheets
assertEquals( 8, record.getRecordSize() ); //sid+size+data
record.validateSid((short)0x01AE);
}
/**
* tests that we can load the record
*/
public void testLoadER() {
SupBookRecord record = new SupBookRecord(new TestcaseRecordInputStream((short)0x01AE, dataER));
assertTrue( record.isExternalReferences() ); //expected flag
assertEquals( 0x2, record.getNumberOfSheets() ); //expected # of sheets
assertEquals( 34, record.getRecordSize() ); //sid+size+data
assertEquals("testURL", record.getURL().getString());
UnicodeString[] sheetNames = record.getSheetNames();
assertEquals(2, sheetNames.length);
assertEquals("Sheet1", sheetNames[0].getString());
assertEquals("Sheet2", sheetNames[1].getString());
record.validateSid((short)0x01AE);
}
/**
* tests that we can load the record
*/
public void testLoadAIF() {
SupBookRecord record = new SupBookRecord(new TestcaseRecordInputStream((short)0x01AE, dataAIF));
assertTrue( record.isAddInFunctions() ); //expected flag
assertEquals( 0x1, record.getNumberOfSheets() ); //expected # of sheets
assertEquals( 8, record.getRecordSize() ); //sid+size+data
record.validateSid((short)0x01AE);
}
/**
* Tests that we can store the record
*
*/
public void testStore()
{
SupBookRecord record = new SupBookRecord();
record.setFlag( (short) 0x401 );
record.setNumberOfSheets( (short)0x4 );
public void testStoreIR() {
SupBookRecord record = SupBookRecord.createInternalReferences((short)4);
TestcaseRecordInputStream.confirmRecordEncoding(0x01AE, dataIR, record.serialize());
}
public void testStoreER() {
UnicodeString url = new UnicodeString("testURL");
UnicodeString[] sheetNames = {
new UnicodeString("Sheet1"),
new UnicodeString("Sheet2"),
};
SupBookRecord record = SupBookRecord.createExternalReferences(url, sheetNames);
byte [] recordBytes = record.serialize();
assertEquals(recordBytes.length - 4, data.length);
for (int i = 0; i < data.length; i++)
assertEquals("At offset " + i, data[i], recordBytes[i+4]);
TestcaseRecordInputStream.confirmRecordEncoding(0x01AE, dataER, record.serialize());
}
public static void main(String [] args) {
@ -84,6 +125,4 @@ public class TestSupBookRecord
.println("Testing org.apache.poi.hssf.record.SupBookRecord");
junit.textui.TestRunner.run(TestSupBookRecord.class);
}
}

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -16,11 +15,12 @@
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record;
import java.io.ByteArrayInputStream;
import junit.framework.Assert;
import org.apache.poi.util.LittleEndian;
/**
@ -33,6 +33,14 @@ import org.apache.poi.util.LittleEndian;
public class TestcaseRecordInputStream
extends RecordInputStream
{
/**
* Convenience constructor
*/
public TestcaseRecordInputStream(int sid, byte[] data)
{
super(new ByteArrayInputStream(mergeDataAndSid((short)sid, (short)data.length, data)));
nextRecord();
}
public TestcaseRecordInputStream(short sid, short length, byte[] data)
{
super(new ByteArrayInputStream(mergeDataAndSid(sid, length, data)));
@ -46,4 +54,18 @@ public class TestcaseRecordInputStream
System.arraycopy(data, 0, result, 4, data.length);
return result;
}
/**
* Confirms data sections are equal
* @param expectedData - just raw data (without sid or size short ints)
* @param actualRecordBytes this includes 4 prefix bytes (sid & size)
*/
public static void confirmRecordEncoding(int expectedSid, byte[] expectedData, byte[] actualRecordBytes) {
int expectedDataSize = expectedData.length;
Assert.assertEquals(actualRecordBytes.length - 4, expectedDataSize);
Assert.assertEquals(expectedSid, LittleEndian.getShort(actualRecordBytes, 0));
Assert.assertEquals(expectedDataSize, LittleEndian.getShort(actualRecordBytes, 2));
for (int i = 0; i < expectedDataSize; i++)
Assert.assertEquals("At offset " + i, expectedData[i], actualRecordBytes[i+4]);
}
}

View File

@ -1,4 +1,3 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@ -28,6 +27,7 @@ import junit.framework.TestCase;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
* Convenient abstract class to reduce the amount of boilerplate code needed
@ -35,8 +35,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
*
* @author Daniel Noll (daniel at nuix dot com dot au)
*/
public abstract class AbstractPtgTestCase extends TestCase
{
public abstract class AbstractPtgTestCase extends TestCase {
/** Directory containing the test data. */
private static String dataDir = System.getProperty("HSSF.testdata.path");
@ -51,16 +50,16 @@ public abstract class AbstractPtgTestCase extends TestCase
throws IOException {
File file = new File(dataDir, filename);
InputStream stream = new BufferedInputStream(new FileInputStream(file));
try
{
return new HSSFWorkbook(stream);
}
finally
{
// TODO - temp workaround to keep stdout quiet due to warning msg in POIFS
// When that warning msg is disabled, remove this wrapper and the close() call,
InputStream wrappedStream = POIFSFileSystem.createNonClosingInputStream(stream);
try {
return new HSSFWorkbook(wrappedStream);
} finally {
stream.close();
}
}
/**
* Creates a new Workbook and adds one sheet with the specified name
*/
@ -73,5 +72,4 @@ public abstract class AbstractPtgTestCase extends TestCase
book.setSheetName(0, sheetName);
return book;
}
}

View File

@ -14,7 +14,6 @@
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula;
@ -33,7 +32,8 @@ public class AllFormulaTests {
result.addTestSuite(TestArea3DPtg.class);
result.addTestSuite(TestAreaErrPtg.class);
result.addTestSuite(TestAreaPtg.class);
result.addTestSuite(TestErrPtg.class);
result.addTestSuite(TestErrPtg.class);
result.addTestSuite(TestExternalFunctionFormulas.class);
result.addTestSuite(TestFuncPtg.class);
result.addTestSuite(TestIntersectionPtg.class);
result.addTestSuite(TestPercentPtg.class);

View File

@ -0,0 +1,56 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula;
import java.io.FileInputStream;
import java.io.IOException;
import junit.framework.TestCase;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* Tests for functions from external workbooks (e.g. YEARFRAC).
*
*
* @author Josh Micich
*/
public final class TestExternalFunctionFormulas extends TestCase {
/**
* tests <tt>NameXPtg.toFormulaString(Workbook)</tt> and logic in Workbook below that
*/
public void testReadFormulaContainingExternalFunction() {
String filePath = System.getProperty("HSSF.testdata.path")+ "/"
+ "externalFunctionExample.xls";
HSSFWorkbook wb;
try {
FileInputStream fin = new FileInputStream(filePath);
wb = new HSSFWorkbook( fin );
} catch (IOException e) {
throw new RuntimeException(e);
}
String expectedFormula = "YEARFRAC(B1,C1)";
HSSFSheet sht = wb.getSheetAt(0);
String cellFormula = sht.getRow(0).getCell((short)0).getCellFormula();
assertEquals(expectedFormula, cellFormula);
}
}

View File

@ -0,0 +1,71 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.usermodel;
import junit.framework.Test;
import junit.framework.TestSuite;
/**
* Collects all tests for the <tt>org.apache.poi.hssf.usermodel</tt> package.
*
* @author Josh Micich
*/
public class AllUserModelTests {
public static Test suite() {
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.usermodel");
result.addTestSuite(TestBugs.class);
result.addTestSuite(TestCellStyle.class);
result.addTestSuite(TestCloneSheet.class);
result.addTestSuite(TestDataValidation.class);
result.addTestSuite(TestEscherGraphics.class);
result.addTestSuite(TestEscherGraphics2d.class);
result.addTestSuite(TestFontDetails.class);
result.addTestSuite(TestFormulas.class);
result.addTestSuite(TestHSSFCell.class);
result.addTestSuite(TestHSSFClientAnchor.class);
result.addTestSuite(TestHSSFComment.class);
result.addTestSuite(TestHSSFDateUtil.class);
result.addTestSuite(TestHSSFHeaderFooter.class);
result.addTestSuite(TestHSSFHyperlink.class);
result.addTestSuite(TestHSSFPalette.class);
result.addTestSuite(TestHSSFPicture.class);
result.addTestSuite(TestHSSFPictureData.class);
result.addTestSuite(TestHSSFRichTextString.class);
result.addTestSuite(TestHSSFRow.class);
result.addTestSuite(TestHSSFSheet.class);
result.addTestSuite(TestHSSFSheetOrder.class);
result.addTestSuite(TestHSSFSheetSetOrder.class);
result.addTestSuite(TestHSSFWorkbook.class);
result.addTestSuite(TestNamedRange.class);
result.addTestSuite(TestOLE2Embeding.class);
result.addTestSuite(TestReadWriteChart.class);
result.addTestSuite(TestSanityChecker.class);
result.addTestSuite(TestSheetHiding.class);
result.addTestSuite(TestSheetShiftRows.class);
if (false) { // deliberately avoiding this one
result.addTestSuite(TestUnfixedBugs.class);
}
result.addTestSuite(TestUnicodeWorkbook.class);
result.addTestSuite(TestUppercaseWorkbook.class);
result.addTestSuite(TestWorkbook.class);
return result;
}
}

View File

@ -16,25 +16,28 @@
*/
package org.apache.poi.hssf.usermodel;
import junit.framework.TestCase;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import junit.framework.TestCase;
/**
* Test <code>HSSFPicture</code>.
*
* @author Yegor Kozlov (yegor at apache.org)
*/
public class TestHSSFPicture extends TestCase{
public final class TestHSSFPicture extends TestCase{
public void testResize() throws Exception {
public void testResize() {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sh1 = wb.createSheet();
HSSFPatriarch p1 = sh1.createDrawingPatriarch();
int idx1 = loadPicture( "src/resources/logos/logoKarmokar4.png", wb);
byte[] pictureData = getTestDataFileContent("logoKarmokar4.png");
int idx1 = wb.addPicture( pictureData, HSSFWorkbook.PICTURE_TYPE_PNG );
HSSFPicture picture1 = p1.createPicture(new HSSFClientAnchor(), idx1);
HSSFClientAnchor anchor1 = picture1.getPreferredSize();
@ -52,28 +55,25 @@ public class TestHSSFPicture extends TestCase{
/**
* Copied from org.apache.poi.hssf.usermodel.examples.OfficeDrawing
*/
private static int loadPicture( String path, HSSFWorkbook wb ) throws IOException
{
int pictureIndex;
FileInputStream fis = null;
ByteArrayOutputStream bos = null;
try
{
fis = new FileInputStream( path);
bos = new ByteArrayOutputStream( );
int c;
while ( (c = fis.read()) != -1)
bos.write( c );
pictureIndex = wb.addPicture( bos.toByteArray(), HSSFWorkbook.PICTURE_TYPE_PNG );
}
finally
{
if (fis != null)
fis.close();
if (bos != null)
bos.close();
}
return pictureIndex;
}
private static byte[] getTestDataFileContent(String fileName) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
String readFilename = System.getProperty("HSSF.testdata.path");
try {
InputStream fis = new FileInputStream(readFilename+File.separator+fileName);
byte[] buf = new byte[512];
while(true) {
int bytesRead = fis.read(buf);
if(bytesRead < 1) {
break;
}
bos.write(buf, 0, bytesRead);
}
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
return bos.toByteArray();
}
}