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

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -15,72 +14,159 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
/** /**
* Title: Sup Book <P> * Title: Sup Book (EXTERNALBOOK) <P>
* Description: A Extrenal Workbook Deciption (Sup Book) * Description: A External Workbook Description (Suplemental Book)
* Its only a dummy record for making new ExternSheet Record <P> * 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 Libin Roman (Vista Portal LDT. Developer)
* @author Andrew C. Oliver (acoliver@apache.org) * @author Andrew C. Oliver (acoliver@apache.org)
* *
*/ */
public class SupBookRecord extends Record public final class SupBookRecord extends Record {
{
public final static short sid = 0x1AE; 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_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() public static SupBookRecord createInternalReferences(short numberOfSheets) {
{ return new SupBookRecord(false, numberOfSheets);
setFlag((short)0x401); }
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. * 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); super(in);
} }
protected void validateSid(short id) protected void validateSid(short id) {
{ if (id != sid) {
if (id != sid) throw new RecordFormatException("NOT An ExternSheet RECORD");
{
throw new RecordFormatException("NOT An Supbook RECORD");
} }
} }
/** public boolean isExternalReferences() {
* @param in the RecordInputstream to read the record from return field_3_sheet_names != null;
*/
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 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() public String toString() {
{ StringBuffer sb = new StringBuffer();
StringBuffer buffer = new StringBuffer(); sb.append(getClass().getName()).append(" [SUPBOOK ");
buffer.append("[SUPBOOK]\n");
buffer.append("numberosheets = ").append(getNumberOfSheets()).append('\n'); if(isExternalReferences()) {
buffer.append("flag = ").append(getFlag()).append('\n'); sb.append("External References");
buffer.append("[/SUPBOOK]\n"); sb.append(" nSheets=").append(field_1_number_of_sheets);
return buffer.toString(); 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 * @param data byte array containing instance data
* @return number of bytes written * @return number of bytes written
*/ */
public int serialize(int offset, byte [] data) public int serialize(int offset, byte [] data) {
{
LittleEndian.putShort(data, 0 + offset, sid); 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, 4 + offset, field_1_number_of_sheets);
LittleEndian.putShort(data, 6 + offset, field_2_flag);
if(isExternalReferences()) {
return getRecordSize();
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){ public void setNumberOfSheets(short number){
@ -110,21 +212,18 @@ public class SupBookRecord extends Record
return field_1_number_of_sheets; return field_1_number_of_sheets;
} }
public void setFlag(short flag){ public int getRecordSize() {
field_2_flag = flag; return getDataSize() + 4;
}
public short getFlag() {
return field_2_flag;
}
public int getRecordSize()
{
return 4 + 4;
} }
public short getSid() public short getSid()
{ {
return sid; 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) * @author Andrew C. Oliver (acoliver at apache dot org)
*/ */
public abstract class AbstractFunctionPtg extends OperationPtg { 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"; /**
* The name of the IF function (i.e. "IF"). Extracted as a constant for clarity.
public static final short INDEX_EXTERNAL = 255; */
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(); private static BinaryTree map = produceHash();
protected static Object[][] functionData = produceFunctionData(); protected static Object[][] functionData = produceFunctionData();
@ -66,6 +69,13 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
public String getName() { public String getName() {
return lookupName(field_2_fnc_index); 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) { public String toFormulaString(Workbook book) {
return getName(); return getName();
@ -73,39 +83,57 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
public String toFormulaString(String[] operands) { public String toFormulaString(String[] operands) {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
if (field_2_fnc_index != 1) { if(isExternalFunction()) {
buf.append(getName()); buf.append(operands[0]); // first operand is actually the function name
buf.append('('); appendArgs(buf, 1, operands);
} } else {
if (operands.length >0) { buf.append(getName());
for (int i=0;i<operands.length;i++) { appendArgs(buf, 0, operands);
buf.append(operands[i]); }
buf.append(',');
}
buf.deleteCharAt(buf.length()-1);
}
if (field_2_fnc_index != 1) {
buf.append(")");
}
return buf.toString(); 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 void writeBytes(byte[] array, int offset);
public abstract int getSize(); 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) { protected String lookupName(short index) {
return ((String)map.get(new Integer(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(); 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(); BinaryTree dmap = new BinaryTree();
dmap.put(new Integer(0),"COUNT"); 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(2),"ISNA");
dmap.put(new Integer(3),"ISERROR"); dmap.put(new Integer(3),"ISERROR");
dmap.put(new Integer(4),"SUM"); 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(252),"FREQUENCY");
dmap.put(new Integer(253),"ADDTOOLBAR"); dmap.put(new Integer(253),"ADDTOOLBAR");
dmap.put(new Integer(254),"DELETETOOLBAR"); 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(256),"RESETTOOLBAR");
dmap.put(new Integer(257),"EVALUATE"); dmap.put(new Integer(257),"EVALUATE");
dmap.put(new Integer(258),"GETTOOLBAR"); dmap.put(new Integer(258),"GETTOOLBAR");

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,12 +15,6 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
/*
* IntPtg.java
*
* Created on October 29, 2001, 7:37 PM
*/
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
@ -29,64 +22,45 @@ import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.RecordInputStream; 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 * Stores an unsigned short value (java int) in a formula
* @author Andrew C. Oliver (acoliver at apache dot org) * @author Andrew C. Oliver (acoliver at apache dot org)
* @author Jason Height (jheight at chariot dot net dot au) * @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 int SIZE = 3;
public final static byte sid = 0x1e; public final static byte sid = 0x1e;
private int field_1_value; private int field_1_value;
private IntPtg() { public IntPtg(RecordInputStream in) {
//Required for clone methods 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));
}
/** public IntPtg(int value) {
* Sets the wrapped value. if(!isInRange(value)) {
* Normally you should call with a positive int. throw new IllegalArgumentException("value is out of range: " + value);
*/ }
public void setValue(int value)
{
if(value < 0 || value > (Short.MAX_VALUE + 1)*2 )
throw new IllegalArgumentException("Unsigned short is out of range: " + value);
field_1_value = value; field_1_value = value;
} }
/** public int getValue() {
* Returns the value as a short, which may have
* been wrapped into negative numbers
*/
public int getValue()
{
return field_1_value; 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) public void writeBytes(byte [] array, int offset)
{ {
@ -94,20 +68,25 @@ public class IntPtg
LittleEndian.putUShort(array, offset + 1, getValue()); LittleEndian.putUShort(array, offset + 1, getValue());
} }
public int getSize() public int getSize() {
{
return SIZE; return SIZE;
} }
public String toFormulaString(Workbook book) public String toFormulaString(Workbook book) {
{ return String.valueOf(getValue());
return "" + getValue(); }
public byte getDefaultOperandClass() {
return Ptg.CLASS_VALUE;
} }
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
public Object clone() { public Object clone() {
IntPtg ptg = new IntPtg(); return new IntPtg(field_1_value);
ptg.field_1_value = field_1_value; }
return ptg; 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; public final static short sid = 0x23;
private final static int SIZE = 5; private final static int SIZE = 5;
/** one-based index to defined name record */
private short field_1_label_index; private short field_1_label_index;
private short field_2_zero; // reserved must be 0 private short field_2_zero; // reserved must be 0
boolean xtra=false; boolean xtra=false;
@ -42,24 +43,32 @@ public class NamePtg
//Required for clone methods //Required for clone methods
} }
/** Creates new NamePtg */ /**
* Creates new NamePtg and sets its name index to that of the corresponding defined name record
public NamePtg(String name, Workbook book) * in the workbook. The search for the name record is case insensitive. If it is not found,
{ * it gets created.
final short n = (short) (book.getNumNames() + 1); */
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; NameRecord rec;
for (short i = 1; i < n; i++) { for (int i = 0; i < countNames; i++) {
rec = book.getNameRecord(i - 1); rec = book.getNameRecord(i);
if (name.equalsIgnoreCase(rec.getNameText())) { if (name.equalsIgnoreCase(rec.getNameText())) {
field_1_label_index = i; return i;
return;
} }
} }
rec = new NameRecord(); rec = new NameRecord();
rec.setNameText(name); rec.setNameText(name);
rec.setNameTextLength((byte) name.length()); rec.setNameTextLength((byte) name.length());
book.addName(rec); book.addName(rec);
field_1_label_index = n; return countNames;
} }
/** Creates new NamePtg */ /** Creates new NamePtg */
@ -71,6 +80,13 @@ public class NamePtg
field_2_zero = in.readShort(); field_2_zero = in.readShort();
//if (data[offset+6]==0) xtra=true; //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) public void writeBytes(byte [] array, int offset)
{ {

View File

@ -25,13 +25,11 @@ import org.apache.poi.hssf.record.RecordInputStream;
* *
* @author aviks * @author aviks
*/ */
public final class NameXPtg extends Ptg {
public class NameXPtg extends Ptg
{
public final static short sid = 0x39; public final static short sid = 0x39;
private final static int SIZE = 7; private final static int SIZE = 7;
private short field_1_ixals; // index to externsheet record private short field_1_ixals; // index to REF entry in externsheet record
private short field_2_ilbl; //index to name or externname table(1 based) private short field_2_ilbl; //index to defined name or externname table(1 based)
private short field_3_reserved; // reserved must be 0 private short field_3_reserved; // reserved must be 0
@ -41,13 +39,6 @@ public class NameXPtg extends Ptg
/** Creates new NamePtg */ /** Creates new NamePtg */
public NameXPtg(String name)
{
//TODO
}
/** Creates new NamePtg */
public NameXPtg(RecordInputStream in) public NameXPtg(RecordInputStream in)
{ {
field_1_ixals = in.readShort(); field_1_ixals = in.readShort();
@ -72,7 +63,8 @@ public class NameXPtg extends Ptg
public String toFormulaString(Workbook book) 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;} 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 //only set to default if there is no extended format index already set
if (rec.getXFIndex() == (short)0) rec.setXFIndex(( short ) 0x0f); if (rec.getXFIndex() == (short)0) rec.setXFIndex(( short ) 0x0f);
FormulaParser fp = new FormulaParser(formula+";",book); FormulaParser fp = new FormulaParser(formula, book);
fp.parse(); fp.parse();
Ptg[] ptg = fp.getRPNPtg(); Ptg[] ptg = fp.getRPNPtg();
int size = 0; int size = 0;

View File

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

View File

@ -245,6 +245,16 @@ public class LittleEndian
putNumber(data, offset, value, SHORT_SIZE); 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 * 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 = private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short) new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED ( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_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) { public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
Eval retval = null; if(args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
double d = 0; double d = 0;
switch (operands.length) { for (int i = 0; i < 2; i++) {
default: // will rarely happen. currently the parser itself fails. ValueEval ve = singleOperandEvaluate(args[i], srcRow, srcCol);
retval = ErrorEval.UNKNOWN_ERROR; if(ve instanceof ErrorEval) {
break; return ve;
case 2: }
for (int i = 0, iSize = 2; retval==null && i < iSize; i++) { if (ve instanceof NumericValueEval) {
ValueEval ve = singleOperandEvaluate(operands[i], srcRow, srcCol); d += ((NumericValueEval) ve).getNumberValue();
if (ve instanceof NumericValueEval) { }
d += ((NumericValueEval) ve).getNumberValue(); else if (ve instanceof BlankEval) {
} // do nothing
else if (ve instanceof BlankEval) { }
// do nothing else {
} return ErrorEval.VALUE_INVALID;
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);
} }
if(Double.isNaN(d) || Double.isInfinite(d)) {
return retval; return ErrorEval.NUM_ERROR;
}
return new NumberEval(d);
} }
public int getNumberOfOperands() { public int getNumberOfOperands() {

View File

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

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval; package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg; 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; * @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 DividePtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR = private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short) new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED ( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@ -49,18 +44,28 @@ public class DivideEval extends NumericOperationEval {
return NUM_XLATOR; 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; Eval retval = null;
double d0 = 0; double d0 = 0;
double d1 = 0; double d1 = 0;
switch (operands.length) { ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
default: // will rarely happen. currently the parser itself fails. if (ve instanceof NumericValueEval) {
retval = ErrorEval.UNKNOWN_ERROR; d0 = ((NumericValueEval) ve).getNumberValue();
break; }
case 2: else if (ve instanceof BlankEval) {
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); // do nothing
}
else {
retval = ErrorEval.VALUE_INVALID;
}
if (retval == null) { // no error yet
ve = singleOperandEvaluate(args[1], srcRow, srcCol);
if (ve instanceof NumericValueEval) { if (ve instanceof NumericValueEval) {
d0 = ((NumericValueEval) ve).getNumberValue(); d1 = ((NumericValueEval) ve).getNumberValue();
} }
else if (ve instanceof BlankEval) { else if (ve instanceof BlankEval) {
// do nothing // do nothing
@ -68,20 +73,7 @@ public class DivideEval extends NumericOperationEval {
else { else {
retval = ErrorEval.VALUE_INVALID; 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) { if (retval == null) {
retval = (d1 == 0) 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 CIRCULAR_REF_ERROR_CODE = 0xFFFFFFC4;
private static final int FUNCTION_NOT_IMPLEMENTED_CODE = 0xFFFFFFE2; 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); 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 // 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); 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; public static final int OFFSET = 78;
/** 148 */ /** 148 */
public static final int INDIRECT = 148; public static final int INDIRECT = 148;
/** 255 */
public static final int EXTERNAL_FUNC = 255;
} }
// convenient access to namespace // convenient access to namespace
private static final FunctionID ID = null; private static final FunctionID ID = null;
@ -51,6 +52,7 @@ public abstract class FunctionEval implements OperationEval {
Map m = new HashMap(); Map m = new HashMap();
addMapping(m, ID.OFFSET, new Offset()); addMapping(m, ID.OFFSET, new Offset());
addMapping(m, ID.INDIRECT, new Indirect()); addMapping(m, ID.INDIRECT, new Indirect());
addMapping(m, ID.EXTERNAL_FUNC, new ExternalFunction());
freeRefFunctionsByIdMap = m; freeRefFunctionsByIdMap = m;
} }
private static void addMapping(Map m, int offset, FreeRefFunction frf) { 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[252] = new Frequency(); // FREQUENCY
retval[253] = new NotImplementedFunction(); // ADDTOOLBAR retval[253] = new NotImplementedFunction(); // ADDTOOLBAR
retval[254] = new NotImplementedFunction(); // DELETETOOLBAR 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[256] = new NotImplementedFunction(); // RESETTOOLBAR
retval[257] = new Evaluate(); // EVALUATE retval[257] = new Evaluate(); // EVALUATE
retval[258] = new NotImplementedFunction(); // GETTOOLBAR retval[258] = new NotImplementedFunction(); // GETTOOLBAR

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval; package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg; 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; * @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 MultiplyPtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR = private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short) new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED ( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@ -49,46 +44,39 @@ public class MultiplyEval extends NumericOperationEval {
return NUM_XLATOR; return NUM_XLATOR;
} }
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
Eval retval = null; if(args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
double d0 = 0; double d0 = 0;
double d1 = 0; double d1 = 0;
switch (operands.length) { ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
default: // will rarely happen. currently the parser itself fails. if (ve instanceof NumericValueEval) {
retval = ErrorEval.UNKNOWN_ERROR; d0 = ((NumericValueEval) ve).getNumberValue();
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);
} }
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() { 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; package org.apache.poi.hssf.record.formula.eval;
/** /**
* Provides functionality for evaluating arguments to functions and operators. * 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) public static ValueEval getSingleValue(Eval arg, int srcCellRow, short srcCellCol)
throws EvaluationException { throws EvaluationException {
if (arg instanceof RefEval) {
RefEval re = (RefEval) arg;
return re.getInnerValueEval();
}
Eval result; 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); result = chooseSingleElementFromArea((AreaEval) arg, srcCellRow, srcCellCol);
} else { } else {
result = arg; result = arg;
@ -223,13 +220,26 @@ public final class OperandResolver {
* Some examples:<br/> * Some examples:<br/>
* " 123 " -&gt; 123.0<br/> * " 123 " -&gt; 123.0<br/>
* ".123" -&gt; 0.123<br/> * ".123" -&gt; 0.123<br/>
* These not supported yet:<br/>
* " $ 1,000.00 " -&gt; 1000.0<br/> * " $ 1,000.00 " -&gt; 1000.0<br/>
* "$1.25E4" -&gt; 12500.0<br/> * "$1.25E4" -&gt; 12500.0<br/>
* "5**2" -&gt; 500<br/>
* "250%" -&gt; 2.5<br/>
* *
* @param text * @param text
* @return <code>null</code> if the specified text cannot be parsed as a number * @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))) { if(!Character.isDigit(text.charAt(0))) {
// avoid using NumberFormatException to tell when string is not a number // avoid using NumberFormatException to tell when string is not a number
return null; return null;
@ -242,8 +252,26 @@ public final class OperandResolver {
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
return null; 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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval; package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg; 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; * @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 PowerPtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR = private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short) new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED ( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@ -49,48 +44,40 @@ public class PowerEval extends NumericOperationEval {
return NUM_XLATOR; return NUM_XLATOR;
} }
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
Eval retval = null; if(args.length != 2) {
return ErrorEval.VALUE_INVALID;
}
double d0 = 0; double d0 = 0;
double d1 = 0; double d1 = 0;
switch (operands.length) { ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
default: // will rarely happen. currently the parser itself fails. if (ve instanceof NumericValueEval) {
retval = ErrorEval.UNKNOWN_ERROR; d0 = ((NumericValueEval) ve).getNumberValue();
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);
} }
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() { public int getNumberOfOperands() {

View File

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

View File

@ -14,11 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 9, 2005
*
*
*/
package org.apache.poi.hssf.record.formula.eval; package org.apache.poi.hssf.record.formula.eval;
/** /**
@ -52,18 +48,4 @@ public interface RefEval extends ValueEval {
* returns the row index. * returns the row index.
*/ */
public short getRow(); 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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval; package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg; 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; * @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 SubtractPtg delegate;
private static final ValueEvalToNumericXlator NUM_XLATOR = private static final ValueEvalToNumericXlator NUM_XLATOR =
new ValueEvalToNumericXlator((short) new ValueEvalToNumericXlator((short)
( ValueEvalToNumericXlator.BOOL_IS_PARSED ( ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.STRING_IS_PARSED
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED | ValueEvalToNumericXlator.REF_STRING_IS_PARSED
@ -49,18 +44,28 @@ public class SubtractEval extends NumericOperationEval {
return NUM_XLATOR; 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; Eval retval = null;
double d0 = 0; double d0 = 0;
double d1 = 0; double d1 = 0;
switch (operands.length) { ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
default: // will rarely happen. currently the parser itself fails. if (ve instanceof NumericValueEval) {
retval = ErrorEval.UNKNOWN_ERROR; d0 = ((NumericValueEval) ve).getNumberValue();
break; }
case 2: else if (ve instanceof BlankEval) {
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); // do nothing
}
else {
retval = ErrorEval.VALUE_INVALID;
}
if (retval == null) { // no error yet
ve = singleOperandEvaluate(args[1], srcRow, srcCol);
if (ve instanceof NumericValueEval) { if (ve instanceof NumericValueEval) {
d0 = ((NumericValueEval) ve).getNumberValue(); d1 = ((NumericValueEval) ve).getNumberValue();
} }
else if (ve instanceof BlankEval) { else if (ve instanceof BlankEval) {
// do nothing // do nothing
@ -68,21 +73,8 @@ public class SubtractEval extends NumericOperationEval {
else { else {
retval = ErrorEval.VALUE_INVALID; 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) { if (retval == null) {
retval = (Double.isNaN(d0) || Double.isNaN(d1)) retval = (Double.isNaN(d0) || Double.isNaN(d1))
? (ValueEval) ErrorEval.VALUE_INVALID ? (ValueEval) ErrorEval.VALUE_INVALID

View File

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

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 8, 2005
*
*/
package org.apache.poi.hssf.record.formula.eval; package org.apache.poi.hssf.record.formula.eval;
import org.apache.poi.hssf.record.formula.Ptg; 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; * @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; 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 * called by reflection
*/ */
public UnaryPlusEval(Ptg ptg) { public UnaryPlusEval(Ptg ptg) {
this.delegate = (UnaryPlusPtg) ptg; this.delegate = (UnaryPlusPtg) ptg;
} }
// protected ValueEvalToNumericXlator getXlator() {
// return NUM_XLATOR;
// }
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) { public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
ValueEval retval = null; if(args.length != 1) {
return ErrorEval.VALUE_INVALID;
switch (operands.length) { }
default: double d;
retval = ErrorEval.UNKNOWN_ERROR; try {
break; ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
case 1: if(ve instanceof BlankEval) {
return NumberEval.ZERO;
// ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol); }
// if (ve instanceof NumericValueEval) { if(ve instanceof StringEval) {
// d = ((NumericValueEval) ve).getNumberValue(); // Note - asymmetric with UnaryMinus
// } // -"hello" evaluates to #VALUE!
// else if (ve instanceof BlankEval) { // but +"hello" evaluates to "hello"
// // do nothing return ve;
// } }
// else if (ve instanceof ErrorEval) { d = OperandResolver.coerceValueToDouble(ve);
// retval = ve; } catch (EvaluationException e) {
// } return e.getErrorEval();
if (operands[0] instanceof RefEval) { }
RefEval re = (RefEval) operands[0]; return new NumberEval(+d);
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 int getNumberOfOperands() { public int getNumberOfOperands() {
@ -144,5 +65,4 @@ public class UnaryPlusEval implements OperationEval /*extends NumericOperationEv
public int getType() { public int getType() {
return delegate.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; * @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 STRING_IS_PARSED = 0x0001;
public static final int BOOL_IS_PARSED = 0x0002; 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_BOOL_IS_PARSED = 0x0010;
public static final int REF_BLANK_IS_PARSED = 0x0020; 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 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; private final int flags;
public ValueEvalToNumericXlator(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 :) // most common case - least worries :)
else if (eval instanceof NumberEval) { else if (eval instanceof NumberEval) {
retval = (NumberEval) eval; retval = eval;
} }
// booleval // booleval
@ -125,50 +117,33 @@ public class ValueEvalToNumericXlator {
* @param eval * @param eval
*/ */
private ValueEval xlateRefEval(RefEval reval) { private ValueEval xlateRefEval(RefEval reval) {
ValueEval retval = null; ValueEval eval = reval.getInnerValueEval();
ValueEval eval = (ValueEval) reval.getInnerValueEval();
// most common case - least worries :) // most common case - least worries :)
if (eval instanceof NumberEval) { if (eval instanceof NumberEval) {
retval = (NumberEval) eval; return eval;
} }
// booleval if (eval instanceof BoolEval) {
else if (eval instanceof BoolEval) { return ((flags & REF_BOOL_IS_PARSED) > 0)
retval = ((flags & REF_BOOL_IS_PARSED) > 0)
? (ValueEval) eval ? (ValueEval) eval
: BlankEval.INSTANCE; : BlankEval.INSTANCE;
} }
// stringeval if (eval instanceof StringEval) {
else if (eval instanceof StringEval) { return xlateRefStringEval((StringEval) eval);
retval = xlateRefStringEval((StringEval) eval);
} }
// erroreval if (eval instanceof ErrorEval) {
else if (eval instanceof ErrorEval) { return eval;
retval = eval;
} }
// refeval if (eval instanceof BlankEval) {
else if (eval instanceof RefEval) { return xlateBlankEval(REF_BLANK_IS_PARSED);
RefEval re = (RefEval) eval;
retval = xlateRefEval(re);
} }
else if (eval instanceof BlankEval) { throw new RuntimeException("Invalid ValueEval type passed for conversion: ("
retval = xlateBlankEval(reval.isEvaluated() ? EVALUATED_REF_BLANK_IS_PARSED : REF_BLANK_IS_PARSED); + eval.getClass().getName() + ")");
}
// probably AreaEval ? then not acceptable.
else {
throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass());
}
return retval;
} }
/** /**
@ -176,93 +151,38 @@ public class ValueEvalToNumericXlator {
* @param eval * @param eval
*/ */
private ValueEval xlateStringEval(StringEval eval) { private ValueEval xlateStringEval(StringEval eval) {
ValueEval retval = null;
if ((flags & STRING_IS_PARSED) > 0) { if ((flags & STRING_IS_PARSED) > 0) {
String s = eval.getStringValue(); String s = eval.getStringValue();
try { Double d = OperandResolver.parseDouble(s);
double d = Double.parseDouble(s); if(d == null) {
retval = new NumberEval(d); return ErrorEval.VALUE_INVALID;
}
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;
}
} }
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? // strings are errors?
else if ((flags & STRING_IS_INVALID_VALUE) > 0) { if ((flags & STRING_IS_INVALID_VALUE) > 0) {
retval = ErrorEval.VALUE_INVALID; return ErrorEval.VALUE_INVALID;
} }
// ignore strings // ignore strings
else { return xlateBlankEval(BLANK_IS_PARSED);
retval = xlateBlankEval(BLANK_IS_PARSED);
}
return retval;
} }
/** /**
* uses the relevant flags to decode the StringEval * uses the relevant flags to decode the StringEval
* @param eval * @param eval
*/ */
private ValueEval xlateRefStringEval(StringEval eval) { private ValueEval xlateRefStringEval(StringEval sve) {
ValueEval retval = null;
if ((flags & REF_STRING_IS_PARSED) > 0) { if ((flags & REF_STRING_IS_PARSED) > 0) {
StringEval sve = (StringEval) eval;
String s = sve.getStringValue(); String s = sve.getStringValue();
try { Double d = OperandResolver.parseDouble(s);
double d = Double.parseDouble(s); if(d == null) {
retval = new NumberEval(d); return ErrorEval.VALUE_INVALID;
}
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;
}
} }
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 // strings are blanks
else { return BlankEval.INSTANCE;
retval = BlankEval.INSTANCE;
}
return retval;
} }
} }

View File

@ -33,8 +33,8 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
public class Avedev extends MultiOperandNumericFunction { public class Avedev extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (0 new ValueEvalToNumericXlator((short) (
// ValueEvalToNumericXlator.BOOL_IS_PARSED ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.STRING_IS_PARSED
@ -44,7 +44,6 @@ public class Avedev extends MultiOperandNumericFunction {
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_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 { public class Average extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (0 new ValueEvalToNumericXlator((short) (
// ValueEvalToNumericXlator.BOOL_IS_PARSED ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.STRING_IS_PARSED
@ -44,7 +44,6 @@ public class Average extends MultiOperandNumericFunction {
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on Jun 20, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.BoolEval;
@ -38,13 +35,10 @@ public abstract class FinanceFunction extends NumericFunction {
new ValueEvalToNumericXlator((short) (0 new ValueEvalToNumericXlator((short) (0
| ValueEvalToNumericXlator.BOOL_IS_PARSED | ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_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.REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.BLANK_IS_PARSED | ValueEvalToNumericXlator.BLANK_IS_PARSED
| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED | ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_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 * if they desire to return a different ValueEvalToNumericXlator instance
* than the default. * than the default.
*/ */
protected ValueEvalToNumericXlator getXlator() { protected final ValueEvalToNumericXlator getXlator() {
return DEFAULT_NUM_XLATOR; 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; ValueEval retval = null;
retval = singleOperandEvaluate(eval, srcRow, srcCol); retval = singleOperandEvaluate(eval, srcRow, srcCol);
if (retval instanceof NumericValueEval) { if (retval instanceof NumericValueEval) {
@ -74,5 +68,4 @@ public abstract class FinanceFunction extends NumericFunction {
} }
return retval; 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; * @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; Eval evalWhenFalse = BoolEval.FALSE;
switch (evals.length) { switch (args.length) {
case 3: case 3:
evalWhenFalse = evals[2]; evalWhenFalse = args[2];
case 2: case 2:
BoolEval beval = (BoolEval) evals[0]; BoolEval beval = (BoolEval) args[0]; // TODO - class cast exception
if (beval.getBooleanValue()) { if (beval.getBooleanValue()) {
retval = evals[1]; return args[1];
} }
else { return evalWhenFalse;
retval = evalWhenFalse;
}
break;
default: 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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval; import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.BoolEval; import org.apache.poi.hssf.record.formula.eval.BoolEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.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; import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
* *
*/ */
public class Isblank implements Function { public final class Isblank implements Function {
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
ValueEval retval = null; if(args.length != 1) {
boolean b = false; return ErrorEval.VALUE_INVALID;
}
switch (operands.length) { Eval arg = args[0];
default:
retval = ErrorEval.VALUE_INVALID; ValueEval singleCellValue;
break; try {
case 1: singleCellValue = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
if (operands[0] instanceof BlankEval) { } catch (EvaluationException e) {
b = true; return BoolEval.FALSE;
} }
else if (operands[0] instanceof AreaEval) { return BoolEval.valueOf(singleCellValue instanceof BlankEval);
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;
}
} }

View File

@ -14,125 +14,36 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.BlankEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.StringValueEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval;
/** /**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
* *
*/ */
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) { String str = OperandResolver.coerceValueToString(veval);
ValueEval retval = null;
String s = null; return new NumberEval(str.length());
} catch (EvaluationException e) {
switch (operands.length) { return e.getErrorEval();
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;
}
} }

View File

@ -14,10 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions; 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.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; * @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 = private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) ( new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_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.REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_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.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.OperandResolver; import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.StringEval; 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; 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; * @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 * Returns a specific number of characters from a text string, starting at
* the position you specify, based on the number of characters you specify. * the position you specify, based on the number of characters you specify.
@ -52,7 +51,8 @@ public class Mid extends TextFunction {
int numChars; int numChars;
try { 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); int startCharNum = evaluateNumberArg(args[1], srcCellRow, srcCellCol);
numChars = evaluateNumberArg(args[2], srcCellRow, srcCellCol); numChars = evaluateNumberArg(args[2], srcCellRow, srcCellCol);
startIx = startCharNum - 1; // convert to zero-based 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); ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
if (ev instanceof BlankEval) { if (ev instanceof BlankEval) {
// Note - for start_num arg, blank causes error(#VALUE!), // Note - for start_num arg, blank causes error(#VALUE!),
@ -89,12 +89,4 @@ public class Mid extends TextFunction {
return OperandResolver.coerceValueToInt(ev); 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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions; 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.ErrorEval;
@ -35,7 +32,6 @@ public class Mina extends MultiOperandNumericFunction {
new ValueEvalToNumericXlator((short) ( new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_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.REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_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; private static final int DEFAULT_MAX_NUM_OPERANDS = 30;
/** protected abstract ValueEvalToNumericXlator getXlator();
* 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;
}
/** /**
* Maximum number of operands accepted by this function. * Maximum number of operands accepted by this function.
@ -160,9 +137,7 @@ public abstract class MultiOperandNumericFunction extends NumericFunction {
* HSSFFormulaEvaluator where we store an array * HSSFFormulaEvaluator where we store an array
* of RefEvals as the "values" array. * of RefEvals as the "values" array.
*/ */
RefEval re = (values[j] instanceof RefEval) RefEval re = new Ref2DEval(null, values[j]);
? new Ref2DEval(null, ((RefEval) values[j]).getInnerValueEval(), true)
: new Ref2DEval(null, values[j], false);
ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol); ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol);
if (ve instanceof NumericValueEval) { if (ve instanceof NumericValueEval) {

View File

@ -39,10 +39,8 @@ public abstract class NumericFunction implements Function {
new ValueEvalToNumericXlator((short) ( new ValueEvalToNumericXlator((short) (
ValueEvalToNumericXlator.BOOL_IS_PARSED ValueEvalToNumericXlator.BOOL_IS_PARSED
| ValueEvalToNumericXlator.REF_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.REF_STRING_IS_PARSED
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE //| 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 { public class Stdev extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (0 new ValueEvalToNumericXlator((short) (
// ValueEvalToNumericXlator.BOOL_IS_PARSED ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.STRING_IS_PARSED
@ -44,7 +44,6 @@ public class Stdev extends MultiOperandNumericFunction {
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_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 { public class Sumsq extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR = private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) ( new ValueEvalToNumericXlator((short) (
// ValueEvalToNumericXlator.BOOL_IS_PARSED ValueEvalToNumericXlator.BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED //| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED //| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_PARSED | ValueEvalToNumericXlator.STRING_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_IS_PARSED //| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED //| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
ValueEvalToNumericXlator.REF_BLANK_IS_PARSED //| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
| ValueEvalToNumericXlator.BLANK_IS_PARSED //| ValueEvalToNumericXlator.BLANK_IS_PARSED
)); ));
protected ValueEvalToNumericXlator getXlator() { protected ValueEvalToNumericXlator getXlator() {

View File

@ -14,50 +14,23 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions; 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; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/ */
public class Sumx2my2 extends XYNumericFunction { public final class Sumx2my2 extends XYNumericFunction {
protected double evaluate(double[] xArray, double[] yArray) {
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { return MathX.sumx2my2(xArray, yArray);
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;
} }
} }

View File

@ -14,50 +14,23 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions; 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; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/ */
public class Sumx2py2 extends XYNumericFunction { public final class Sumx2py2 extends XYNumericFunction {
protected double evaluate(double[] xArray, double[] yArray) {
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { return MathX.sumx2py2(xArray, yArray);
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;
} }
} }

View File

@ -14,50 +14,23 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 15, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions; 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; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
*
*/ */
public class Sumxmy2 extends XYNumericFunction { public final class Sumxmy2 extends XYNumericFunction {
protected double evaluate(double[] xArray, double[] yArray) {
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { return MathX.sumxmy2(xArray, yArray);
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;
} }
} }

View File

@ -16,12 +16,11 @@
*/ */
package org.apache.poi.hssf.record.formula.functions; 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.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
import org.apache.poi.hssf.record.formula.eval.StringEval; import org.apache.poi.hssf.record.formula.eval.StringEval;
import org.apache.poi.hssf.record.formula.eval.StringValueEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval; 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. * value is string.
* @author Manda Wilson &lt; wilson at c bio dot msk cc dot org &gt; * @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 {
/** public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
* Removes leading and trailing spaces from value if evaluated
* operand value is string. if(args.length != 1) {
* Returns StringEval only if evaluated operand is of type string return ErrorEval.VALUE_INVALID;
* (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. try {
* ValueEval veval = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
* @see org.apache.poi.hssf.record.formula.eval.Eval
*/ String str = OperandResolver.coerceValueToString(veval);
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) { str = str.trim();
Eval retval = ErrorEval.VALUE_INVALID; if(str.length() < 1) {
String str = null; return StringEval.EMPTY_INSTANCE;
}
switch (operands.length) { return new StringEval(str);
default: } catch (EvaluationException e) {
break; return e.getErrorEval();
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;
}
} }

View File

@ -14,154 +14,151 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/*
* Created on May 29, 2005
*
*/
package org.apache.poi.hssf.record.formula.functions; package org.apache.poi.hssf.record.formula.functions;
import org.apache.poi.hssf.record.formula.eval.AreaEval; import org.apache.poi.hssf.record.formula.eval.AreaEval;
import org.apache.poi.hssf.record.formula.eval.ErrorEval; import org.apache.poi.hssf.record.formula.eval.ErrorEval;
import org.apache.poi.hssf.record.formula.eval.Eval; import org.apache.poi.hssf.record.formula.eval.Eval;
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.RefEval; import org.apache.poi.hssf.record.formula.eval.RefEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval; import org.apache.poi.hssf.record.formula.eval.ValueEval;
import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
/** /**
* @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt; * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
* *
*/ */
public abstract class XYNumericFunction extends NumericFunction { public abstract class XYNumericFunction implements Function {
protected static final int X = 0; protected static final int X = 0;
protected static final int Y = 1; 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() { protected static final class DoubleArrayPair {
return 30;
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 * Returns a double array that contains values for the numeric cells
* from among the list of operands. Blanks and Blank equivalent cells * from among the list of operands. Blanks and Blank equivalent cells
* are ignored. Error operands or cells containing operands of type * are ignored. Error operands or cells containing operands of type
* that are considered invalid and would result in #VALUE! error in * that are considered invalid and would result in #VALUE! error in
* excel cause this function to return null. * 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) { private static double[][] getNumberArray(Eval[] xops, Eval[] yops) throws EvaluationException {
double[][] retval = new double[2][30];
// 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; int count = 0;
if (xops.length > getMaxNumOperands() for (int i=0, iSize=nArrayItems; i<iSize; i++) {
|| yops.length > getMaxNumOperands() Eval xEval = xops[i];
|| xops.length != yops.length) { Eval yEval = yops[i];
retval = null;
} if (isNumberEval(xEval) && isNumberEval(yEval)) {
else { xResult[count] = getDoubleValue(xEval);
yResult[count] = getDoubleValue(yEval);
for (int i=0, iSize=xops.length; i<iSize; i++) { if (Double.isNaN(xResult[count]) || Double.isNaN(xResult[count])) {
Eval xEval = xops[i]; throw new EvaluationException(ErrorEval.NUM_ERROR);
Eval yEval = yops[i]; }
count++;
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++;
}
}
}
if (retval != null) { return new double[][] {
double[][] temp = retval; trimToSize(xResult, count),
retval[X] = trimToSize(retval[X], count); trimToSize(yResult, count),
retval[Y] = trimToSize(retval[Y], count); };
}
return retval;
} }
protected double[][] getValues(Eval[] operands, int srcCellRow, short srcCellCol) { private static double[][] getValues(Eval argX, Eval argY) throws EvaluationException {
double[][] retval = null;
if (argX instanceof ErrorEval) {
outer: do { throw new EvaluationException((ErrorEval) argX);
if (operands.length == 2) { }
Eval[] xEvals = new Eval[1]; if (argY instanceof ErrorEval) {
Eval[] yEvals = new Eval[1]; throw new EvaluationException((ErrorEval) argY);
if (operands[X] instanceof AreaEval) { }
AreaEval ae = (AreaEval) operands[0];
xEvals = ae.getValues(); Eval[] xEvals;
} Eval[] yEvals;
else if (operands[X] instanceof ErrorEval) { if (argX instanceof AreaEval) {
break outer; AreaEval ae = (AreaEval) argX;
} xEvals = ae.getValues();
else { } else {
xEvals[0] = operands[X]; xEvals = new Eval[] { argX, };
} }
if (operands[Y] instanceof AreaEval) { if (argY instanceof AreaEval) {
AreaEval ae = (AreaEval) operands[Y]; AreaEval ae = (AreaEval) argY;
yEvals = ae.getValues(); yEvals = ae.getValues();
} } else {
else if (operands[Y] instanceof ErrorEval) { yEvals = new Eval[] { argY, };
break outer; }
}
else { return getNumberArray(xEvals, yEvals);
yEvals[0] = operands[Y];
}
retval = getNumberArray(xEvals, yEvals, srcCellRow, srcCellCol);
}
} while (false);
return retval;
} }
private static double[] trimToSize(double[] arr, int len) {
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) {
double[] tarr = arr; double[] tarr = arr;
if (arr.length > len) { if (arr.length > len) {
tarr = new double[len]; tarr = new double[len];
@ -170,7 +167,7 @@ public abstract class XYNumericFunction extends NumericFunction {
return tarr; return tarr;
} }
protected static boolean isNumberEval(Eval eval) { private static boolean isNumberEval(Eval eval) {
boolean retval = false; boolean retval = false;
if (eval instanceof NumberEval) { if (eval instanceof NumberEval) {
@ -185,7 +182,7 @@ public abstract class XYNumericFunction extends NumericFunction {
return retval; return retval;
} }
protected static double getDoubleValue(Eval eval) { private static double getDoubleValue(Eval eval) {
double retval = 0; double retval = 0;
if (eval instanceof NumberEval) { if (eval instanceof NumberEval) {
NumberEval ne = (NumberEval) eval; 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.LessEqualEval;
import org.apache.poi.hssf.record.formula.eval.LessThanEval; 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.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.NotEqualEval;
import org.apache.poi.hssf.record.formula.eval.NumberEval; import org.apache.poi.hssf.record.formula.eval.NumberEval;
import org.apache.poi.hssf.record.formula.eval.OperationEval; import org.apache.poi.hssf.record.formula.eval.OperationEval;
@ -360,16 +361,16 @@ public class HSSFFormulaEvaluator {
EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker(); EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) { if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) {
return ErrorEval.CIRCULAR_REF_ERROR; return ErrorEval.CIRCULAR_REF_ERROR;
} }
try { try {
return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula()); return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula());
} finally { } finally {
tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum); tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
} }
} }
private static ValueEval evaluateCell(HSSFWorkbook workbook, HSSFSheet sheet, 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()); FormulaParser parser = new FormulaParser(cellFormulaText, workbook.getWorkbook());
parser.parse(); parser.parse();
Ptg[] ptgs = parser.getRPNPtg(); Ptg[] ptgs = parser.getRPNPtg();
@ -380,15 +381,24 @@ public class HSSFFormulaEvaluator {
for (int i = 0, iSize = ptgs.length; i < iSize; i++) { for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
// since we don't know how to handle these yet :( // since we don't know how to handle these yet :(
if (ptgs[i] instanceof ControlPtg) { continue; } Ptg ptg = ptgs[i];
if (ptgs[i] instanceof MemErrPtg) { continue; } if (ptg instanceof ControlPtg) { continue; }
if (ptgs[i] instanceof MissingArgPtg) { continue; } if (ptg instanceof MemErrPtg) { continue; }
if (ptgs[i] instanceof NamePtg) { continue; } if (ptg instanceof MissingArgPtg) { continue; }
if (ptgs[i] instanceof NameXPtg) { continue; } if (ptg instanceof NamePtg) {
if (ptgs[i] instanceof UnknownPtg) { continue; } // 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) { if (ptg instanceof OperationPtg) {
OperationPtg optg = (OperationPtg) ptgs[i]; OperationPtg optg = (OperationPtg) ptg;
// parens can be ignored since we have RPN tokens // parens can be ignored since we have RPN tokens
if (optg instanceof ParenthesisPtg) { continue; } if (optg instanceof ParenthesisPtg) { continue; }
@ -408,47 +418,67 @@ public class HSSFFormulaEvaluator {
Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet); Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
stack.push(opresult); stack.push(opresult);
} }
else if (ptgs[i] instanceof ReferencePtg) { else if (ptg instanceof ReferencePtg) {
ReferencePtg ptg = (ReferencePtg) ptgs[i]; ReferencePtg refPtg = (ReferencePtg) ptg;
short colnum = ptg.getColumn(); short colnum = refPtg.getColumn();
short rownum = ptg.getRow(); short rownum = refPtg.getRow();
HSSFRow row = sheet.getRow(rownum); HSSFRow row = sheet.getRow(rownum);
HSSFCell cell = (row != null) ? row.getCell(colnum) : null; 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) { else if (ptg instanceof Ref3DPtg) {
Ref3DPtg ptg = (Ref3DPtg) ptgs[i]; Ref3DPtg refPtg = (Ref3DPtg) ptg;
short colnum = ptg.getColumn(); short colnum = refPtg.getColumn();
short rownum = ptg.getRow(); short rownum = refPtg.getRow();
Workbook wb = workbook.getWorkbook(); 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); HSSFRow row = xsheet.getRow(rownum);
HSSFCell cell = (row != null) ? row.getCell(colnum) : null; 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) { else if (ptg instanceof AreaPtg) {
AreaPtg ap = (AreaPtg) ptgs[i]; AreaPtg ap = (AreaPtg) ptg;
AreaEval ae = evaluateAreaPtg(sheet, workbook, ap); AreaEval ae = evaluateAreaPtg(sheet, workbook, ap);
stack.push(ae); stack.push(ae);
} }
else if (ptgs[i] instanceof Area3DPtg) { else if (ptg instanceof Area3DPtg) {
Area3DPtg a3dp = (Area3DPtg) ptgs[i]; Area3DPtg a3dp = (Area3DPtg) ptg;
AreaEval ae = evaluateArea3dPtg(workbook, a3dp); AreaEval ae = evaluateArea3dPtg(workbook, a3dp);
stack.push(ae); stack.push(ae);
} }
else { else {
Eval ptgEval = getEvalForPtg(ptgs[i]); Eval ptgEval = getEvalForPtg(ptg);
stack.push(ptgEval); stack.push(ptgEval);
} }
} }
ValueEval value = ((ValueEval) stack.pop()); ValueEval value = ((ValueEval) stack.pop());
if (value instanceof RefEval) { if (!stack.isEmpty()) {
RefEval rv = (RefEval) value; 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(); return rv.getInnerValueEval();
} }
if (value instanceof AreaEval) { if (evaluationResult instanceof AreaEval) {
AreaEval ae = (AreaEval) value; AreaEval ae = (AreaEval) evaluationResult;
if (ae.isRow()) { if (ae.isRow()) {
if(ae.isColumn()) { if(ae.isColumn()) {
return ae.getValues()[0]; return ae.getValues()[0];
@ -460,7 +490,7 @@ public class HSSFFormulaEvaluator {
} }
return ErrorEval.VALUE_INVALID; return ErrorEval.VALUE_INVALID;
} }
return value; return evaluationResult;
} }
private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum, private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum,
@ -486,7 +516,7 @@ public class HSSFFormulaEvaluator {
// (eg C:C) // (eg C:C)
// TODO: Handle whole column ranges properly // TODO: Handle whole column ranges properly
if(row1 == -1 && row0 >= 0) { if(row1 == -1 && row0 >= 0) {
row1 = (short)sheet.getLastRowNum(); row1 = (short)sheet.getLastRowNum();
} }
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)]; ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
@ -514,7 +544,7 @@ public class HSSFFormulaEvaluator {
// (eg C:C) // (eg C:C)
// TODO: Handle whole column ranges properly // TODO: Handle whole column ranges properly
if(row1 == -1 && row0 >= 0) { if(row1 == -1 && row0 >= 0) {
row1 = (short)xsheet.getLastRowNum(); row1 = (short)xsheet.getLastRowNum();
} }
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)]; 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, private static Ref2DEval createRef2DEval(ReferencePtg ptg, HSSFCell cell,
HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) { HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
if (cell == null) { if (cell == null) {
return new Ref2DEval(ptg, BlankEval.INSTANCE, false); return new Ref2DEval(ptg, BlankEval.INSTANCE);
} }
switch (cell.getCellType()) { switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_NUMERIC: 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: 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: 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: 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: case HSSFCell.CELL_TYPE_BLANK:
return new Ref2DEval(ptg, BlankEval.INSTANCE, false); return new Ref2DEval(ptg, BlankEval.INSTANCE);
case HSSFCell.CELL_TYPE_ERROR: 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() + ")"); throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
} }
@ -657,21 +687,21 @@ public class HSSFFormulaEvaluator {
private static Ref3DEval createRef3DEval(Ref3DPtg ptg, HSSFCell cell, private static Ref3DEval createRef3DEval(Ref3DPtg ptg, HSSFCell cell,
HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) { HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
if (cell == null) { if (cell == null) {
return new Ref3DEval(ptg, BlankEval.INSTANCE, false); return new Ref3DEval(ptg, BlankEval.INSTANCE);
} }
switch (cell.getCellType()) { switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_NUMERIC: 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: 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: 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: 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: case HSSFCell.CELL_TYPE_BLANK:
return new Ref3DEval(ptg, BlankEval.INSTANCE, false); return new Ref3DEval(ptg, BlankEval.INSTANCE);
case HSSFCell.CELL_TYPE_ERROR: 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() + ")"); throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
} }

View File

@ -16,14 +16,14 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.eventusermodel; 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.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList; 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.LastCellOfRowDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord; import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord; 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.Record;
import org.apache.poi.hssf.record.RowRecord; import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
import junit.framework.TestCase; * Tests for MissingRecordAwareHSSFListener
*/
public class TestMissingRecordAwareHSSFListener extends TestCase { public final class TestMissingRecordAwareHSSFListener extends TestCase {
private String dirname;
public TestMissingRecordAwareHSSFListener() { private Record[] r;
dirname = System.getProperty("HSSF.testdata.path");
} public void setUp() {
String dirname = System.getProperty("HSSF.testdata.path");
public void testMissingRowRecords() throws Exception {
File f = new File(dirname + "/MissingBits.xls"); File f = new File(dirname + "/MissingBits.xls");
HSSFRequest req = new HSSFRequest(); HSSFRequest req = new HSSFRequest();
MockHSSFListener mockListen = new MockHSSFListener(); MockHSSFListener mockListen = new MockHSSFListener();
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen); MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
req.addListenerForAllRecords(listener); req.addListenerForAllRecords(listener);
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
HSSFEventFactory factory = new HSSFEventFactory(); HSSFEventFactory factory = new HSSFEventFactory();
factory.processWorkbookEvents(req, fs); try {
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
// Check we got the dummy records factory.processWorkbookEvents(req, fs);
Record[] r = (Record[]) } catch (IOException e) {
mockListen.records.toArray(new Record[mockListen.records.size()]); throw new RuntimeException(e);
}
r = mockListen.getRecords();
}
public void testMissingRowRecords() throws Exception {
// We have rows 0, 1, 2, 20 and 21 // We have rows 0, 1, 2, 20 and 21
int row0 = -1; int row0 = -1;
@ -105,20 +107,6 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
} }
public void testEndOfRowRecords() throws Exception { 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 // Find the cell at 0,0
int cell00 = -1; int cell00 = -1;
@ -240,20 +228,6 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
public void testMissingCellRecords() throws Exception { 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 // Find the cell at 0,0
int cell00 = -1; int cell00 = -1;
@ -352,25 +326,35 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
assertEquals(10, mc.getColumn()); assertEquals(10, mc.getColumn());
} }
private static class MockHSSFListener implements HSSFListener { private static final class MockHSSFListener implements HSSFListener {
private MockHSSFListener() {} public MockHSSFListener() {}
private ArrayList records = new ArrayList(); private final List _records = new ArrayList();
public void processRecord(Record record) { public void processRecord(Record record) {
records.add(record); _records.add(record);
if(record instanceof MissingRowDummyRecord) { if(record instanceof MissingRowDummyRecord) {
MissingRowDummyRecord mr = (MissingRowDummyRecord)record; MissingRowDummyRecord mr = (MissingRowDummyRecord)record;
System.out.println("Got dummy row " + mr.getRowNumber()); log("Got dummy row " + mr.getRowNumber());
} }
if(record instanceof MissingCellDummyRecord) { if(record instanceof MissingCellDummyRecord) {
MissingCellDummyRecord mc = (MissingCellDummyRecord)record; 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) { if(record instanceof LastCellOfRowDummyRecord) {
LastCellOfRowDummyRecord lc = (LastCellOfRowDummyRecord)record; 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 // TODO - have this suite incorporated into a higher level one
public static Test suite() { public static Test suite() {
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula.functions"); TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula.functions");
result.addTestSuite(TestAverage.class);
result.addTestSuite(TestCountFuncs.class); result.addTestSuite(TestCountFuncs.class);
result.addTestSuite(TestDate.class); result.addTestSuite(TestDate.class);
result.addTestSuite(TestFinanceLib.class); result.addTestSuite(TestFinanceLib.class);
result.addTestSuite(TestIndex.class); result.addTestSuite(TestIndex.class);
result.addTestSuite(TestIsBlank.class);
result.addTestSuite(TestLen.class);
result.addTestSuite(TestMid.class); result.addTestSuite(TestMid.class);
result.addTestSuite(TestMathX.class); result.addTestSuite(TestMathX.class);
result.addTestSuite(TestMatch.class); result.addTestSuite(TestMatch.class);
@ -43,6 +46,8 @@ public final class AllIndividualFunctionEvaluationTests {
result.addTestSuite(TestSumproduct.class); result.addTestSuite(TestSumproduct.class);
result.addTestSuite(TestStatsLib.class); result.addTestSuite(TestStatsLib.class);
result.addTestSuite(TestTFunc.class); result.addTestSuite(TestTFunc.class);
result.addTestSuite(TestTrim.class);
result.addTestSuite(TestXYNumericFunction.class);
return result; return result;
} }

View File

@ -58,6 +58,6 @@ final class EvalFactory {
* Creates a single RefEval (with value zero) * Creates a single RefEval (with value zero)
*/ */
public static RefEval createRefEval(String refStr) { public static RefEval createRefEval(String refStr) {
return new Ref2DEval(new 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)) { if(errorCodesAreEqual(ee, ErrorEval.FUNCTION_NOT_IMPLEMENTED)) {
return "Function not implemented"; return "Function not implemented";
} }
if(errorCodesAreEqual(ee, ErrorEval.UNKNOWN_ERROR)) {
return "Unknown error";
}
if(errorCodesAreEqual(ee, ErrorEval.VALUE_INVALID)) { if(errorCodesAreEqual(ee, ErrorEval.VALUE_INVALID)) {
return "Error code: #VALUE! (invalid value)"; 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); 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, }; Eval[] args= { arg0, criteriaArg, };
double actual = NumericFunctionInvoker.invoke(new Countif(), args); 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 // startPos is 1x1 area ref, numChars is cell ref
AreaEval aeStart = new Area2DEval(new AreaPtg("A1:A1"), new ValueEval[] { new NumberEval(2), } ); 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"), aeStart, reNumChars, "ala");
confirmMid(new StringEval("galactic"), new NumberEval(3.1), BlankEval.INSTANCE, ""); 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.FALSE, "");
confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.TRUE, "l"); confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.TRUE, "l");
confirmMid(BlankEval.INSTANCE, new NumberEval(3), BoolEval.TRUE, "");
} }

View File

@ -50,7 +50,7 @@ public final class TestSumproduct extends TestCase {
public void testScalarSimple() { 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 = { Eval[] args = {
refEval, refEval,
new NumberEval(2), new NumberEval(2),

View File

@ -50,7 +50,7 @@ public final class TestTFunc extends TestCase {
* where cell A1 has the specified innerValue * where cell A1 has the specified innerValue
*/ */
private Eval invokeTWithReference(ValueEval innerValue) { private Eval invokeTWithReference(ValueEval innerValue) {
Eval arg = new Ref2DEval(new 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); 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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf; package org.apache.poi.hssf;
import junit.framework.Test; 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.TestDrawingManager;
import org.apache.poi.hssf.model.TestFormulaParser; import org.apache.poi.hssf.model.TestFormulaParser;
import org.apache.poi.hssf.model.TestSheet; import org.apache.poi.hssf.model.TestSheet;
import org.apache.poi.hssf.record.TestAreaFormatRecord; import org.apache.poi.hssf.record.AllRecordTests;
import org.apache.poi.hssf.record.TestAreaRecord; import org.apache.poi.hssf.usermodel.AllUserModelTests;
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.util.TestAreaReference; import org.apache.poi.hssf.util.TestAreaReference;
import org.apache.poi.hssf.util.TestCellReference; import org.apache.poi.hssf.util.TestCellReference;
import org.apache.poi.hssf.util.TestRKUtil; 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 * @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()); junit.textui.TestRunner.run(suite());
} }
public static Test suite() public static Test suite() {
{ TestSuite suite = new TestSuite("Tests for org.apache.poi.hssf");
TestSuite suite = // $JUnit-BEGIN$
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));
suite.addTest(AllUserModelTests.suite());
suite.addTest(AllRecordTests.suite());
suite.addTest(new TestSuite(TestFormulaParser.class)); 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(TestAreaReference.class));
suite.addTest(new TestSuite(TestCellReference.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(TestRKUtil.class));
suite.addTest(new TestSuite(TestSheetReferences.class)); suite.addTest(new TestSuite(TestSheetReferences.class));
suite.addTest(new TestSuite(TestEventRecordFactory.class));
suite.addTest(new TestSuite(TestModelFactory.class));
suite.addTest(AllFormulaTests.suite()); suite.addTest(new TestSuite(TestDrawingManager.class));
suite.addTest(new TestSuite(TestValueRecordsAggregate.class)); suite.addTest(new TestSuite(TestSheet.class));
suite.addTest(new TestSuite(TestNameRecord.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));
suite.addTest(new TestSuite(TestHSSFComment.class));
//$JUnit-END$
return suite; return suite;
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -18,27 +18,34 @@
package org.apache.poi.hssf.model; package org.apache.poi.hssf.model;
import java.util.List; import junit.framework.AssertionFailedError;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg; import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
import org.apache.poi.hssf.record.formula.AddPtg; import org.apache.poi.hssf.record.formula.AddPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.AttrPtg; import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.BoolPtg; import org.apache.poi.hssf.record.formula.BoolPtg;
import org.apache.poi.hssf.record.formula.ConcatPtg;
import org.apache.poi.hssf.record.formula.DividePtg; import org.apache.poi.hssf.record.formula.DividePtg;
import org.apache.poi.hssf.record.formula.EqualPtg; 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.FuncPtg;
import org.apache.poi.hssf.record.formula.FuncVarPtg; import org.apache.poi.hssf.record.formula.FuncVarPtg;
import org.apache.poi.hssf.record.formula.IntPtg; import org.apache.poi.hssf.record.formula.IntPtg;
import org.apache.poi.hssf.record.formula.LessEqualPtg; import org.apache.poi.hssf.record.formula.LessEqualPtg;
import org.apache.poi.hssf.record.formula.LessThanPtg; 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.NamePtg;
import org.apache.poi.hssf.record.formula.NotEqualPtg; import org.apache.poi.hssf.record.formula.NotEqualPtg;
import org.apache.poi.hssf.record.formula.NumberPtg; 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.Ptg;
import org.apache.poi.hssf.record.formula.ReferencePtg; import org.apache.poi.hssf.record.formula.ReferencePtg;
import org.apache.poi.hssf.record.formula.StringPtg; 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.UnaryMinusPtg;
import org.apache.poi.hssf.record.formula.UnaryPlusPtg; import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.hssf.usermodel.HSSFCell;
@ -64,15 +71,25 @@ public class TestFormulaParser extends TestCase {
public void tearDown() { 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() { public void testSimpleFormula() {
FormulaParser fp = new FormulaParser("2+2;",null); FormulaParser fp = new FormulaParser("2+2",null);
fp.parse(); fp.parse();
Ptg[] ptgs = fp.getRPNPtg(); Ptg[] ptgs = fp.getRPNPtg();
assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3); assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
} }
public void testFormulaWithSpace1() { public void testFormulaWithSpace1() {
FormulaParser fp = new FormulaParser(" 2 + 2 ;",null); FormulaParser fp = new FormulaParser(" 2 + 2 ",null);
fp.parse(); fp.parse();
Ptg[] ptgs = fp.getRPNPtg(); Ptg[] ptgs = fp.getRPNPtg();
assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3); assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
@ -85,7 +102,7 @@ public class TestFormulaParser extends TestCase {
public void testFormulaWithSpace2() { public void testFormulaWithSpace2() {
Ptg[] ptgs; Ptg[] ptgs;
FormulaParser fp; FormulaParser fp;
fp = new FormulaParser("2+ sum( 3 , 4) ;",null); fp = new FormulaParser("2+ sum( 3 , 4) ",null);
fp.parse(); fp.parse();
ptgs = fp.getRPNPtg(); ptgs = fp.getRPNPtg();
assertTrue("five tokens expected, got "+ptgs.length,ptgs.length == 5); assertTrue("five tokens expected, got "+ptgs.length,ptgs.length == 5);
@ -94,7 +111,7 @@ public class TestFormulaParser extends TestCase {
public void testFormulaWithSpaceNRef() { public void testFormulaWithSpaceNRef() {
Ptg[] ptgs; Ptg[] ptgs;
FormulaParser fp; FormulaParser fp;
fp = new FormulaParser("sum( A2:A3 );",null); fp = new FormulaParser("sum( A2:A3 )",null);
fp.parse(); fp.parse();
ptgs = fp.getRPNPtg(); ptgs = fp.getRPNPtg();
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2); assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
@ -103,7 +120,7 @@ public class TestFormulaParser extends TestCase {
public void testFormulaWithString() { public void testFormulaWithString() {
Ptg[] ptgs; Ptg[] ptgs;
FormulaParser fp; FormulaParser fp;
fp = new FormulaParser("\"hello\" & \"world\" ;",null); fp = new FormulaParser("\"hello\" & \"world\" ",null);
fp.parse(); fp.parse();
ptgs = fp.getRPNPtg(); ptgs = fp.getRPNPtg();
assertTrue("three token expected, got " + ptgs.length, ptgs.length == 3); assertTrue("three token expected, got " + ptgs.length, ptgs.length == 3);
@ -276,20 +293,21 @@ public class TestFormulaParser extends TestCase {
} }
public void testMacroFunction() { public void testMacroFunction() {
Workbook w = new Workbook(); Workbook w = Workbook.createWorkbook();
FormulaParser fp = new FormulaParser("FOO()", w); FormulaParser fp = new FormulaParser("FOO()", w);
fp.parse(); fp.parse();
Ptg[] ptg = fp.getRPNPtg(); Ptg[] ptg = fp.getRPNPtg();
AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[0]; // the name gets encoded as the first arg
assertEquals("externalflag", tfunc.getName()); NamePtg tname = (NamePtg) ptg[0];
NamePtg tname = (NamePtg) ptg[1];
assertEquals("FOO", tname.toFormulaString(w)); assertEquals("FOO", tname.toFormulaString(w));
AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1];
assertEquals("externalflag", tfunc.getName());
} }
public void testEmbeddedSlash() { 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(); fp.parse();
Ptg[] ptg = fp.getRPNPtg(); Ptg[] ptg = fp.getRPNPtg();
assertTrue("first ptg is string",ptg[0] instanceof StringPtg); assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
@ -589,4 +607,234 @@ public class TestFormulaParser extends TestCase {
}; };
assertEquals("NA()", FormulaParser.toFormulaString(book, ptgs)); 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) * @author Andrew C. Oliver (acoliver at apache dot org)
*/ */
public class TestSupBookRecord public final class TestSupBookRecord extends TestCase {
extends TestCase
{
/** /**
* This contains a fake data section of a SubBookRecord * This contains a fake data section of a SubBookRecord
*/ */
byte[] data = new byte[] { byte[] dataIR = new byte[] {
(byte)0x04,(byte)0x00,(byte)0x01,(byte)0x04 (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) public TestSupBookRecord(String name)
{ {
@ -47,36 +57,67 @@ public class TestSupBookRecord
/** /**
* tests that we can load the record * tests that we can load the record
*/ */
public void testLoad() public void testLoadIR() {
throws Exception
{
SupBookRecord record = new SupBookRecord(new TestcaseRecordInputStream((short)0x01AE, (short)data.length, data)); SupBookRecord record = new SupBookRecord(new TestcaseRecordInputStream((short)0x01AE, dataIR));
assertEquals( 0x401, record.getFlag()); //expected flag assertTrue( record.isInternalReferences() ); //expected flag
assertEquals( 0x4, record.getNumberOfSheets() ); //expected # of sheets assertEquals( 0x4, record.getNumberOfSheets() ); //expected # of sheets
assertEquals( 8, record.getRecordSize() ); //sid+size+data assertEquals( 8, record.getRecordSize() ); //sid+size+data
record.validateSid((short)0x01AE); 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 * Tests that we can store the record
* *
*/ */
public void testStore() public void testStoreIR() {
{ SupBookRecord record = SupBookRecord.createInternalReferences((short)4);
SupBookRecord record = new SupBookRecord();
record.setFlag( (short) 0x401 );
record.setNumberOfSheets( (short)0x4 );
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(); TestcaseRecordInputStream.confirmRecordEncoding(0x01AE, dataER, 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]);
} }
public static void main(String [] args) { public static void main(String [] args) {
@ -84,6 +125,4 @@ public class TestSupBookRecord
.println("Testing org.apache.poi.hssf.record.SupBookRecord"); .println("Testing org.apache.poi.hssf.record.SupBookRecord");
junit.textui.TestRunner.run(TestSupBookRecord.class); junit.textui.TestRunner.run(TestSupBookRecord.class);
} }
} }

View File

@ -1,4 +1,3 @@
/* ==================================================================== /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -16,11 +15,12 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import junit.framework.Assert;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
/** /**
@ -33,6 +33,14 @@ import org.apache.poi.util.LittleEndian;
public class TestcaseRecordInputStream public class TestcaseRecordInputStream
extends RecordInputStream 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) public TestcaseRecordInputStream(short sid, short length, byte[] data)
{ {
super(new ByteArrayInputStream(mergeDataAndSid(sid, length, data))); super(new ByteArrayInputStream(mergeDataAndSid(sid, length, data)));
@ -46,4 +54,18 @@ public class TestcaseRecordInputStream
System.arraycopy(data, 0, result, 4, data.length); System.arraycopy(data, 0, result, 4, data.length);
return result; 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 Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with contributor license agreements. See the NOTICE file distributed with
@ -28,6 +27,7 @@ import junit.framework.TestCase;
import org.apache.poi.hssf.model.Workbook; import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; 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 * 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) * @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. */ /** Directory containing the test data. */
private static String dataDir = System.getProperty("HSSF.testdata.path"); private static String dataDir = System.getProperty("HSSF.testdata.path");
@ -51,16 +50,16 @@ public abstract class AbstractPtgTestCase extends TestCase
throws IOException { throws IOException {
File file = new File(dataDir, filename); File file = new File(dataDir, filename);
InputStream stream = new BufferedInputStream(new FileInputStream(file)); InputStream stream = new BufferedInputStream(new FileInputStream(file));
try // 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,
return new HSSFWorkbook(stream); InputStream wrappedStream = POIFSFileSystem.createNonClosingInputStream(stream);
} try {
finally return new HSSFWorkbook(wrappedStream);
{ } finally {
stream.close(); stream.close();
} }
} }
/** /**
* Creates a new Workbook and adds one sheet with the specified name * 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); book.setSheetName(0, sheetName);
return book; return book;
} }
} }

View File

@ -14,7 +14,6 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hssf.record.formula; package org.apache.poi.hssf.record.formula;
@ -33,7 +32,8 @@ public class AllFormulaTests {
result.addTestSuite(TestArea3DPtg.class); result.addTestSuite(TestArea3DPtg.class);
result.addTestSuite(TestAreaErrPtg.class); result.addTestSuite(TestAreaErrPtg.class);
result.addTestSuite(TestAreaPtg.class); result.addTestSuite(TestAreaPtg.class);
result.addTestSuite(TestErrPtg.class); result.addTestSuite(TestErrPtg.class);
result.addTestSuite(TestExternalFunctionFormulas.class);
result.addTestSuite(TestFuncPtg.class); result.addTestSuite(TestFuncPtg.class);
result.addTestSuite(TestIntersectionPtg.class); result.addTestSuite(TestIntersectionPtg.class);
result.addTestSuite(TestPercentPtg.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; 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.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>. * Test <code>HSSFPicture</code>.
* *
* @author Yegor Kozlov (yegor at apache.org) * @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(); HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sh1 = wb.createSheet(); HSSFSheet sh1 = wb.createSheet();
HSSFPatriarch p1 = sh1.createDrawingPatriarch(); 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); HSSFPicture picture1 = p1.createPicture(new HSSFClientAnchor(), idx1);
HSSFClientAnchor anchor1 = picture1.getPreferredSize(); HSSFClientAnchor anchor1 = picture1.getPreferredSize();
@ -52,28 +55,25 @@ public class TestHSSFPicture extends TestCase{
/** /**
* Copied from org.apache.poi.hssf.usermodel.examples.OfficeDrawing * Copied from org.apache.poi.hssf.usermodel.examples.OfficeDrawing
*/ */
private static int loadPicture( String path, HSSFWorkbook wb ) throws IOException private static byte[] getTestDataFileContent(String fileName) {
{ ByteArrayOutputStream bos = new ByteArrayOutputStream();
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;
}
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();
}
} }