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:
parent
900f3999f0
commit
e685873465
File diff suppressed because it is too large
Load Diff
302
src/java/org/apache/poi/hssf/model/LinkTable.java
Executable file
302
src/java/org/apache/poi/hssf/model/LinkTable.java
Executable 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);
|
||||
}
|
||||
}
|
65
src/java/org/apache/poi/hssf/model/RecordStream.java
Executable file
65
src/java/org/apache/poi/hssf/model/RecordStream.java
Executable 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;
|
||||
}
|
||||
}
|
@ -79,10 +79,8 @@ public class Workbook implements Model
|
||||
*/
|
||||
protected SSTRecord sst = null;
|
||||
|
||||
/**
|
||||
* Holds the Extern Sheet with references to bound sheets
|
||||
*/
|
||||
protected ExternSheetRecord externSheet= null;
|
||||
|
||||
private LinkTable linkTable; // optionally occurs if there are references in the document. (4.10.3)
|
||||
|
||||
/**
|
||||
* holds the "boundsheet" records (aka bundlesheet) so that they can have their
|
||||
@ -92,8 +90,6 @@ public class Workbook implements Model
|
||||
|
||||
protected ArrayList formats = new ArrayList();
|
||||
|
||||
protected ArrayList names = new ArrayList();
|
||||
|
||||
protected ArrayList hyperlinks = new ArrayList();
|
||||
|
||||
protected int numxfs = 0; // hold the number of extended format records
|
||||
@ -134,6 +130,7 @@ public class Workbook implements Model
|
||||
new Integer(recs.size()));
|
||||
Workbook retval = new Workbook();
|
||||
ArrayList records = new ArrayList(recs.size() / 3);
|
||||
retval.records.setRecords(records);
|
||||
|
||||
int k;
|
||||
for (k = 0; k < recs.size(); k++) {
|
||||
@ -192,21 +189,16 @@ public class Workbook implements Model
|
||||
retval.records.setBackuppos( k );
|
||||
break;
|
||||
case ExternSheetRecord.sid :
|
||||
if (log.check( POILogger.DEBUG ))
|
||||
log.log(DEBUG, "found extern sheet record at " + k);
|
||||
retval.externSheet = ( ExternSheetRecord ) rec;
|
||||
break;
|
||||
throw new RuntimeException("Extern sheet is part of LinkTable");
|
||||
case NameRecord.sid :
|
||||
if (log.check( POILogger.DEBUG ))
|
||||
log.log(DEBUG, "found name record at " + k);
|
||||
retval.names.add(rec);
|
||||
// retval.records.namepos = k;
|
||||
break;
|
||||
throw new RuntimeException("DEFINEDNAME is part of LinkTable");
|
||||
case SupBookRecord.sid :
|
||||
if (log.check( POILogger.DEBUG ))
|
||||
log.log(DEBUG, "found SupBook record at " + k);
|
||||
retval.linkTable = new LinkTable(recs, k, retval.records);
|
||||
// retval.records.supbookpos = k;
|
||||
break;
|
||||
k+=retval.linkTable.getRecordCount() - 1;
|
||||
continue;
|
||||
case FormatRecord.sid :
|
||||
if (log.check( POILogger.DEBUG ))
|
||||
log.log(DEBUG, "found format record at " + k);
|
||||
@ -263,8 +255,6 @@ public class Workbook implements Model
|
||||
}
|
||||
}
|
||||
|
||||
retval.records.setRecords(records);
|
||||
|
||||
if (retval.windowOne == null) {
|
||||
retval.windowOne = (WindowOneRecord) retval.createWindowOne();
|
||||
}
|
||||
@ -283,6 +273,7 @@ public class Workbook implements Model
|
||||
log.log( DEBUG, "creating new workbook from scratch" );
|
||||
Workbook retval = new Workbook();
|
||||
ArrayList records = new ArrayList( 30 );
|
||||
retval.records.setRecords(records);
|
||||
ArrayList formats = new ArrayList( 8 );
|
||||
|
||||
records.add( retval.createBOF() );
|
||||
@ -339,8 +330,9 @@ public class Workbook implements Model
|
||||
records.add( retval.createStyle( k ) );
|
||||
}
|
||||
records.add( retval.createUseSelFS() );
|
||||
for ( int k = 0; k < 1; k++ )
|
||||
{ // now just do 1
|
||||
|
||||
int nBoundSheets = 1; // now just do 1
|
||||
for ( int k = 0; k < nBoundSheets; k++ ) {
|
||||
BoundSheetRecord bsr =
|
||||
(BoundSheetRecord) retval.createBoundSheet( k );
|
||||
|
||||
@ -351,12 +343,14 @@ public class Workbook implements Model
|
||||
// retval.records.supbookpos = retval.records.bspos + 1;
|
||||
// retval.records.namepos = retval.records.supbookpos + 2;
|
||||
records.add( retval.createCountry() );
|
||||
for ( int k = 0; k < nBoundSheets; k++ ) {
|
||||
retval.getOrCreateLinkTable().checkExternSheet(k);
|
||||
}
|
||||
retval.sst = (SSTRecord) retval.createSST();
|
||||
records.add( retval.sst );
|
||||
records.add( retval.createExtendedSST() );
|
||||
|
||||
records.add( retval.createEOF() );
|
||||
retval.records.setRecords(records);
|
||||
if (log.check( POILogger.DEBUG ))
|
||||
log.log( DEBUG, "exit create new workbook from scratch" );
|
||||
return retval;
|
||||
@ -369,36 +363,20 @@ public class Workbook implements Model
|
||||
* @param sheetIndex Index to match
|
||||
* @return null if no builtin NameRecord matches
|
||||
*/
|
||||
public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex)
|
||||
{
|
||||
Iterator iterator = names.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
NameRecord record = ( NameRecord ) iterator.next();
|
||||
|
||||
//print areas are one based
|
||||
if (record.getBuiltInName() == name && record.getIndexToSheet() == sheetIndex) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
public NameRecord getSpecificBuiltinRecord(byte name, int sheetIndex)
|
||||
{
|
||||
return getOrCreateLinkTable().getSpecificBuiltinRecord(name, sheetIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified Builtin NameRecord that matches the name and index
|
||||
* @param name byte representation of the builtin to match
|
||||
* @param sheetIndex zero-based sheet reference
|
||||
*/
|
||||
public void removeBuiltinRecord(byte name, int sheetIndex) {
|
||||
//the name array is smaller so searching through it should be faster than
|
||||
//using the findFirstXXXX methods
|
||||
NameRecord record = getSpecificBuiltinRecord(name, sheetIndex);
|
||||
if (record != null) {
|
||||
names.remove(record);
|
||||
}
|
||||
|
||||
}
|
||||
public void removeBuiltinRecord(byte name, int sheetIndex) {
|
||||
linkTable.removeBuiltinRecord(name, sheetIndex);
|
||||
// TODO - do we need "this.records.remove(...);" similar to that in this.removeName(int namenum) {}?
|
||||
}
|
||||
|
||||
public int getNumRecords() {
|
||||
return records.size();
|
||||
@ -614,6 +592,7 @@ public class Workbook implements Model
|
||||
records.add(records.getBspos()+1, bsr);
|
||||
records.setBspos( records.getBspos() + 1 );
|
||||
boundsheets.add(bsr);
|
||||
getOrCreateLinkTable().checkExternSheet(sheetnum);
|
||||
fixTabIdRecord();
|
||||
}
|
||||
}
|
||||
@ -1825,13 +1804,25 @@ public class Workbook implements Model
|
||||
return new EOFRecord();
|
||||
}
|
||||
|
||||
/**
|
||||
* lazy initialization
|
||||
* Note - creating the link table causes creation of 1 EXTERNALBOOK and 1 EXTERNALSHEET record
|
||||
*/
|
||||
private LinkTable getOrCreateLinkTable() {
|
||||
if(linkTable == null) {
|
||||
linkTable = new LinkTable((short) getNumSheets(), records);
|
||||
}
|
||||
return linkTable;
|
||||
}
|
||||
|
||||
public SheetReferences getSheetReferences() {
|
||||
SheetReferences refs = new SheetReferences();
|
||||
|
||||
if (externSheet != null) {
|
||||
for (int k = 0; k < externSheet.getNumOfREFStructures(); k++) {
|
||||
if (linkTable != null) {
|
||||
int numRefStructures = linkTable.getNumberOfREFStructures();
|
||||
for (short k = 0; k < numRefStructures; k++) {
|
||||
|
||||
String sheetName = findSheetNameFromExternSheet((short)k);
|
||||
String sheetName = findSheetNameFromExternSheet(k);
|
||||
refs.addSheetReference(sheetName, k);
|
||||
|
||||
}
|
||||
@ -1846,7 +1837,8 @@ public class Workbook implements Model
|
||||
public String findSheetNameFromExternSheet(short num){
|
||||
String result="";
|
||||
|
||||
short indexToSheet = externSheet.getREFRecordAt(num).getIndexToFirstSupBook();
|
||||
short indexToSheet = linkTable.getIndexToSheet(num);
|
||||
|
||||
if (indexToSheet>-1) { //error check, bail out gracefully!
|
||||
result = getSheetName(indexToSheet);
|
||||
}
|
||||
@ -1861,10 +1853,7 @@ public class Workbook implements Model
|
||||
*/
|
||||
public int getSheetIndexFromExternSheetIndex(int externSheetNumber)
|
||||
{
|
||||
if (externSheetNumber >= externSheet.getNumOfREFStructures())
|
||||
return -1;
|
||||
else
|
||||
return externSheet.getREFRecordAt(externSheetNumber).getIndexToFirstSupBook();
|
||||
return linkTable.getSheetIndexFromExternSheetIndex(externSheetNumber);
|
||||
}
|
||||
|
||||
/** returns the extern sheet number for specific sheet number ,
|
||||
@ -1873,58 +1862,17 @@ public class Workbook implements Model
|
||||
* @return index to extern sheet
|
||||
*/
|
||||
public short checkExternSheet(int sheetNumber){
|
||||
|
||||
int i = 0;
|
||||
boolean flag = false;
|
||||
short result = 0;
|
||||
|
||||
if (externSheet == null) {
|
||||
externSheet = createExternSheet();
|
||||
}
|
||||
|
||||
//Trying to find reference to this sheet
|
||||
while (i < externSheet.getNumOfREFStructures() && !flag){
|
||||
ExternSheetSubRecord record = externSheet.getREFRecordAt(i);
|
||||
|
||||
if (record.getIndexToFirstSupBook() == sheetNumber &&
|
||||
record.getIndexToLastSupBook() == sheetNumber){
|
||||
flag = true;
|
||||
result = (short) i;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
//We Havent found reference to this sheet
|
||||
if (!flag) {
|
||||
result = addSheetIndexToExternSheet((short) sheetNumber);
|
||||
}
|
||||
|
||||
return result;
|
||||
return getOrCreateLinkTable().checkExternSheet(sheetNumber);
|
||||
}
|
||||
|
||||
private short addSheetIndexToExternSheet(short sheetNumber){
|
||||
short result;
|
||||
|
||||
ExternSheetSubRecord record = new ExternSheetSubRecord();
|
||||
record.setIndexToFirstSupBook(sheetNumber);
|
||||
record.setIndexToLastSupBook(sheetNumber);
|
||||
externSheet.addREFRecord(record);
|
||||
externSheet.setNumOfREFStructures((short)(externSheet.getNumOfREFStructures() + 1));
|
||||
result = (short)(externSheet.getNumOfREFStructures() - 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** gets the total number of names
|
||||
* @return number of names
|
||||
*/
|
||||
public int getNumNames(){
|
||||
int result = names.size();
|
||||
|
||||
return result;
|
||||
if(linkTable == null) {
|
||||
return 0;
|
||||
}
|
||||
return linkTable.getNumNames();
|
||||
}
|
||||
|
||||
/** gets the name record
|
||||
@ -1932,28 +1880,14 @@ public class Workbook implements Model
|
||||
* @return name record
|
||||
*/
|
||||
public NameRecord getNameRecord(int index){
|
||||
NameRecord result = (NameRecord) names.get(index);
|
||||
|
||||
return result;
|
||||
|
||||
return linkTable.getNameRecord(index);
|
||||
}
|
||||
|
||||
/** creates new name
|
||||
* @return new name record
|
||||
*/
|
||||
public NameRecord createName(){
|
||||
|
||||
NameRecord name = new NameRecord();
|
||||
|
||||
// Not the most efficient way but the other way was causing too many bugs
|
||||
int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
|
||||
if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid);
|
||||
if (idx == -1) idx = findFirstRecordLocBySid(CountryRecord.sid);
|
||||
|
||||
records.add(idx+names.size()+1, name);
|
||||
names.add(name);
|
||||
|
||||
return name;
|
||||
return addName(new NameRecord());
|
||||
}
|
||||
|
||||
|
||||
@ -1962,67 +1896,41 @@ public class Workbook implements Model
|
||||
*/
|
||||
public NameRecord addName(NameRecord name)
|
||||
{
|
||||
// Not the most efficient way but the other way was causing too many bugs
|
||||
int idx = findFirstRecordLocBySid(ExternSheetRecord.sid);
|
||||
if (idx == -1) idx = findFirstRecordLocBySid(SupBookRecord.sid);
|
||||
if (idx == -1) idx = findFirstRecordLocBySid(CountryRecord.sid);
|
||||
records.add(idx+names.size()+1, name);
|
||||
names.add(name);
|
||||
|
||||
getOrCreateLinkTable().addName(name);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**Generates a NameRecord to represent a built-in region
|
||||
* @return a new NameRecord unless the index is invalid
|
||||
*/
|
||||
public NameRecord createBuiltInName(byte builtInName, int index)
|
||||
{
|
||||
if (index == -1 || index+1 > (int)Short.MAX_VALUE)
|
||||
throw new IllegalArgumentException("Index is not valid ["+index+"]");
|
||||
/**Generates a NameRecord to represent a built-in region
|
||||
* @return a new NameRecord unless the index is invalid
|
||||
*/
|
||||
public NameRecord createBuiltInName(byte builtInName, int index)
|
||||
{
|
||||
if (index == -1 || index+1 > Short.MAX_VALUE)
|
||||
throw new IllegalArgumentException("Index is not valid ["+index+"]");
|
||||
|
||||
NameRecord name = new NameRecord(builtInName, (short)(index));
|
||||
NameRecord name = new NameRecord(builtInName, (short)(index));
|
||||
|
||||
addName(name);
|
||||
addName(name);
|
||||
|
||||
return name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/** removes the name
|
||||
* @param namenum name index
|
||||
*/
|
||||
public void removeName(int namenum){
|
||||
if (names.size() > namenum) {
|
||||
|
||||
if (linkTable.getNumNames() > namenum) {
|
||||
int idx = findFirstRecordLocBySid(NameRecord.sid);
|
||||
records.remove(idx + namenum);
|
||||
names.remove(namenum);
|
||||
linkTable.removeName(namenum);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** creates a new extern sheet record
|
||||
* @return the new extern sheet record
|
||||
*/
|
||||
protected ExternSheetRecord createExternSheet(){
|
||||
ExternSheetRecord externSheet = new ExternSheetRecord();
|
||||
|
||||
int idx = findFirstRecordLocBySid(CountryRecord.sid);
|
||||
|
||||
records.add(idx+1, externSheet);
|
||||
// records.add(records.supbookpos + 1 , rec);
|
||||
|
||||
//We also adds the supBook for internal reference
|
||||
SupBookRecord supbook = new SupBookRecord();
|
||||
|
||||
supbook.setNumberOfSheets((short)getNumSheets());
|
||||
//supbook.setFlag();
|
||||
|
||||
records.add(idx+1, supbook);
|
||||
// records.add(records.supbookpos + 1 , supbook);
|
||||
|
||||
return externSheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a format index that matches the passed in format. It does not tie into HSSFDataFormat.
|
||||
* @param format the format string
|
||||
@ -2419,5 +2327,14 @@ public class Workbook implements Model
|
||||
writeProtect = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
|
||||
* @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
|
||||
* @return the string representation of the defined or external name
|
||||
*/
|
||||
public String resolveNameXText(int refIndex, int definedNameIndex) {
|
||||
return linkTable.resolveNameXText(refIndex, definedNameIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
94
src/java/org/apache/poi/hssf/record/CRNCountRecord.java
Executable file
94
src/java/org/apache/poi/hssf/record/CRNCountRecord.java
Executable 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;
|
||||
}
|
||||
}
|
99
src/java/org/apache/poi/hssf/record/CRNRecord.java
Executable file
99
src/java/org/apache/poi/hssf/record/CRNRecord.java
Executable 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;
|
||||
}
|
||||
}
|
179
src/java/org/apache/poi/hssf/record/ExternalNameRecord.java
Executable file
179
src/java/org/apache/poi/hssf/record/ExternalNameRecord.java
Executable 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();
|
||||
}
|
||||
}
|
@ -77,7 +77,11 @@ public class RecordFactory
|
||||
NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class,
|
||||
FileSharingRecord.class, ChartTitleFormatRecord.class,
|
||||
DVRecord.class, DVALRecord.class, UncalcedRecord.class,
|
||||
HyperlinkRecord.class
|
||||
HyperlinkRecord.class,
|
||||
ExternalNameRecord.class, // TODO - same changes in non-@deprecated version of this class
|
||||
SupBookRecord.class,
|
||||
CRNCountRecord.class,
|
||||
CRNRecord.class,
|
||||
};
|
||||
}
|
||||
private static Map recordsMap = recordsToMap(records);
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
@ -16,71 +15,158 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
import org.apache.poi.hssf.record.UnicodeString.UnicodeRecordStats;
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
* Title: Sup Book <P>
|
||||
* Description: A Extrenal Workbook Deciption (Sup Book)
|
||||
* Title: Sup Book (EXTERNALBOOK) <P>
|
||||
* Description: A External Workbook Description (Suplemental Book)
|
||||
* Its only a dummy record for making new ExternSheet Record <P>
|
||||
* REFERENCE: <P>
|
||||
* REFERENCE: 5.38<P>
|
||||
* @author Libin Roman (Vista Portal LDT. Developer)
|
||||
* @author Andrew C. Oliver (acoliver@apache.org)
|
||||
*
|
||||
*/
|
||||
public class SupBookRecord extends Record
|
||||
{
|
||||
public final class SupBookRecord extends Record {
|
||||
|
||||
public final static short sid = 0x1AE;
|
||||
|
||||
private static final short SMALL_RECORD_SIZE = 4;
|
||||
private static final short TAG_INTERNAL_REFERENCES = 0x0401;
|
||||
private static final short TAG_ADD_IN_FUNCTIONS = 0x3A01;
|
||||
|
||||
private short field_1_number_of_sheets;
|
||||
private short field_2_flag;
|
||||
private UnicodeString field_2_encoded_url;
|
||||
private UnicodeString[] field_3_sheet_names;
|
||||
private boolean _isAddInFunctions;
|
||||
|
||||
|
||||
public SupBookRecord()
|
||||
{
|
||||
setFlag((short)0x401);
|
||||
public static SupBookRecord createInternalReferences(short numberOfSheets) {
|
||||
return new SupBookRecord(false, numberOfSheets);
|
||||
}
|
||||
public static SupBookRecord createAddInFunctions() {
|
||||
return new SupBookRecord(true, (short)0);
|
||||
}
|
||||
public static SupBookRecord createExternalReferences(UnicodeString url, UnicodeString[] sheetNames) {
|
||||
return new SupBookRecord(url, sheetNames);
|
||||
}
|
||||
private SupBookRecord(boolean isAddInFuncs, short numberOfSheets) {
|
||||
// else not 'External References'
|
||||
field_1_number_of_sheets = numberOfSheets;
|
||||
field_2_encoded_url = null;
|
||||
field_3_sheet_names = null;
|
||||
_isAddInFunctions = isAddInFuncs;
|
||||
}
|
||||
public SupBookRecord(UnicodeString url, UnicodeString[] sheetNames) {
|
||||
field_1_number_of_sheets = (short) sheetNames.length;
|
||||
field_2_encoded_url = url;
|
||||
field_3_sheet_names = sheetNames;
|
||||
_isAddInFunctions = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Extern Sheet record and sets its fields appropriately.
|
||||
*
|
||||
* @param in the RecordInputstream to read the record from
|
||||
* @param id id must be 0x16 or an exception will be throw upon validation
|
||||
* @param size the size of the data area of the record
|
||||
* @param data data of the record (should not contain sid/len)
|
||||
*/
|
||||
public SupBookRecord(RecordInputStream in)
|
||||
{
|
||||
public SupBookRecord(RecordInputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
protected void validateSid(short id)
|
||||
{
|
||||
if (id != sid)
|
||||
{
|
||||
throw new RecordFormatException("NOT An Supbook RECORD");
|
||||
protected void validateSid(short id) {
|
||||
if (id != sid) {
|
||||
throw new RecordFormatException("NOT An ExternSheet RECORD");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in the RecordInputstream to read the record from
|
||||
*/
|
||||
protected void fillFields(RecordInputStream in)
|
||||
{
|
||||
//For now We use it only for one case
|
||||
//When we need to add an named range when no named ranges was
|
||||
//before it
|
||||
field_1_number_of_sheets = in.readShort();
|
||||
field_2_flag = in.readShort();
|
||||
public boolean isExternalReferences() {
|
||||
return field_3_sheet_names != null;
|
||||
}
|
||||
public boolean isInternalReferences() {
|
||||
return field_3_sheet_names == null && !_isAddInFunctions;
|
||||
}
|
||||
public boolean isAddInFunctions() {
|
||||
return field_3_sheet_names == null && _isAddInFunctions;
|
||||
}
|
||||
/**
|
||||
* called by the constructor, should set class level fields. Should throw
|
||||
* runtime exception for bad/incomplete data.
|
||||
*
|
||||
* @param data raw data
|
||||
* @param size size of data
|
||||
* @param offset of the record's data (provided a big array of the file)
|
||||
*/
|
||||
protected void fillFields(RecordInputStream in) {
|
||||
field_1_number_of_sheets = in.readShort();
|
||||
|
||||
if(in.getLength() > SMALL_RECORD_SIZE) {
|
||||
// 5.38.1 External References
|
||||
_isAddInFunctions = false;
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append("[SUPBOOK]\n");
|
||||
buffer.append("numberosheets = ").append(getNumberOfSheets()).append('\n');
|
||||
buffer.append("flag = ").append(getFlag()).append('\n');
|
||||
buffer.append("[/SUPBOOK]\n");
|
||||
return buffer.toString();
|
||||
field_2_encoded_url = in.readUnicodeString();
|
||||
UnicodeString[] sheetNames = new UnicodeString[field_1_number_of_sheets];
|
||||
for (int i = 0; i < sheetNames.length; i++) {
|
||||
sheetNames[i] = in.readUnicodeString();
|
||||
}
|
||||
field_3_sheet_names = sheetNames;
|
||||
return;
|
||||
}
|
||||
// else not 'External References'
|
||||
field_2_encoded_url = null;
|
||||
field_3_sheet_names = null;
|
||||
|
||||
short nextShort = in.readShort();
|
||||
if(nextShort == TAG_INTERNAL_REFERENCES) {
|
||||
// 5.38.2 'Internal References'
|
||||
_isAddInFunctions = false;
|
||||
} else if(nextShort == TAG_ADD_IN_FUNCTIONS) {
|
||||
// 5.38.3 'Add-In Functions'
|
||||
_isAddInFunctions = true;
|
||||
if(field_1_number_of_sheets != 1) {
|
||||
throw new RuntimeException("Expected 0x0001 for number of sheets field in 'Add-In Functions' but got ("
|
||||
+ field_1_number_of_sheets + ")");
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("invalid EXTERNALBOOK code ("
|
||||
+ Integer.toHexString(nextShort) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(getClass().getName()).append(" [SUPBOOK ");
|
||||
|
||||
if(isExternalReferences()) {
|
||||
sb.append("External References");
|
||||
sb.append(" nSheets=").append(field_1_number_of_sheets);
|
||||
sb.append(" url=").append(field_2_encoded_url);
|
||||
} else if(_isAddInFunctions) {
|
||||
sb.append("Add-In Functions");
|
||||
} else {
|
||||
sb.append("Internal References ");
|
||||
sb.append(" nSheets= ").append(field_1_number_of_sheets);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
private int getDataSize() {
|
||||
if(!isExternalReferences()) {
|
||||
return SMALL_RECORD_SIZE;
|
||||
}
|
||||
int sum = 2; // u16 number of sheets
|
||||
UnicodeRecordStats urs = new UnicodeRecordStats();
|
||||
field_2_encoded_url.getRecordSize(urs);
|
||||
sum += urs.recordSize;
|
||||
|
||||
for(int i=0; i<field_3_sheet_names.length; i++) {
|
||||
urs = new UnicodeRecordStats();
|
||||
field_3_sheet_names[i].getRecordSize(urs);
|
||||
sum += urs.recordSize;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,14 +178,30 @@ public class SupBookRecord extends Record
|
||||
* @param data byte array containing instance data
|
||||
* @return number of bytes written
|
||||
*/
|
||||
public int serialize(int offset, byte [] data)
|
||||
{
|
||||
public int serialize(int offset, byte [] data) {
|
||||
LittleEndian.putShort(data, 0 + offset, sid);
|
||||
LittleEndian.putShort(data, 2 + offset, (short) 4);
|
||||
int dataSize = getDataSize();
|
||||
LittleEndian.putShort(data, 2 + offset, (short) dataSize);
|
||||
LittleEndian.putShort(data, 4 + offset, field_1_number_of_sheets);
|
||||
LittleEndian.putShort(data, 6 + offset, field_2_flag);
|
||||
|
||||
return getRecordSize();
|
||||
if(isExternalReferences()) {
|
||||
|
||||
int currentOffset = 6 + offset;
|
||||
UnicodeRecordStats urs = new UnicodeRecordStats();
|
||||
field_2_encoded_url.serialize(urs, currentOffset, data);
|
||||
currentOffset += urs.recordSize;
|
||||
|
||||
for(int i=0; i<field_3_sheet_names.length; i++) {
|
||||
urs = new UnicodeRecordStats();
|
||||
field_3_sheet_names[i].serialize(urs, currentOffset, data);
|
||||
currentOffset += urs.recordSize;
|
||||
}
|
||||
} else {
|
||||
short field2val = _isAddInFunctions ? TAG_ADD_IN_FUNCTIONS : TAG_INTERNAL_REFERENCES;
|
||||
|
||||
LittleEndian.putShort(data, 6 + offset, field2val);
|
||||
}
|
||||
return dataSize + 4;
|
||||
}
|
||||
|
||||
public void setNumberOfSheets(short number){
|
||||
@ -110,21 +212,18 @@ public class SupBookRecord extends Record
|
||||
return field_1_number_of_sheets;
|
||||
}
|
||||
|
||||
public void setFlag(short flag){
|
||||
field_2_flag = flag;
|
||||
}
|
||||
|
||||
public short getFlag() {
|
||||
return field_2_flag;
|
||||
}
|
||||
|
||||
public int getRecordSize()
|
||||
{
|
||||
return 4 + 4;
|
||||
public int getRecordSize() {
|
||||
return getDataSize() + 4;
|
||||
}
|
||||
|
||||
public short getSid()
|
||||
{
|
||||
return sid;
|
||||
}
|
||||
public UnicodeString getURL() {
|
||||
return field_2_encoded_url;
|
||||
}
|
||||
public UnicodeString[] getSheetNames() {
|
||||
return (UnicodeString[]) field_3_sheet_names.clone();
|
||||
}
|
||||
}
|
||||
|
111
src/java/org/apache/poi/hssf/record/constant/ConstantValueParser.java
Executable file
111
src/java/org/apache/poi/hssf/record/constant/ConstantValueParser.java
Executable 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;
|
||||
}
|
||||
}
|
@ -29,10 +29,13 @@ import org.apache.poi.hssf.model.Workbook;
|
||||
* @author Andrew C. Oliver (acoliver at apache dot org)
|
||||
*/
|
||||
public abstract class AbstractFunctionPtg extends OperationPtg {
|
||||
//constant used allow a ptgAttr to be mapped properly for its functionPtg
|
||||
public static final String ATTR_NAME = "specialflag";
|
||||
|
||||
public static final short INDEX_EXTERNAL = 255;
|
||||
/**
|
||||
* The name of the IF function (i.e. "IF"). Extracted as a constant for clarity.
|
||||
*/
|
||||
public static final String FUNCTION_NAME_IF = "IF";
|
||||
/** All external functions have function index 255 */
|
||||
private static final short FUNCTION_INDEX_EXTERNAL = 255;
|
||||
|
||||
private static BinaryTree map = produceHash();
|
||||
protected static Object[][] functionData = produceFunctionData();
|
||||
@ -66,6 +69,13 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
|
||||
public String getName() {
|
||||
return lookupName(field_2_fnc_index);
|
||||
}
|
||||
/**
|
||||
* external functions get some special processing
|
||||
* @return <code>true</code> if this is an external function
|
||||
*/
|
||||
public boolean isExternalFunction() {
|
||||
return field_2_fnc_index == FUNCTION_INDEX_EXTERNAL;
|
||||
}
|
||||
|
||||
public String toFormulaString(Workbook book) {
|
||||
return getName();
|
||||
@ -74,38 +84,56 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
|
||||
public String toFormulaString(String[] operands) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
if (field_2_fnc_index != 1) {
|
||||
buf.append(getName());
|
||||
buf.append('(');
|
||||
}
|
||||
if (operands.length >0) {
|
||||
for (int i=0;i<operands.length;i++) {
|
||||
buf.append(operands[i]);
|
||||
buf.append(',');
|
||||
}
|
||||
buf.deleteCharAt(buf.length()-1);
|
||||
}
|
||||
if (field_2_fnc_index != 1) {
|
||||
buf.append(")");
|
||||
}
|
||||
if(isExternalFunction()) {
|
||||
buf.append(operands[0]); // first operand is actually the function name
|
||||
appendArgs(buf, 1, operands);
|
||||
} else {
|
||||
buf.append(getName());
|
||||
appendArgs(buf, 0, operands);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static void appendArgs(StringBuffer buf, int firstArgIx, String[] operands) {
|
||||
buf.append('(');
|
||||
for (int i=firstArgIx;i<operands.length;i++) {
|
||||
if (i>firstArgIx) {
|
||||
buf.append(',');
|
||||
}
|
||||
buf.append(operands[i]);
|
||||
}
|
||||
buf.append(")");
|
||||
}
|
||||
|
||||
public abstract void writeBytes(byte[] array, int offset);
|
||||
public abstract int getSize();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Used to detect whether a function name found in a formula is one of the standard excel functions
|
||||
* <p>
|
||||
* The name matching is case insensitive.
|
||||
* @return <code>true</code> if the name specifies a standard worksheet function,
|
||||
* <code>false</code> if the name should be assumed to be an external function.
|
||||
*/
|
||||
public static final boolean isInternalFunctionName(String name) {
|
||||
return map.containsValue(name.toUpperCase());
|
||||
}
|
||||
|
||||
protected String lookupName(short index) {
|
||||
return ((String)map.get(new Integer(index)));
|
||||
}
|
||||
|
||||
protected short lookupIndex(String name) {
|
||||
Integer index = (Integer) map.getKeyForValue(name);
|
||||
/**
|
||||
* Resolves internal function names into function indexes.
|
||||
* <p>
|
||||
* The name matching is case insensitive.
|
||||
* @return the standard worksheet function index if found, otherwise <tt>FUNCTION_INDEX_EXTERNAL</tt>
|
||||
*/
|
||||
protected static short lookupIndex(String name) {
|
||||
Integer index = (Integer) map.getKeyForValue(name.toUpperCase());
|
||||
if (index != null) return index.shortValue();
|
||||
return INDEX_EXTERNAL;
|
||||
return FUNCTION_INDEX_EXTERNAL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,7 +143,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
|
||||
BinaryTree dmap = new BinaryTree();
|
||||
|
||||
dmap.put(new Integer(0),"COUNT");
|
||||
dmap.put(new Integer(1),"specialflag");
|
||||
dmap.put(new Integer(1),FUNCTION_NAME_IF);
|
||||
dmap.put(new Integer(2),"ISNA");
|
||||
dmap.put(new Integer(3),"ISERROR");
|
||||
dmap.put(new Integer(4),"SUM");
|
||||
@ -354,7 +382,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
|
||||
dmap.put(new Integer(252),"FREQUENCY");
|
||||
dmap.put(new Integer(253),"ADDTOOLBAR");
|
||||
dmap.put(new Integer(254),"DELETETOOLBAR");
|
||||
dmap.put(new Integer(255),"externalflag");
|
||||
dmap.put(new Integer(FUNCTION_INDEX_EXTERNAL),"externalflag");
|
||||
dmap.put(new Integer(256),"RESETTOOLBAR");
|
||||
dmap.put(new Integer(257),"EVALUATE");
|
||||
dmap.put(new Integer(258),"GETTOOLBAR");
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
@ -16,12 +15,6 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
* IntPtg.java
|
||||
*
|
||||
* Created on October 29, 2001, 7:37 PM
|
||||
*/
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
@ -29,64 +22,45 @@ import org.apache.poi.hssf.model.Workbook;
|
||||
import org.apache.poi.hssf.record.RecordInputStream;
|
||||
|
||||
/**
|
||||
* Integer (unsigned short intger)
|
||||
* Integer (unsigned short integer)
|
||||
* Stores an unsigned short value (java int) in a formula
|
||||
* @author Andrew C. Oliver (acoliver at apache dot org)
|
||||
* @author Jason Height (jheight at chariot dot net dot au)
|
||||
*/
|
||||
public final class IntPtg extends Ptg {
|
||||
// 16 bit unsigned integer
|
||||
private static final int MIN_VALUE = 0x0000;
|
||||
private static final int MAX_VALUE = 0xFFFF;
|
||||
|
||||
/**
|
||||
* Excel represents integers 0..65535 with the tInt token.
|
||||
* @return <code>true</code> if the specified value is within the range of values
|
||||
* <tt>IntPtg</tt> can represent.
|
||||
*/
|
||||
public static boolean isInRange(int i) {
|
||||
return i>=MIN_VALUE && i <=MAX_VALUE;
|
||||
}
|
||||
|
||||
public class IntPtg
|
||||
extends Ptg
|
||||
{
|
||||
public final static int SIZE = 3;
|
||||
public final static byte sid = 0x1e;
|
||||
private int field_1_value;
|
||||
|
||||
private IntPtg() {
|
||||
//Required for clone methods
|
||||
}
|
||||
|
||||
public IntPtg(RecordInputStream in)
|
||||
{
|
||||
setValue(in.readUShort());
|
||||
public IntPtg(RecordInputStream in) {
|
||||
this(in.readUShort());
|
||||
}
|
||||
|
||||
|
||||
// IntPtg should be able to create itself, shouldnt have to call setValue
|
||||
public IntPtg(String formulaToken) {
|
||||
setValue(Integer.parseInt(formulaToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the wrapped value.
|
||||
* Normally you should call with a positive int.
|
||||
*/
|
||||
public void setValue(int value)
|
||||
{
|
||||
if(value < 0 || value > (Short.MAX_VALUE + 1)*2 )
|
||||
throw new IllegalArgumentException("Unsigned short is out of range: " + value);
|
||||
public IntPtg(int value) {
|
||||
if(!isInRange(value)) {
|
||||
throw new IllegalArgumentException("value is out of range: " + value);
|
||||
}
|
||||
field_1_value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value as a short, which may have
|
||||
* been wrapped into negative numbers
|
||||
*/
|
||||
public int getValue()
|
||||
{
|
||||
public int getValue() {
|
||||
return field_1_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value as an unsigned positive int.
|
||||
*/
|
||||
public int getValueAsInt()
|
||||
{
|
||||
if(field_1_value < 0) {
|
||||
return (Short.MAX_VALUE + 1)*2 + field_1_value;
|
||||
}
|
||||
return field_1_value;
|
||||
}
|
||||
|
||||
public void writeBytes(byte [] array, int offset)
|
||||
{
|
||||
@ -94,20 +68,25 @@ public class IntPtg
|
||||
LittleEndian.putUShort(array, offset + 1, getValue());
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
public int getSize() {
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
public String toFormulaString(Workbook book)
|
||||
{
|
||||
return "" + getValue();
|
||||
public String toFormulaString(Workbook book) {
|
||||
return String.valueOf(getValue());
|
||||
}
|
||||
public byte getDefaultOperandClass() {
|
||||
return Ptg.CLASS_VALUE;
|
||||
}
|
||||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
|
||||
|
||||
public Object clone() {
|
||||
IntPtg ptg = new IntPtg();
|
||||
ptg.field_1_value = field_1_value;
|
||||
return ptg;
|
||||
}
|
||||
public Object clone() {
|
||||
return new IntPtg(field_1_value);
|
||||
}
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(field_1_value);
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ public class NamePtg
|
||||
{
|
||||
public final static short sid = 0x23;
|
||||
private final static int SIZE = 5;
|
||||
/** one-based index to defined name record */
|
||||
private short field_1_label_index;
|
||||
private short field_2_zero; // reserved must be 0
|
||||
boolean xtra=false;
|
||||
@ -42,24 +43,32 @@ public class NamePtg
|
||||
//Required for clone methods
|
||||
}
|
||||
|
||||
/** Creates new NamePtg */
|
||||
|
||||
public NamePtg(String name, Workbook book)
|
||||
{
|
||||
final short n = (short) (book.getNumNames() + 1);
|
||||
/**
|
||||
* Creates new NamePtg and sets its name index to that of the corresponding defined name record
|
||||
* in the workbook. The search for the name record is case insensitive. If it is not found,
|
||||
* it gets created.
|
||||
*/
|
||||
public NamePtg(String name, Workbook book) {
|
||||
field_1_label_index = (short)(1+getOrCreateNameRecord(book, name)); // convert to 1-based
|
||||
}
|
||||
/**
|
||||
* @return zero based index of the found or newly created defined name record.
|
||||
*/
|
||||
private static final int getOrCreateNameRecord(Workbook book, String name) {
|
||||
// perhaps this logic belongs in Workbook
|
||||
int countNames = book.getNumNames();
|
||||
NameRecord rec;
|
||||
for (short i = 1; i < n; i++) {
|
||||
rec = book.getNameRecord(i - 1);
|
||||
for (int i = 0; i < countNames; i++) {
|
||||
rec = book.getNameRecord(i);
|
||||
if (name.equalsIgnoreCase(rec.getNameText())) {
|
||||
field_1_label_index = i;
|
||||
return;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
rec = new NameRecord();
|
||||
rec.setNameText(name);
|
||||
rec.setNameTextLength((byte) name.length());
|
||||
book.addName(rec);
|
||||
field_1_label_index = n;
|
||||
return countNames;
|
||||
}
|
||||
|
||||
/** Creates new NamePtg */
|
||||
@ -72,6 +81,13 @@ public class NamePtg
|
||||
//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)
|
||||
{
|
||||
array[offset+0]= (byte) (sid + ptgClass);
|
||||
|
@ -25,13 +25,11 @@ import org.apache.poi.hssf.record.RecordInputStream;
|
||||
*
|
||||
* @author aviks
|
||||
*/
|
||||
|
||||
public class NameXPtg extends Ptg
|
||||
{
|
||||
public final class NameXPtg extends Ptg {
|
||||
public final static short sid = 0x39;
|
||||
private final static int SIZE = 7;
|
||||
private short field_1_ixals; // index to externsheet record
|
||||
private short field_2_ilbl; //index to name or externname table(1 based)
|
||||
private short field_1_ixals; // index to REF entry in externsheet record
|
||||
private short field_2_ilbl; //index to defined name or externname table(1 based)
|
||||
private short field_3_reserved; // reserved must be 0
|
||||
|
||||
|
||||
@ -41,13 +39,6 @@ public class NameXPtg extends Ptg
|
||||
|
||||
/** Creates new NamePtg */
|
||||
|
||||
public NameXPtg(String name)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
||||
/** Creates new NamePtg */
|
||||
|
||||
public NameXPtg(RecordInputStream in)
|
||||
{
|
||||
field_1_ixals = in.readShort();
|
||||
@ -72,7 +63,8 @@ public class NameXPtg extends Ptg
|
||||
|
||||
public String toFormulaString(Workbook book)
|
||||
{
|
||||
return "NO IDEA - NAME";
|
||||
// -1 to convert definedNameIndex from 1-based to zero-based
|
||||
return book.resolveNameXText(field_1_ixals, field_2_ilbl-1);
|
||||
}
|
||||
|
||||
public byte getDefaultOperandClass() {return Ptg.CLASS_VALUE;}
|
||||
|
@ -639,7 +639,7 @@ public class HSSFCell
|
||||
|
||||
//only set to default if there is no extended format index already set
|
||||
if (rec.getXFIndex() == (short)0) rec.setXFIndex(( short ) 0x0f);
|
||||
FormulaParser fp = new FormulaParser(formula+";",book);
|
||||
FormulaParser fp = new FormulaParser(formula, book);
|
||||
fp.parse();
|
||||
Ptg[] ptg = fp.getRPNPtg();
|
||||
int size = 0;
|
||||
|
@ -400,7 +400,7 @@ public class HSSFSheet
|
||||
|
||||
//formula fields ( size and data )
|
||||
String str_formula = obj_validation.getFirstFormula();
|
||||
FormulaParser fp = new FormulaParser(str_formula+";",book);
|
||||
FormulaParser fp = new FormulaParser(str_formula, book);
|
||||
fp.parse();
|
||||
Stack ptg_arr = new Stack();
|
||||
Ptg[] ptg = fp.getRPNPtg();
|
||||
@ -424,7 +424,7 @@ public class HSSFSheet
|
||||
if ( obj_validation.getSecondFormula() != null )
|
||||
{
|
||||
str_formula = obj_validation.getSecondFormula();
|
||||
fp = new FormulaParser(str_formula+";",book);
|
||||
fp = new FormulaParser(str_formula, book);
|
||||
fp.parse();
|
||||
ptg_arr = new Stack();
|
||||
ptg = fp.getRPNPtg();
|
||||
|
@ -245,6 +245,16 @@ public class LittleEndian
|
||||
putNumber(data, offset, value, SHORT_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* executes:<p/>
|
||||
* <code>
|
||||
* data[offset] = (byte)value;
|
||||
* </code></p>
|
||||
* Added for consistency with other put~() methods
|
||||
*/
|
||||
public static void putByte(byte[] data, int offset, int value) {
|
||||
putNumber(data, offset, value, LittleEndianConsts.BYTE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* put a array of shorts into a byte array
|
||||
|
@ -43,8 +43,6 @@ public class AddEval extends NumericOperationEval {
|
||||
private static final ValueEvalToNumericXlator NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short)
|
||||
( ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
@ -59,33 +57,31 @@ public class AddEval extends NumericOperationEval {
|
||||
}
|
||||
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
Eval retval = null;
|
||||
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
if(args.length != 2) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
double d = 0;
|
||||
switch (operands.length) {
|
||||
default: // will rarely happen. currently the parser itself fails.
|
||||
retval = ErrorEval.UNKNOWN_ERROR;
|
||||
break;
|
||||
case 2:
|
||||
for (int i = 0, iSize = 2; retval==null && i < iSize; i++) {
|
||||
ValueEval ve = singleOperandEvaluate(operands[i], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d += ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
} // end for inside case
|
||||
} // end switch
|
||||
|
||||
if (retval == null) {
|
||||
retval = Double.isNaN(d) ? (ValueEval) ErrorEval.VALUE_INVALID : new NumberEval(d);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ValueEval ve = singleOperandEvaluate(args[i], srcRow, srcCol);
|
||||
if(ve instanceof ErrorEval) {
|
||||
return ve;
|
||||
}
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d += ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
if(Double.isNaN(d) || Double.isInfinite(d)) {
|
||||
return ErrorEval.NUM_ERROR;
|
||||
}
|
||||
return new NumberEval(d);
|
||||
}
|
||||
|
||||
public int getNumberOfOperands() {
|
||||
|
@ -14,10 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 8, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.ConcatPtg;
|
||||
@ -27,7 +24,7 @@ import org.apache.poi.hssf.record.formula.Ptg;
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class ConcatEval extends StringOperationEval {
|
||||
public final class ConcatEval extends StringOperationEval {
|
||||
|
||||
private ConcatPtg delegate;
|
||||
|
||||
@ -35,36 +32,27 @@ public class ConcatEval extends StringOperationEval {
|
||||
this.delegate = (ConcatPtg) ptg;
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
Eval retval = null;
|
||||
StringBuffer sb = null;
|
||||
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
if(args.length != 2) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
||||
switch (operands.length) {
|
||||
default: // paranoid check :)
|
||||
retval = ErrorEval.UNKNOWN_ERROR;
|
||||
break;
|
||||
case 2:
|
||||
sb = new StringBuffer();
|
||||
for (int i = 0, iSize = 2; retval == null && i < iSize; i++) {
|
||||
|
||||
ValueEval ve = singleOperandEvaluate(operands[i], srcRow, srcCol);
|
||||
if (ve instanceof StringValueEval) {
|
||||
StringValueEval sve = (StringValueEval) ve;
|
||||
sb.append(sve.getStringValue());
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else { // must be an error eval
|
||||
retval = ve;
|
||||
}
|
||||
ValueEval ve = singleOperandEvaluate(args[i], srcRow, srcCol);
|
||||
if (ve instanceof StringValueEval) {
|
||||
StringValueEval sve = (StringValueEval) ve;
|
||||
sb.append(sve.getStringValue());
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else { // must be an error eval
|
||||
return ve;
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == null) {
|
||||
retval = new StringEval(sb.toString());
|
||||
}
|
||||
return retval;
|
||||
return new StringEval(sb.toString());
|
||||
}
|
||||
|
||||
public int getNumberOfOperands() {
|
||||
|
@ -14,10 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 8, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
@ -27,15 +24,13 @@ import org.apache.poi.hssf.record.formula.DividePtg;
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class DivideEval extends NumericOperationEval {
|
||||
public final class DivideEval extends NumericOperationEval {
|
||||
|
||||
private DividePtg delegate;
|
||||
|
||||
private static final ValueEvalToNumericXlator NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short)
|
||||
( ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
@ -49,18 +44,28 @@ public class DivideEval extends NumericOperationEval {
|
||||
return NUM_XLATOR;
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
if(args.length != 2) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
Eval retval = null;
|
||||
double d0 = 0;
|
||||
double d1 = 0;
|
||||
switch (operands.length) {
|
||||
default: // will rarely happen. currently the parser itself fails.
|
||||
retval = ErrorEval.UNKNOWN_ERROR;
|
||||
break;
|
||||
case 2:
|
||||
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
|
||||
ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d0 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (retval == null) { // no error yet
|
||||
ve = singleOperandEvaluate(args[1], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d0 = ((NumericValueEval) ve).getNumberValue();
|
||||
d1 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
@ -68,20 +73,7 @@ public class DivideEval extends NumericOperationEval {
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (retval == null) { // no error yet
|
||||
ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d1 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
} // end switch
|
||||
}
|
||||
|
||||
if (retval == null) {
|
||||
retval = (d1 == 0)
|
||||
|
@ -48,12 +48,6 @@ public final class ErrorEval implements ValueEval {
|
||||
private static final int CIRCULAR_REF_ERROR_CODE = 0xFFFFFFC4;
|
||||
private static final int FUNCTION_NOT_IMPLEMENTED_CODE = 0xFFFFFFE2;
|
||||
|
||||
/**
|
||||
* @deprecated do not use this error code. For conditions that should never occur, throw an
|
||||
* unchecked exception. For all other situations use the error code that corresponds to the
|
||||
* error Excel would have raised under the same circumstances.
|
||||
*/
|
||||
public static final ErrorEval UNKNOWN_ERROR = new ErrorEval(-20);
|
||||
public static final ErrorEval FUNCTION_NOT_IMPLEMENTED = new ErrorEval(FUNCTION_NOT_IMPLEMENTED_CODE);
|
||||
// Note - Excel does not seem to represent this condition with an error code
|
||||
public static final ErrorEval CIRCULAR_REF_ERROR = new ErrorEval(CIRCULAR_REF_ERROR_CODE);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -38,7 +38,8 @@ public abstract class FunctionEval implements OperationEval {
|
||||
public static final int OFFSET = 78;
|
||||
/** 148 */
|
||||
public static final int INDIRECT = 148;
|
||||
|
||||
/** 255 */
|
||||
public static final int EXTERNAL_FUNC = 255;
|
||||
}
|
||||
// convenient access to namespace
|
||||
private static final FunctionID ID = null;
|
||||
@ -51,6 +52,7 @@ public abstract class FunctionEval implements OperationEval {
|
||||
Map m = new HashMap();
|
||||
addMapping(m, ID.OFFSET, new Offset());
|
||||
addMapping(m, ID.INDIRECT, new Indirect());
|
||||
addMapping(m, ID.EXTERNAL_FUNC, new ExternalFunction());
|
||||
freeRefFunctionsByIdMap = m;
|
||||
}
|
||||
private static void addMapping(Map m, int offset, FreeRefFunction frf) {
|
||||
@ -316,7 +318,7 @@ public abstract class FunctionEval implements OperationEval {
|
||||
retval[252] = new Frequency(); // FREQUENCY
|
||||
retval[253] = new NotImplementedFunction(); // ADDTOOLBAR
|
||||
retval[254] = new NotImplementedFunction(); // DELETETOOLBAR
|
||||
retval[255] = new NotImplementedFunction(); // EXTERNALFLAG
|
||||
retval[ID.EXTERNAL_FUNC] = null; // ExternalFunction is a FreeREfFunction
|
||||
retval[256] = new NotImplementedFunction(); // RESETTOOLBAR
|
||||
retval[257] = new Evaluate(); // EVALUATE
|
||||
retval[258] = new NotImplementedFunction(); // GETTOOLBAR
|
||||
|
@ -14,10 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 8, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
@ -27,15 +24,13 @@ import org.apache.poi.hssf.record.formula.MultiplyPtg;
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class MultiplyEval extends NumericOperationEval {
|
||||
public final class MultiplyEval extends NumericOperationEval {
|
||||
|
||||
private MultiplyPtg delegate;
|
||||
|
||||
private static final ValueEvalToNumericXlator NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short)
|
||||
( ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
@ -49,46 +44,39 @@ public class MultiplyEval extends NumericOperationEval {
|
||||
return NUM_XLATOR;
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
Eval retval = null;
|
||||
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
if(args.length != 2) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
double d0 = 0;
|
||||
double d1 = 0;
|
||||
switch (operands.length) {
|
||||
default: // will rarely happen. currently the parser itself fails.
|
||||
retval = ErrorEval.UNKNOWN_ERROR;
|
||||
break;
|
||||
case 2:
|
||||
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d0 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (retval == null) { // no error yet
|
||||
ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d1 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
} // end switch
|
||||
|
||||
if (retval == null) {
|
||||
retval = (Double.isNaN(d0) || Double.isNaN(d1))
|
||||
? (ValueEval) ErrorEval.VALUE_INVALID
|
||||
: new NumberEval(d0 * d1);
|
||||
ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d0 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
return retval;
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
ve = singleOperandEvaluate(args[1], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d1 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (Double.isNaN(d0) || Double.isNaN(d1)) {
|
||||
return ErrorEval.NUM_ERROR;
|
||||
}
|
||||
return new NumberEval(d0 * d1);
|
||||
}
|
||||
|
||||
public int getNumberOfOperands() {
|
||||
|
48
src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/NameEval.java
Executable file
48
src/scratchpad/src/org/apache/poi/hssf/record/formula/eval/NameEval.java
Executable 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();
|
||||
}
|
||||
}
|
@ -17,7 +17,6 @@
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
|
||||
/**
|
||||
* Provides functionality for evaluating arguments to functions and operators.
|
||||
*
|
||||
@ -43,12 +42,10 @@ public final class OperandResolver {
|
||||
*/
|
||||
public static ValueEval getSingleValue(Eval arg, int srcCellRow, short srcCellCol)
|
||||
throws EvaluationException {
|
||||
if (arg instanceof RefEval) {
|
||||
RefEval re = (RefEval) arg;
|
||||
return re.getInnerValueEval();
|
||||
}
|
||||
Eval result;
|
||||
if (arg instanceof AreaEval) {
|
||||
if (arg instanceof RefEval) {
|
||||
result = ((RefEval) arg).getInnerValueEval();
|
||||
} else if (arg instanceof AreaEval) {
|
||||
result = chooseSingleElementFromArea((AreaEval) arg, srcCellRow, srcCellCol);
|
||||
} else {
|
||||
result = arg;
|
||||
@ -223,13 +220,26 @@ public final class OperandResolver {
|
||||
* Some examples:<br/>
|
||||
* " 123 " -> 123.0<br/>
|
||||
* ".123" -> 0.123<br/>
|
||||
* These not supported yet:<br/>
|
||||
* " $ 1,000.00 " -> 1000.0<br/>
|
||||
* "$1.25E4" -> 12500.0<br/>
|
||||
* "5**2" -> 500<br/>
|
||||
* "250%" -> 2.5<br/>
|
||||
*
|
||||
* @param text
|
||||
* @return <code>null</code> if the specified text cannot be parsed as a number
|
||||
*/
|
||||
public static Double parseDouble(String text) {
|
||||
public static Double parseDouble(String pText) {
|
||||
String text = pText.trim();
|
||||
if(text.length() < 1) {
|
||||
return null;
|
||||
}
|
||||
boolean isPositive = true;
|
||||
if(text.charAt(0) == '-') {
|
||||
isPositive = false;
|
||||
text= text.substring(1).trim();
|
||||
}
|
||||
|
||||
if(!Character.isDigit(text.charAt(0))) {
|
||||
// avoid using NumberFormatException to tell when string is not a number
|
||||
return null;
|
||||
@ -242,8 +252,26 @@ public final class OperandResolver {
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
return new Double(val);
|
||||
return new Double(isPositive ? +val : -val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ve must be a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>, or <tt>BlankEval</tt>
|
||||
* @return the converted string value. never <code>null</code>
|
||||
*/
|
||||
public static String coerceValueToString(ValueEval ve) {
|
||||
if (ve instanceof StringValueEval) {
|
||||
StringValueEval sve = (StringValueEval) ve;
|
||||
return sve.getStringValue();
|
||||
}
|
||||
if (ve instanceof NumberEval) {
|
||||
NumberEval neval = (NumberEval) ve;
|
||||
return neval.getStringValue();
|
||||
}
|
||||
|
||||
if (ve instanceof BlankEval) {
|
||||
return "";
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected eval class (" + ve.getClass().getName() + ")");
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 8, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
@ -27,15 +24,13 @@ import org.apache.poi.hssf.record.formula.PowerPtg;
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class PowerEval extends NumericOperationEval {
|
||||
public final class PowerEval extends NumericOperationEval {
|
||||
|
||||
private PowerPtg delegate;
|
||||
|
||||
private static final ValueEvalToNumericXlator NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short)
|
||||
( ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
@ -49,48 +44,40 @@ public class PowerEval extends NumericOperationEval {
|
||||
return NUM_XLATOR;
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
Eval retval = null;
|
||||
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
if(args.length != 2) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
double d0 = 0;
|
||||
double d1 = 0;
|
||||
|
||||
switch (operands.length) {
|
||||
default: // will rarely happen. currently the parser itself fails.
|
||||
retval = ErrorEval.UNKNOWN_ERROR;
|
||||
break;
|
||||
case 2:
|
||||
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d0 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (retval == null) { // no error yet
|
||||
ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d1 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
} // end switch
|
||||
|
||||
if (retval == null) {
|
||||
double p = Math.pow(d0, d1);
|
||||
retval = (Double.isNaN(p))
|
||||
? (ValueEval) ErrorEval.VALUE_INVALID
|
||||
: new NumberEval(p);
|
||||
ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d0 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
return retval;
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
ve = singleOperandEvaluate(args[1], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d1 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
double p = Math.pow(d0, d1);
|
||||
if (Double.isNaN(p)) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return new NumberEval(p);
|
||||
}
|
||||
|
||||
public int getNumberOfOperands() {
|
||||
|
@ -14,47 +14,31 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 9, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.ReferencePtg;
|
||||
|
||||
/**
|
||||
* @author adeshmukh
|
||||
*
|
||||
*/
|
||||
public class Ref2DEval implements RefEval {
|
||||
public final class Ref2DEval implements RefEval {
|
||||
|
||||
private ValueEval value;
|
||||
private final ValueEval value;
|
||||
private final ReferencePtg delegate;
|
||||
|
||||
private ReferencePtg delegate;
|
||||
|
||||
private boolean evaluated;
|
||||
|
||||
public Ref2DEval(Ptg ptg, ValueEval value, boolean evaluated) {
|
||||
this.value = value;
|
||||
this.delegate = (ReferencePtg) ptg;
|
||||
this.evaluated = evaluated;
|
||||
public Ref2DEval(ReferencePtg ptg, ValueEval ve) {
|
||||
value = ve;
|
||||
delegate = ptg;
|
||||
}
|
||||
|
||||
public ValueEval getInnerValueEval() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public short getRow() {
|
||||
return delegate.getRow();
|
||||
}
|
||||
|
||||
public short getColumn() {
|
||||
return delegate.getColumn();
|
||||
}
|
||||
|
||||
public boolean isEvaluated() {
|
||||
return evaluated;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,13 +14,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 9, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
|
||||
/**
|
||||
@ -29,36 +25,23 @@ import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
*/
|
||||
public final class Ref3DEval implements RefEval {
|
||||
|
||||
private ValueEval value;
|
||||
private final ValueEval value;
|
||||
private final Ref3DPtg delegate;
|
||||
|
||||
private Ref3DPtg delegate;
|
||||
|
||||
private boolean evaluated;
|
||||
|
||||
public Ref3DEval(Ptg ptg, ValueEval value, boolean evaluated) {
|
||||
this.value = value;
|
||||
this.delegate = (Ref3DPtg) ptg;
|
||||
this.evaluated = evaluated;
|
||||
public Ref3DEval(Ref3DPtg ptg, ValueEval ve) {
|
||||
value = ve;
|
||||
delegate = ptg;
|
||||
}
|
||||
|
||||
public ValueEval getInnerValueEval() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public short getRow() {
|
||||
return delegate.getRow();
|
||||
}
|
||||
|
||||
public short getColumn() {
|
||||
return delegate.getColumn();
|
||||
}
|
||||
|
||||
public boolean isEvaluated() {
|
||||
return evaluated;
|
||||
}
|
||||
|
||||
public int getExternSheetIndex() {
|
||||
return delegate.getExternSheetIndex();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,11 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 9, 2005
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
/**
|
||||
@ -52,18 +48,4 @@ public interface RefEval extends ValueEval {
|
||||
* returns the row index.
|
||||
*/
|
||||
public short getRow();
|
||||
|
||||
/**
|
||||
* returns true if this RefEval contains an
|
||||
* evaluated value instead of a direct value.
|
||||
* eg. say cell A1 has the value: ="test"
|
||||
* Then the RefEval representing A1 will return
|
||||
* isEvaluated() equal to false. On the other
|
||||
* hand, say cell A1 has the value: =B1 and
|
||||
* B1 has the value "test", then the RefEval
|
||||
* representing A1 will return isEvaluated()
|
||||
* equal to true.
|
||||
*/
|
||||
public boolean isEvaluated();
|
||||
|
||||
}
|
||||
|
@ -14,10 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 8, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
@ -27,15 +24,13 @@ import org.apache.poi.hssf.record.formula.SubtractPtg;
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class SubtractEval extends NumericOperationEval {
|
||||
public final class SubtractEval extends NumericOperationEval {
|
||||
|
||||
private SubtractPtg delegate;
|
||||
|
||||
private static final ValueEvalToNumericXlator NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short)
|
||||
( ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
@ -49,18 +44,28 @@ public class SubtractEval extends NumericOperationEval {
|
||||
return NUM_XLATOR;
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
if(args.length != 2) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
Eval retval = null;
|
||||
double d0 = 0;
|
||||
double d1 = 0;
|
||||
switch (operands.length) {
|
||||
default: // will rarely happen. currently the parser itself fails.
|
||||
retval = ErrorEval.UNKNOWN_ERROR;
|
||||
break;
|
||||
case 2:
|
||||
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
|
||||
ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d0 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (retval == null) { // no error yet
|
||||
ve = singleOperandEvaluate(args[1], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d0 = ((NumericValueEval) ve).getNumberValue();
|
||||
d1 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
@ -68,20 +73,7 @@ public class SubtractEval extends NumericOperationEval {
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (retval == null) { // no error yet
|
||||
ve = singleOperandEvaluate(operands[1], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d1 = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
} // end switch
|
||||
}
|
||||
|
||||
if (retval == null) {
|
||||
retval = (Double.isNaN(d0) || Double.isNaN(d1))
|
||||
|
@ -14,10 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 8, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
@ -27,14 +24,12 @@ import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class UnaryMinusEval extends NumericOperationEval {
|
||||
public final class UnaryMinusEval extends NumericOperationEval {
|
||||
|
||||
private UnaryMinusPtg delegate;
|
||||
private static final ValueEvalToNumericXlator NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short)
|
||||
( ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
@ -49,32 +44,24 @@ public class UnaryMinusEval extends NumericOperationEval {
|
||||
return NUM_XLATOR;
|
||||
}
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
ValueEval retval = null;
|
||||
public Eval evaluate(Eval[] args, int srcRow, short srcCol) {
|
||||
if(args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
double d = 0;
|
||||
|
||||
switch (operands.length) {
|
||||
default:
|
||||
retval = ErrorEval.UNKNOWN_ERROR;
|
||||
break;
|
||||
case 1:
|
||||
ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else if (ve instanceof ErrorEval) {
|
||||
retval = ve;
|
||||
}
|
||||
ValueEval ve = singleOperandEvaluate(args[0], srcRow, srcCol);
|
||||
if (ve instanceof NumericValueEval) {
|
||||
d = ((NumericValueEval) ve).getNumberValue();
|
||||
}
|
||||
else if (ve instanceof BlankEval) {
|
||||
// do nothing
|
||||
}
|
||||
else if (ve instanceof ErrorEval) {
|
||||
return ve;
|
||||
}
|
||||
|
||||
if (retval == null) {
|
||||
retval = new NumberEval(-d);
|
||||
}
|
||||
|
||||
return retval;
|
||||
return new NumberEval(-d);
|
||||
}
|
||||
|
||||
public int getNumberOfOperands() {
|
||||
|
@ -14,10 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 8, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
@ -27,37 +24,10 @@ import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class UnaryPlusEval implements OperationEval /*extends NumericOperationEval*/ {
|
||||
public final class UnaryPlusEval implements OperationEval {
|
||||
|
||||
private UnaryPlusPtg delegate;
|
||||
|
||||
/*
|
||||
* COMMENT FOR COMMENTED CODE IN THIS FILE
|
||||
*
|
||||
* In excel the programmer seems to not have cared to
|
||||
* think about how strings were handled in other numeric
|
||||
* operations when he/she was implementing this operation :P
|
||||
*
|
||||
* Here's what I mean:
|
||||
*
|
||||
* Q. If the formula -"hello" evaluates to #VALUE! in excel, what should
|
||||
* the formula +"hello" evaluate to?
|
||||
*
|
||||
* A. +"hello" evaluates to "hello" (what the...?)
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// private static final ValueEvalToNumericXlator NUM_XLATOR =
|
||||
// new ValueEvalToNumericXlator((short)
|
||||
// ( ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
// | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
// | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
// | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
// | ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
// ));
|
||||
|
||||
|
||||
/**
|
||||
* called by reflection
|
||||
*/
|
||||
@ -65,76 +35,27 @@ public class UnaryPlusEval implements OperationEval /*extends NumericOperationEv
|
||||
this.delegate = (UnaryPlusPtg) ptg;
|
||||
}
|
||||
|
||||
// protected ValueEvalToNumericXlator getXlator() {
|
||||
// return NUM_XLATOR;
|
||||
// }
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcRow, short srcCol) {
|
||||
ValueEval retval = null;
|
||||
|
||||
switch (operands.length) {
|
||||
default:
|
||||
retval = ErrorEval.UNKNOWN_ERROR;
|
||||
break;
|
||||
case 1:
|
||||
|
||||
// ValueEval ve = singleOperandEvaluate(operands[0], srcRow, srcCol);
|
||||
// if (ve instanceof NumericValueEval) {
|
||||
// d = ((NumericValueEval) ve).getNumberValue();
|
||||
// }
|
||||
// else if (ve instanceof BlankEval) {
|
||||
// // do nothing
|
||||
// }
|
||||
// else if (ve instanceof ErrorEval) {
|
||||
// retval = ve;
|
||||
// }
|
||||
if (operands[0] instanceof RefEval) {
|
||||
RefEval re = (RefEval) operands[0];
|
||||
retval = re.getInnerValueEval();
|
||||
}
|
||||
else if (operands[0] instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) operands[0];
|
||||
if (ae.contains(srcRow, srcCol)) { // circular ref!
|
||||
retval = ErrorEval.CIRCULAR_REF_ERROR;
|
||||
}
|
||||
else if (ae.isRow()) {
|
||||
if (ae.containsColumn(srcCol)) {
|
||||
ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
|
||||
if (ve instanceof RefEval) {
|
||||
ve = ((RefEval) ve).getInnerValueEval();
|
||||
}
|
||||
retval = ve;
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
else if (ae.isColumn()) {
|
||||
if (ae.containsRow(srcRow)) {
|
||||
ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
|
||||
if (ve instanceof RefEval) {
|
||||
ve = ((RefEval) ve).getInnerValueEval();
|
||||
}
|
||||
retval = ve;
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
else {
|
||||
retval = (ValueEval) operands[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (retval instanceof BlankEval) {
|
||||
retval = new NumberEval(0);
|
||||
}
|
||||
|
||||
return retval;
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
if(args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
double d;
|
||||
try {
|
||||
ValueEval ve = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
|
||||
if(ve instanceof BlankEval) {
|
||||
return NumberEval.ZERO;
|
||||
}
|
||||
if(ve instanceof StringEval) {
|
||||
// Note - asymmetric with UnaryMinus
|
||||
// -"hello" evaluates to #VALUE!
|
||||
// but +"hello" evaluates to "hello"
|
||||
return ve;
|
||||
}
|
||||
d = OperandResolver.coerceValueToDouble(ve);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
return new NumberEval(+d);
|
||||
}
|
||||
|
||||
public int getNumberOfOperands() {
|
||||
@ -144,5 +65,4 @@ public class UnaryPlusEval implements OperationEval /*extends NumericOperationEv
|
||||
public int getType() {
|
||||
return delegate.getType();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ package org.apache.poi.hssf.record.formula.eval;
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class ValueEvalToNumericXlator {
|
||||
public final class ValueEvalToNumericXlator {
|
||||
|
||||
public static final int STRING_IS_PARSED = 0x0001;
|
||||
public static final int BOOL_IS_PARSED = 0x0002;
|
||||
@ -34,26 +34,18 @@ public class ValueEvalToNumericXlator {
|
||||
public static final int REF_BOOL_IS_PARSED = 0x0010;
|
||||
public static final int REF_BLANK_IS_PARSED = 0x0020;
|
||||
|
||||
public static final int EVALUATED_REF_STRING_IS_PARSED = 0x0040;
|
||||
public static final int EVALUATED_REF_BOOL_IS_PARSED = 0x0080;
|
||||
public static final int EVALUATED_REF_BLANK_IS_PARSED = 0x0100;
|
||||
|
||||
public static final int STRING_TO_BOOL_IS_PARSED = 0x0200;
|
||||
public static final int REF_STRING_TO_BOOL_IS_PARSED = 0x0400;
|
||||
|
||||
public static final int STRING_IS_INVALID_VALUE = 0x0800;
|
||||
public static final int REF_STRING_IS_INVALID_VALUE = 0x1000;
|
||||
|
||||
// public static final int BOOL_IS_BLANK = 0x2000;
|
||||
// public static final int REF_BOOL_IS_BLANK = 0x4000;
|
||||
// public static final int STRING_IS_BLANK = 0x8000;
|
||||
// public static final int REF_STRING_IS_BLANK = 0x10000;
|
||||
|
||||
private final int flags;
|
||||
|
||||
|
||||
public ValueEvalToNumericXlator(int flags) {
|
||||
this.flags = flags;
|
||||
|
||||
if (false) { // uncomment to see who is using this class
|
||||
System.err.println(new Throwable().getStackTrace()[1].getClassName() + "\t0x"
|
||||
+ Integer.toHexString(flags).toUpperCase());
|
||||
}
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,7 +63,7 @@ public class ValueEvalToNumericXlator {
|
||||
|
||||
// most common case - least worries :)
|
||||
else if (eval instanceof NumberEval) {
|
||||
retval = (NumberEval) eval;
|
||||
retval = eval;
|
||||
}
|
||||
|
||||
// booleval
|
||||
@ -125,50 +117,33 @@ public class ValueEvalToNumericXlator {
|
||||
* @param eval
|
||||
*/
|
||||
private ValueEval xlateRefEval(RefEval reval) {
|
||||
ValueEval retval = null;
|
||||
ValueEval eval = (ValueEval) reval.getInnerValueEval();
|
||||
ValueEval eval = reval.getInnerValueEval();
|
||||
|
||||
// most common case - least worries :)
|
||||
if (eval instanceof NumberEval) {
|
||||
retval = (NumberEval) eval;
|
||||
return eval;
|
||||
}
|
||||
|
||||
// booleval
|
||||
else if (eval instanceof BoolEval) {
|
||||
retval = ((flags & REF_BOOL_IS_PARSED) > 0)
|
||||
if (eval instanceof BoolEval) {
|
||||
return ((flags & REF_BOOL_IS_PARSED) > 0)
|
||||
? (ValueEval) eval
|
||||
: BlankEval.INSTANCE;
|
||||
}
|
||||
|
||||
// stringeval
|
||||
else if (eval instanceof StringEval) {
|
||||
retval = xlateRefStringEval((StringEval) eval);
|
||||
if (eval instanceof StringEval) {
|
||||
return xlateRefStringEval((StringEval) eval);
|
||||
}
|
||||
|
||||
// erroreval
|
||||
else if (eval instanceof ErrorEval) {
|
||||
retval = eval;
|
||||
if (eval instanceof ErrorEval) {
|
||||
return eval;
|
||||
}
|
||||
|
||||
// refeval
|
||||
else if (eval instanceof RefEval) {
|
||||
RefEval re = (RefEval) eval;
|
||||
retval = xlateRefEval(re);
|
||||
if (eval instanceof BlankEval) {
|
||||
return xlateBlankEval(REF_BLANK_IS_PARSED);
|
||||
}
|
||||
|
||||
else if (eval instanceof BlankEval) {
|
||||
retval = xlateBlankEval(reval.isEvaluated() ? EVALUATED_REF_BLANK_IS_PARSED : REF_BLANK_IS_PARSED);
|
||||
}
|
||||
|
||||
// probably AreaEval ? then not acceptable.
|
||||
else {
|
||||
throw new RuntimeException("Invalid ValueEval type passed for conversion: " + eval.getClass());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return retval;
|
||||
throw new RuntimeException("Invalid ValueEval type passed for conversion: ("
|
||||
+ eval.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -176,93 +151,38 @@ public class ValueEvalToNumericXlator {
|
||||
* @param eval
|
||||
*/
|
||||
private ValueEval xlateStringEval(StringEval eval) {
|
||||
ValueEval retval = null;
|
||||
|
||||
if ((flags & STRING_IS_PARSED) > 0) {
|
||||
String s = eval.getStringValue();
|
||||
try {
|
||||
double d = Double.parseDouble(s);
|
||||
retval = new NumberEval(d);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if ((flags & STRING_TO_BOOL_IS_PARSED) > 0) {
|
||||
try {
|
||||
boolean b = Boolean.getBoolean(s);
|
||||
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
|
||||
}
|
||||
catch (Exception e2) { retval = ErrorEval.VALUE_INVALID; }
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
Double d = OperandResolver.parseDouble(s);
|
||||
if(d == null) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return new NumberEval(d.doubleValue());
|
||||
}
|
||||
else if ((flags & STRING_TO_BOOL_IS_PARSED) > 0) {
|
||||
String s = eval.getStringValue();
|
||||
try {
|
||||
boolean b = Boolean.getBoolean(s);
|
||||
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
|
||||
}
|
||||
catch (Exception e) { retval = ErrorEval.VALUE_INVALID; }
|
||||
}
|
||||
|
||||
// strings are errors?
|
||||
else if ((flags & STRING_IS_INVALID_VALUE) > 0) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
if ((flags & STRING_IS_INVALID_VALUE) > 0) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
// ignore strings
|
||||
else {
|
||||
retval = xlateBlankEval(BLANK_IS_PARSED);
|
||||
}
|
||||
return retval;
|
||||
return xlateBlankEval(BLANK_IS_PARSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* uses the relevant flags to decode the StringEval
|
||||
* @param eval
|
||||
*/
|
||||
private ValueEval xlateRefStringEval(StringEval eval) {
|
||||
ValueEval retval = null;
|
||||
private ValueEval xlateRefStringEval(StringEval sve) {
|
||||
if ((flags & REF_STRING_IS_PARSED) > 0) {
|
||||
StringEval sve = (StringEval) eval;
|
||||
String s = sve.getStringValue();
|
||||
try {
|
||||
double d = Double.parseDouble(s);
|
||||
retval = new NumberEval(d);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if ((flags & REF_STRING_TO_BOOL_IS_PARSED) > 0) {
|
||||
try {
|
||||
boolean b = Boolean.getBoolean(s);
|
||||
retval = b ? BoolEval.TRUE : BoolEval.FALSE;
|
||||
}
|
||||
catch (Exception e2) { retval = ErrorEval.VALUE_INVALID; }
|
||||
}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
Double d = OperandResolver.parseDouble(s);
|
||||
if(d == null) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return new NumberEval(d.doubleValue());
|
||||
}
|
||||
else if ((flags & REF_STRING_TO_BOOL_IS_PARSED) > 0) {
|
||||
StringEval sve = (StringEval) eval;
|
||||
String s = sve.getStringValue();
|
||||
try {
|
||||
boolean b = Boolean.getBoolean(s);
|
||||
retval = b ? BoolEval.TRUE : BoolEval.FALSE;;
|
||||
}
|
||||
catch (Exception e) { retval = ErrorEval.VALUE_INVALID; }
|
||||
}
|
||||
|
||||
// strings are errors?
|
||||
else if ((flags & REF_STRING_IS_INVALID_VALUE) > 0) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
// strings are blanks
|
||||
else {
|
||||
retval = BlankEval.INSTANCE;
|
||||
}
|
||||
return retval;
|
||||
return BlankEval.INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,8 +33,8 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
|
||||
public class Avedev extends MultiOperandNumericFunction {
|
||||
|
||||
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short) (0
|
||||
// ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
new ValueEvalToNumericXlator((short) (
|
||||
ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
@ -44,7 +44,6 @@ public class Avedev extends MultiOperandNumericFunction {
|
||||
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
|
||||
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
|
||||
));
|
||||
|
||||
/**
|
||||
|
@ -33,8 +33,8 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
|
||||
public class Average extends MultiOperandNumericFunction {
|
||||
|
||||
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short) (0
|
||||
// ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
new ValueEvalToNumericXlator((short) (
|
||||
ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
@ -44,7 +44,6 @@ public class Average extends MultiOperandNumericFunction {
|
||||
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
|
||||
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
|
||||
));
|
||||
|
||||
/**
|
||||
|
@ -14,10 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on Jun 20, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
@ -38,13 +35,10 @@ public abstract class FinanceFunction extends NumericFunction {
|
||||
new ValueEvalToNumericXlator((short) (0
|
||||
| ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.BLANK_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
|
||||
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
|
||||
@ -56,11 +50,11 @@ public abstract class FinanceFunction extends NumericFunction {
|
||||
* if they desire to return a different ValueEvalToNumericXlator instance
|
||||
* than the default.
|
||||
*/
|
||||
protected ValueEvalToNumericXlator getXlator() {
|
||||
protected final ValueEvalToNumericXlator getXlator() {
|
||||
return DEFAULT_NUM_XLATOR;
|
||||
}
|
||||
|
||||
protected ValueEval singleOperandNumericAsBoolean(Eval eval, int srcRow, short srcCol) {
|
||||
protected final ValueEval singleOperandNumericAsBoolean(Eval eval, int srcRow, short srcCol) {
|
||||
ValueEval retval = null;
|
||||
retval = singleOperandEvaluate(eval, srcRow, srcCol);
|
||||
if (retval instanceof NumericValueEval) {
|
||||
@ -74,5 +68,4 @@ public abstract class FinanceFunction extends NumericFunction {
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,28 +28,22 @@ import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class If implements Function {
|
||||
public final class If implements Function {
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
|
||||
public Eval evaluate(Eval[] evals, int srcCellRow, short srcCellCol) {
|
||||
Eval retval = null;
|
||||
Eval evalWhenFalse = BoolEval.FALSE;
|
||||
switch (evals.length) {
|
||||
switch (args.length) {
|
||||
case 3:
|
||||
evalWhenFalse = evals[2];
|
||||
evalWhenFalse = args[2];
|
||||
case 2:
|
||||
BoolEval beval = (BoolEval) evals[0];
|
||||
BoolEval beval = (BoolEval) args[0]; // TODO - class cast exception
|
||||
if (beval.getBooleanValue()) {
|
||||
retval = evals[1];
|
||||
return args[1];
|
||||
}
|
||||
else {
|
||||
retval = evalWhenFalse;
|
||||
}
|
||||
break;
|
||||
return evalWhenFalse;
|
||||
default:
|
||||
retval = ErrorEval.UNKNOWN_ERROR;
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -14,79 +14,35 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class Isblank implements Function {
|
||||
public final class Isblank implements Function {
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
|
||||
ValueEval retval = null;
|
||||
boolean b = false;
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
if(args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
Eval arg = args[0];
|
||||
|
||||
switch (operands.length) {
|
||||
default:
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
break;
|
||||
case 1:
|
||||
if (operands[0] instanceof BlankEval) {
|
||||
b = true;
|
||||
}
|
||||
else if (operands[0] instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) operands[0];
|
||||
if (ae.contains(srcCellRow, srcCellCol)) { // circular ref!
|
||||
retval = ErrorEval.CIRCULAR_REF_ERROR;
|
||||
}
|
||||
else if (ae.isRow()) {
|
||||
if (ae.containsColumn(srcCellCol)) {
|
||||
ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCellCol);
|
||||
b = (ve instanceof BlankEval);
|
||||
}
|
||||
else {
|
||||
b = false;
|
||||
}
|
||||
}
|
||||
else if (ae.isColumn()) {
|
||||
if (ae.containsRow(srcCellRow)) {
|
||||
ValueEval ve = ae.getValueAt(srcCellRow, ae.getFirstColumn());
|
||||
b = (ve instanceof BlankEval);
|
||||
}
|
||||
else {
|
||||
b = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
b = false;
|
||||
}
|
||||
}
|
||||
else if (operands[0] instanceof RefEval) {
|
||||
RefEval re = (RefEval) operands[0];
|
||||
b = (!re.isEvaluated()) && re.getInnerValueEval() instanceof BlankEval;
|
||||
}
|
||||
else {
|
||||
b = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == null) {
|
||||
retval = b
|
||||
? BoolEval.TRUE
|
||||
: BoolEval.FALSE;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
ValueEval singleCellValue;
|
||||
try {
|
||||
singleCellValue = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
} catch (EvaluationException e) {
|
||||
return BoolEval.FALSE;
|
||||
}
|
||||
return BoolEval.valueOf(singleCellValue instanceof BlankEval);
|
||||
}
|
||||
}
|
||||
|
@ -14,125 +14,36 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringValueEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class Len extends TextFunction {
|
||||
public final class Len extends TextFunction {
|
||||
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
|
||||
ValueEval retval = null;
|
||||
String s = null;
|
||||
if(args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
switch (operands.length) {
|
||||
default:
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
break;
|
||||
case 1:
|
||||
ValueEval ve = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
|
||||
if (ve instanceof StringValueEval) {
|
||||
StringValueEval sve = (StringValueEval) ve;
|
||||
s = sve.getStringValue();
|
||||
}
|
||||
else if (ve instanceof RefEval) {
|
||||
RefEval re = (RefEval) ve;
|
||||
ValueEval ive = re.getInnerValueEval();
|
||||
if (ive instanceof BlankEval) {
|
||||
s = re.isEvaluated() ? "0" : null;
|
||||
}
|
||||
else if (ive instanceof StringValueEval) {
|
||||
s = ((StringValueEval) ive).getStringValue();
|
||||
}
|
||||
else if (ive instanceof BlankEval) {}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
else if (ve instanceof BlankEval) {}
|
||||
else {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
ValueEval veval = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
|
||||
|
||||
if (retval == null) {
|
||||
s = (s == null) ? EMPTY_STRING : s;
|
||||
retval = new NumberEval(s.length());
|
||||
}
|
||||
String str = OperandResolver.coerceValueToString(veval);
|
||||
|
||||
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;
|
||||
}
|
||||
return new NumberEval(str.length());
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
@ -30,12 +27,11 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public class Maxa extends MultiOperandNumericFunction {
|
||||
public final class Maxa extends MultiOperandNumericFunction {
|
||||
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short) (
|
||||
ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
|
@ -23,7 +23,6 @@ import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringValueEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
@ -35,7 +34,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
*
|
||||
* @author Manda Wilson < wilson at c bio dot msk cc dot org >
|
||||
*/
|
||||
public class Mid extends TextFunction {
|
||||
public class Mid implements Function {
|
||||
/**
|
||||
* Returns a specific number of characters from a text string, starting at
|
||||
* the position you specify, based on the number of characters you specify.
|
||||
@ -52,7 +51,8 @@ public class Mid extends TextFunction {
|
||||
int numChars;
|
||||
|
||||
try {
|
||||
text = evaluateTextArg(args[0], srcCellRow, srcCellCol);
|
||||
ValueEval evText = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
|
||||
text = OperandResolver.coerceValueToString(evText);
|
||||
int startCharNum = evaluateNumberArg(args[1], srcCellRow, srcCellCol);
|
||||
numChars = evaluateNumberArg(args[2], srcCellRow, srcCellCol);
|
||||
startIx = startCharNum - 1; // convert to zero-based
|
||||
@ -79,7 +79,7 @@ public class Mid extends TextFunction {
|
||||
|
||||
}
|
||||
|
||||
public static int evaluateNumberArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||
private static int evaluateNumberArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||
ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
if (ev instanceof BlankEval) {
|
||||
// Note - for start_num arg, blank causes error(#VALUE!),
|
||||
@ -89,12 +89,4 @@ public class Mid extends TextFunction {
|
||||
|
||||
return OperandResolver.coerceValueToInt(ev);
|
||||
}
|
||||
|
||||
private static String evaluateTextArg(Eval arg, int srcCellRow, short srcCellCol) throws EvaluationException {
|
||||
ValueEval ev = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
|
||||
if (ev instanceof StringValueEval) {
|
||||
return ((StringValueEval) ev).getStringValue();
|
||||
}
|
||||
throw EvaluationException.invalidValue();
|
||||
}
|
||||
}
|
@ -14,10 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
@ -35,7 +32,6 @@ public class Mina extends MultiOperandNumericFunction {
|
||||
new ValueEvalToNumericXlator((short) (
|
||||
ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
|
@ -76,32 +76,9 @@ public abstract class MultiOperandNumericFunction extends NumericFunction {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short) (
|
||||
ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
|
||||
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
|
||||
));
|
||||
|
||||
private static final int DEFAULT_MAX_NUM_OPERANDS = 30;
|
||||
|
||||
/**
|
||||
* this is the default impl for the factory method getXlator
|
||||
* of the super class NumericFunction. Subclasses can override this method
|
||||
* if they desire to return a different ValueEvalToNumericXlator instance
|
||||
* than the default.
|
||||
*/
|
||||
protected ValueEvalToNumericXlator getXlator() {
|
||||
return DEFAULT_NUM_XLATOR;
|
||||
}
|
||||
protected abstract ValueEvalToNumericXlator getXlator();
|
||||
|
||||
/**
|
||||
* Maximum number of operands accepted by this function.
|
||||
@ -160,9 +137,7 @@ public abstract class MultiOperandNumericFunction extends NumericFunction {
|
||||
* HSSFFormulaEvaluator where we store an array
|
||||
* of RefEvals as the "values" array.
|
||||
*/
|
||||
RefEval re = (values[j] instanceof RefEval)
|
||||
? new Ref2DEval(null, ((RefEval) values[j]).getInnerValueEval(), true)
|
||||
: new Ref2DEval(null, values[j], false);
|
||||
RefEval re = new Ref2DEval(null, values[j]);
|
||||
ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol);
|
||||
|
||||
if (ve instanceof NumericValueEval) {
|
||||
|
@ -39,10 +39,8 @@ public abstract class NumericFunction implements Function {
|
||||
new ValueEvalToNumericXlator((short) (
|
||||
ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
|
||||
|
@ -33,8 +33,8 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
|
||||
public class Stdev extends MultiOperandNumericFunction {
|
||||
|
||||
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short) (0
|
||||
// ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
new ValueEvalToNumericXlator((short) (
|
||||
ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
@ -44,7 +44,6 @@ public class Stdev extends MultiOperandNumericFunction {
|
||||
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
|
||||
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BLANK_IS_PARSED
|
||||
));
|
||||
|
||||
/**
|
||||
|
@ -33,18 +33,18 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
|
||||
public class Sumsq extends MultiOperandNumericFunction {
|
||||
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short) (
|
||||
// ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
|
||||
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
|
||||
ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
|
||||
| ValueEvalToNumericXlator.BLANK_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_BLANK_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.BLANK_IS_PARSED
|
||||
));
|
||||
|
||||
protected ValueEvalToNumericXlator getXlator() {
|
||||
|
@ -14,50 +14,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* 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 = Σ<sub>i: 0..n</sub>(x<sub>i</sub><sup>2</sup>-y<sub>i</sub><sup>2</sup>)
|
||||
*
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public class Sumx2my2 extends XYNumericFunction {
|
||||
public final class Sumx2my2 extends XYNumericFunction {
|
||||
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
|
||||
ValueEval retval = null;
|
||||
double[][] values = null;
|
||||
|
||||
int checkLen = 0; // check to see that all array lengths are equal
|
||||
switch (operands.length) {
|
||||
default:
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
break;
|
||||
case 2:
|
||||
values = getValues(operands, srcCellRow, srcCellCol);
|
||||
if (values==null
|
||||
|| values[X] == null || values[Y] == null
|
||||
|| values[X].length == 0 || values[Y].length == 0
|
||||
|| values[X].length != values[Y].length) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == null) {
|
||||
double d = MathX.sumx2my2(values[X], values[Y]);
|
||||
retval = (Double.isNaN(d) || Double.isInfinite(d))
|
||||
? (ValueEval) ErrorEval.NUM_ERROR
|
||||
: new NumberEval(d);
|
||||
}
|
||||
|
||||
return retval;
|
||||
protected double evaluate(double[] xArray, double[] yArray) {
|
||||
return MathX.sumx2my2(xArray, yArray);
|
||||
}
|
||||
}
|
||||
|
@ -14,50 +14,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* 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 = Σ<sub>i: 0..n</sub>(x<sub>i</sub><sup>2</sup>+y<sub>i</sub><sup>2</sup>)
|
||||
*
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public class Sumx2py2 extends XYNumericFunction {
|
||||
public final class Sumx2py2 extends XYNumericFunction {
|
||||
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
|
||||
ValueEval retval = null;
|
||||
double[][] values = null;
|
||||
|
||||
int checkLen = 0; // check to see that all array lengths are equal
|
||||
switch (operands.length) {
|
||||
default:
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
break;
|
||||
case 2:
|
||||
values = getValues(operands, srcCellRow, srcCellCol);
|
||||
if (values==null
|
||||
|| values[X] == null || values[Y] == null
|
||||
|| values[X].length == 0 || values[Y].length == 0
|
||||
|| values[X].length != values[Y].length) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == null) {
|
||||
double d = MathX.sumx2py2(values[X], values[Y]);
|
||||
retval = (Double.isNaN(d) || Double.isInfinite(d))
|
||||
? (ValueEval) ErrorEval.NUM_ERROR
|
||||
: new NumberEval(d);
|
||||
}
|
||||
|
||||
return retval;
|
||||
protected double evaluate(double[] xArray, double[] yArray) {
|
||||
return MathX.sumx2py2(xArray, yArray);
|
||||
}
|
||||
}
|
||||
|
@ -14,50 +14,23 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 15, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* 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 = Σ<sub>i: 0..n</sub>(x<sub>i</sub>-y<sub>i</sub>)<sup>2</sup>
|
||||
*
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public class Sumxmy2 extends XYNumericFunction {
|
||||
public final class Sumxmy2 extends XYNumericFunction {
|
||||
|
||||
|
||||
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
|
||||
ValueEval retval = null;
|
||||
double[][] values = null;
|
||||
|
||||
int checkLen = 0; // check to see that all array lengths are equal
|
||||
switch (operands.length) {
|
||||
default:
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
break;
|
||||
case 2:
|
||||
values = getValues(operands, srcCellRow, srcCellCol);
|
||||
if (values==null
|
||||
|| values[X] == null || values[Y] == null
|
||||
|| values[X].length == 0 || values[Y].length == 0
|
||||
|| values[X].length != values[Y].length) {
|
||||
retval = ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == null) {
|
||||
double d = MathX.sumxmy2(values[X], values[Y]);
|
||||
retval = (Double.isNaN(d) || Double.isInfinite(d))
|
||||
? (ValueEval) ErrorEval.NUM_ERROR
|
||||
: new NumberEval(d);
|
||||
}
|
||||
|
||||
return retval;
|
||||
protected double evaluate(double[] xArray, double[] yArray) {
|
||||
return MathX.sumxmy2(xArray, yArray);
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,11 @@
|
||||
*/
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringValueEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
|
||||
/**
|
||||
@ -30,46 +29,25 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
* value is string.
|
||||
* @author Manda Wilson < wilson at c bio dot msk cc dot org >
|
||||
*/
|
||||
public class Trim extends TextFunction {
|
||||
public final class Trim extends TextFunction {
|
||||
|
||||
/**
|
||||
* Removes leading and trailing spaces from value if evaluated
|
||||
* operand value is string.
|
||||
* Returns StringEval only if evaluated operand is of type string
|
||||
* (and is not blank or null) or number. If evaluated operand is
|
||||
* of type string and is blank or null, or if evaluated operand is
|
||||
* of type blank, returns BlankEval. Otherwise returns ErrorEval.
|
||||
*
|
||||
* @see org.apache.poi.hssf.record.formula.eval.Eval
|
||||
*/
|
||||
public Eval evaluate(Eval[] operands, int srcCellRow, short srcCellCol) {
|
||||
Eval retval = ErrorEval.VALUE_INVALID;
|
||||
String str = null;
|
||||
public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
|
||||
|
||||
switch (operands.length) {
|
||||
default:
|
||||
break;
|
||||
case 1:
|
||||
ValueEval veval = singleOperandEvaluate(operands[0], srcCellRow, srcCellCol);
|
||||
if (veval instanceof StringValueEval) {
|
||||
StringValueEval sve = (StringValueEval) veval;
|
||||
str = sve.getStringValue();
|
||||
if (str == null || str.trim().equals("")) {
|
||||
return BlankEval.INSTANCE;
|
||||
}
|
||||
}
|
||||
else if (veval instanceof NumberEval) {
|
||||
NumberEval neval = (NumberEval) veval;
|
||||
str = neval.getStringValue();
|
||||
}
|
||||
else if (veval instanceof BlankEval) {
|
||||
return BlankEval.INSTANCE;
|
||||
}
|
||||
}
|
||||
if(args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
if (str != null) {
|
||||
retval = new StringEval(str.trim());
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
try {
|
||||
ValueEval veval = OperandResolver.getSingleValue(args[0], srcCellRow, srcCellCol);
|
||||
|
||||
String str = OperandResolver.coerceValueToString(veval);
|
||||
str = str.trim();
|
||||
if(str.length() < 1) {
|
||||
return StringEval.EMPTY_INSTANCE;
|
||||
}
|
||||
return new StringEval(str);
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,55 +14,68 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
* Created on May 29, 2005
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*
|
||||
*/
|
||||
public abstract class XYNumericFunction extends NumericFunction {
|
||||
public abstract class XYNumericFunction implements Function {
|
||||
protected static final int X = 0;
|
||||
protected static final int Y = 1;
|
||||
|
||||
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
|
||||
new ValueEvalToNumericXlator((short) (
|
||||
ValueEvalToNumericXlator.BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.REF_STRING_IS_PARSED
|
||||
| ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED
|
||||
//| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE
|
||||
//| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE
|
||||
));
|
||||
protected static final class DoubleArrayPair {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
protected int getMaxNumOperands() {
|
||||
return 30;
|
||||
|
||||
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
|
||||
@ -70,98 +83,82 @@ public abstract class XYNumericFunction extends NumericFunction {
|
||||
* are ignored. Error operands or cells containing operands of type
|
||||
* that are considered invalid and would result in #VALUE! error in
|
||||
* excel cause this function to return null.
|
||||
*
|
||||
* @param xops
|
||||
* @param yops
|
||||
* @param srcRow
|
||||
* @param srcCol
|
||||
*/
|
||||
protected double[][] getNumberArray(Eval[] xops, Eval[] yops, int srcRow, short srcCol) {
|
||||
double[][] retval = new double[2][30];
|
||||
private static double[][] getNumberArray(Eval[] xops, Eval[] yops) throws EvaluationException {
|
||||
|
||||
// check for errors first: size mismatch, value errors in x, value errors in y
|
||||
|
||||
int nArrayItems = xops.length;
|
||||
if(nArrayItems != yops.length) {
|
||||
throw new EvaluationException(ErrorEval.NA);
|
||||
}
|
||||
for (int i = 0; i < xops.length; i++) {
|
||||
Eval eval = xops[i];
|
||||
if (eval instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) eval);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < yops.length; i++) {
|
||||
Eval eval = yops[i];
|
||||
if (eval instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) eval);
|
||||
}
|
||||
}
|
||||
|
||||
double[] xResult = new double[nArrayItems];
|
||||
double[] yResult = new double[nArrayItems];
|
||||
|
||||
int count = 0;
|
||||
|
||||
if (xops.length > getMaxNumOperands()
|
||||
|| yops.length > getMaxNumOperands()
|
||||
|| xops.length != yops.length) {
|
||||
retval = null;
|
||||
}
|
||||
else {
|
||||
for (int i=0, iSize=nArrayItems; i<iSize; i++) {
|
||||
Eval xEval = xops[i];
|
||||
Eval yEval = yops[i];
|
||||
|
||||
for (int i=0, iSize=xops.length; i<iSize; i++) {
|
||||
Eval xEval = xops[i];
|
||||
Eval yEval = yops[i];
|
||||
if (isNumberEval(xEval) && isNumberEval(yEval)) {
|
||||
xResult[count] = getDoubleValue(xEval);
|
||||
yResult[count] = getDoubleValue(yEval);
|
||||
if (Double.isNaN(xResult[count]) || Double.isNaN(xResult[count])) {
|
||||
throw new EvaluationException(ErrorEval.NUM_ERROR);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (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) {
|
||||
double[][] temp = retval;
|
||||
retval[X] = trimToSize(retval[X], count);
|
||||
retval[Y] = trimToSize(retval[Y], count);
|
||||
}
|
||||
|
||||
return retval;
|
||||
return new double[][] {
|
||||
trimToSize(xResult, count),
|
||||
trimToSize(yResult, count),
|
||||
};
|
||||
}
|
||||
|
||||
protected double[][] getValues(Eval[] operands, int srcCellRow, short srcCellCol) {
|
||||
double[][] retval = null;
|
||||
private static double[][] getValues(Eval argX, Eval argY) throws EvaluationException {
|
||||
|
||||
outer: do {
|
||||
if (operands.length == 2) {
|
||||
Eval[] xEvals = new Eval[1];
|
||||
Eval[] yEvals = new Eval[1];
|
||||
if (operands[X] instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) operands[0];
|
||||
xEvals = ae.getValues();
|
||||
}
|
||||
else if (operands[X] instanceof ErrorEval) {
|
||||
break outer;
|
||||
}
|
||||
else {
|
||||
xEvals[0] = operands[X];
|
||||
}
|
||||
if (argX instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) argX);
|
||||
}
|
||||
if (argY instanceof ErrorEval) {
|
||||
throw new EvaluationException((ErrorEval) argY);
|
||||
}
|
||||
|
||||
if (operands[Y] instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) operands[Y];
|
||||
yEvals = ae.getValues();
|
||||
}
|
||||
else if (operands[Y] instanceof ErrorEval) {
|
||||
break outer;
|
||||
}
|
||||
else {
|
||||
yEvals[0] = operands[Y];
|
||||
}
|
||||
Eval[] xEvals;
|
||||
Eval[] yEvals;
|
||||
if (argX instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) argX;
|
||||
xEvals = ae.getValues();
|
||||
} else {
|
||||
xEvals = new Eval[] { argX, };
|
||||
}
|
||||
|
||||
retval = getNumberArray(xEvals, yEvals, srcCellRow, srcCellCol);
|
||||
}
|
||||
} while (false);
|
||||
if (argY instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) argY;
|
||||
yEvals = ae.getValues();
|
||||
} else {
|
||||
yEvals = new Eval[] { argY, };
|
||||
}
|
||||
|
||||
return retval;
|
||||
return getNumberArray(xEvals, yEvals);
|
||||
}
|
||||
|
||||
|
||||
protected static double[] ensureCapacity(double[] arr, int pos) {
|
||||
double[] temp = arr;
|
||||
while (pos >= arr.length) {
|
||||
arr = new double[arr.length << 2];
|
||||
}
|
||||
if (temp.length != arr.length)
|
||||
System.arraycopy(temp, 0, arr, 0, temp.length);
|
||||
return arr;
|
||||
}
|
||||
|
||||
protected static double[] trimToSize(double[] arr, int len) {
|
||||
private static double[] trimToSize(double[] arr, int len) {
|
||||
double[] tarr = arr;
|
||||
if (arr.length > len) {
|
||||
tarr = new double[len];
|
||||
@ -170,7 +167,7 @@ public abstract class XYNumericFunction extends NumericFunction {
|
||||
return tarr;
|
||||
}
|
||||
|
||||
protected static boolean isNumberEval(Eval eval) {
|
||||
private static boolean isNumberEval(Eval eval) {
|
||||
boolean retval = false;
|
||||
|
||||
if (eval instanceof NumberEval) {
|
||||
@ -185,7 +182,7 @@ public abstract class XYNumericFunction extends NumericFunction {
|
||||
return retval;
|
||||
}
|
||||
|
||||
protected static double getDoubleValue(Eval eval) {
|
||||
private static double getDoubleValue(Eval eval) {
|
||||
double retval = 0;
|
||||
if (eval instanceof NumberEval) {
|
||||
NumberEval ne = (NumberEval) eval;
|
||||
|
@ -80,6 +80,7 @@ import org.apache.poi.hssf.record.formula.eval.GreaterThanEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.LessEqualEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.LessThanEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.MultiplyEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NameEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NotEqualEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperationEval;
|
||||
@ -360,16 +361,16 @@ public class HSSFFormulaEvaluator {
|
||||
EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
|
||||
|
||||
if(!tracker.startEvaluate(workbook, sheet, srcRowNum, srcColNum)) {
|
||||
return ErrorEval.CIRCULAR_REF_ERROR;
|
||||
return ErrorEval.CIRCULAR_REF_ERROR;
|
||||
}
|
||||
try {
|
||||
return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula());
|
||||
return evaluateCell(workbook, sheet, srcRowNum, srcColNum, srcCell.getCellFormula());
|
||||
} finally {
|
||||
tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
|
||||
tracker.endEvaluate(workbook, sheet, srcRowNum, srcColNum);
|
||||
}
|
||||
}
|
||||
private static ValueEval evaluateCell(HSSFWorkbook workbook, HSSFSheet sheet,
|
||||
int srcRowNum, short srcColNum, String cellFormulaText) {
|
||||
int srcRowNum, short srcColNum, String cellFormulaText) {
|
||||
FormulaParser parser = new FormulaParser(cellFormulaText, workbook.getWorkbook());
|
||||
parser.parse();
|
||||
Ptg[] ptgs = parser.getRPNPtg();
|
||||
@ -380,15 +381,24 @@ public class HSSFFormulaEvaluator {
|
||||
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
|
||||
|
||||
// since we don't know how to handle these yet :(
|
||||
if (ptgs[i] instanceof ControlPtg) { continue; }
|
||||
if (ptgs[i] instanceof MemErrPtg) { continue; }
|
||||
if (ptgs[i] instanceof MissingArgPtg) { continue; }
|
||||
if (ptgs[i] instanceof NamePtg) { continue; }
|
||||
if (ptgs[i] instanceof NameXPtg) { continue; }
|
||||
if (ptgs[i] instanceof UnknownPtg) { continue; }
|
||||
Ptg ptg = ptgs[i];
|
||||
if (ptg instanceof ControlPtg) { continue; }
|
||||
if (ptg instanceof MemErrPtg) { continue; }
|
||||
if (ptg instanceof MissingArgPtg) { continue; }
|
||||
if (ptg instanceof NamePtg) {
|
||||
// named ranges, macro functions
|
||||
NamePtg namePtg = (NamePtg) ptg;
|
||||
stack.push(new NameEval(namePtg.getIndex()));
|
||||
continue;
|
||||
}
|
||||
if (ptg instanceof NameXPtg) {
|
||||
// TODO - external functions
|
||||
continue;
|
||||
}
|
||||
if (ptg instanceof UnknownPtg) { continue; }
|
||||
|
||||
if (ptgs[i] instanceof OperationPtg) {
|
||||
OperationPtg optg = (OperationPtg) ptgs[i];
|
||||
if (ptg instanceof OperationPtg) {
|
||||
OperationPtg optg = (OperationPtg) ptg;
|
||||
|
||||
// parens can be ignored since we have RPN tokens
|
||||
if (optg instanceof ParenthesisPtg) { continue; }
|
||||
@ -408,47 +418,67 @@ public class HSSFFormulaEvaluator {
|
||||
Eval opresult = invokeOperation(operation, ops, srcRowNum, srcColNum, workbook, sheet);
|
||||
stack.push(opresult);
|
||||
}
|
||||
else if (ptgs[i] instanceof ReferencePtg) {
|
||||
ReferencePtg ptg = (ReferencePtg) ptgs[i];
|
||||
short colnum = ptg.getColumn();
|
||||
short rownum = ptg.getRow();
|
||||
else if (ptg instanceof ReferencePtg) {
|
||||
ReferencePtg refPtg = (ReferencePtg) ptg;
|
||||
short colnum = refPtg.getColumn();
|
||||
short rownum = refPtg.getRow();
|
||||
HSSFRow row = sheet.getRow(rownum);
|
||||
HSSFCell cell = (row != null) ? row.getCell(colnum) : null;
|
||||
stack.push(createRef2DEval(ptg, cell, row, sheet, workbook));
|
||||
stack.push(createRef2DEval(refPtg, cell, row, sheet, workbook));
|
||||
}
|
||||
else if (ptgs[i] instanceof Ref3DPtg) {
|
||||
Ref3DPtg ptg = (Ref3DPtg) ptgs[i];
|
||||
short colnum = ptg.getColumn();
|
||||
short rownum = ptg.getRow();
|
||||
else if (ptg instanceof Ref3DPtg) {
|
||||
Ref3DPtg refPtg = (Ref3DPtg) ptg;
|
||||
short colnum = refPtg.getColumn();
|
||||
short rownum = refPtg.getRow();
|
||||
Workbook wb = workbook.getWorkbook();
|
||||
HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(ptg.getExternSheetIndex()));
|
||||
HSSFSheet xsheet = workbook.getSheetAt(wb.getSheetIndexFromExternSheetIndex(refPtg.getExternSheetIndex()));
|
||||
HSSFRow row = xsheet.getRow(rownum);
|
||||
HSSFCell cell = (row != null) ? row.getCell(colnum) : null;
|
||||
stack.push(createRef3DEval(ptg, cell, row, xsheet, workbook));
|
||||
stack.push(createRef3DEval(refPtg, cell, row, xsheet, workbook));
|
||||
}
|
||||
else if (ptgs[i] instanceof AreaPtg) {
|
||||
AreaPtg ap = (AreaPtg) ptgs[i];
|
||||
else if (ptg instanceof AreaPtg) {
|
||||
AreaPtg ap = (AreaPtg) ptg;
|
||||
AreaEval ae = evaluateAreaPtg(sheet, workbook, ap);
|
||||
stack.push(ae);
|
||||
}
|
||||
else if (ptgs[i] instanceof Area3DPtg) {
|
||||
Area3DPtg a3dp = (Area3DPtg) ptgs[i];
|
||||
else if (ptg instanceof Area3DPtg) {
|
||||
Area3DPtg a3dp = (Area3DPtg) ptg;
|
||||
AreaEval ae = evaluateArea3dPtg(workbook, a3dp);
|
||||
stack.push(ae);
|
||||
}
|
||||
else {
|
||||
Eval ptgEval = getEvalForPtg(ptgs[i]);
|
||||
Eval ptgEval = getEvalForPtg(ptg);
|
||||
stack.push(ptgEval);
|
||||
}
|
||||
}
|
||||
|
||||
ValueEval value = ((ValueEval) stack.pop());
|
||||
if (value instanceof RefEval) {
|
||||
RefEval rv = (RefEval) value;
|
||||
if (!stack.isEmpty()) {
|
||||
throw new IllegalStateException("evaluation stack not empty");
|
||||
}
|
||||
value = dereferenceValue(value, srcRowNum, srcColNum);
|
||||
if (value instanceof BlankEval) {
|
||||
// Note Excel behaviour here. A blank final final value is converted to zero.
|
||||
return NumberEval.ZERO;
|
||||
// Formulas _never_ evaluate to blank. If a formula appears to have evaluated to
|
||||
// blank, the actual value is empty string. This can be verified with ISBLANK().
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dereferences a single value from any AreaEval or RefEval evaluation result.
|
||||
* If the supplied evaluationResult is just a plain value, it is returned as-is.
|
||||
* @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
|
||||
* <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
|
||||
*/
|
||||
private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) {
|
||||
if (evaluationResult instanceof RefEval) {
|
||||
RefEval rv = (RefEval) evaluationResult;
|
||||
return rv.getInnerValueEval();
|
||||
}
|
||||
if (value instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) value;
|
||||
if (evaluationResult instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) evaluationResult;
|
||||
if (ae.isRow()) {
|
||||
if(ae.isColumn()) {
|
||||
return ae.getValues()[0];
|
||||
@ -460,7 +490,7 @@ public class HSSFFormulaEvaluator {
|
||||
}
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return value;
|
||||
return evaluationResult;
|
||||
}
|
||||
|
||||
private static Eval invokeOperation(OperationEval operation, Eval[] ops, int srcRowNum, short srcColNum,
|
||||
@ -486,7 +516,7 @@ public class HSSFFormulaEvaluator {
|
||||
// (eg C:C)
|
||||
// TODO: Handle whole column ranges properly
|
||||
if(row1 == -1 && row0 >= 0) {
|
||||
row1 = (short)sheet.getLastRowNum();
|
||||
row1 = (short)sheet.getLastRowNum();
|
||||
}
|
||||
|
||||
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
|
||||
@ -514,7 +544,7 @@ public class HSSFFormulaEvaluator {
|
||||
// (eg C:C)
|
||||
// TODO: Handle whole column ranges properly
|
||||
if(row1 == -1 && row0 >= 0) {
|
||||
row1 = (short)xsheet.getLastRowNum();
|
||||
row1 = (short)xsheet.getLastRowNum();
|
||||
}
|
||||
|
||||
ValueEval[] values = new ValueEval[(row1 - row0 + 1) * (col1 - col0 + 1)];
|
||||
@ -631,22 +661,22 @@ public class HSSFFormulaEvaluator {
|
||||
private static Ref2DEval createRef2DEval(ReferencePtg ptg, HSSFCell cell,
|
||||
HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
|
||||
if (cell == null) {
|
||||
return new Ref2DEval(ptg, BlankEval.INSTANCE, false);
|
||||
return new Ref2DEval(ptg, BlankEval.INSTANCE);
|
||||
}
|
||||
|
||||
switch (cell.getCellType()) {
|
||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
||||
return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()), false);
|
||||
return new Ref2DEval(ptg, new NumberEval(cell.getNumericCellValue()));
|
||||
case HSSFCell.CELL_TYPE_STRING:
|
||||
return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()), false);
|
||||
return new Ref2DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
|
||||
case HSSFCell.CELL_TYPE_FORMULA:
|
||||
return new Ref2DEval(ptg, internalEvaluate(cell, row, sheet, workbook), true);
|
||||
return new Ref2DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
|
||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
||||
return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()), false);
|
||||
return new Ref2DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
|
||||
case HSSFCell.CELL_TYPE_BLANK:
|
||||
return new Ref2DEval(ptg, BlankEval.INSTANCE, false);
|
||||
return new Ref2DEval(ptg, BlankEval.INSTANCE);
|
||||
case HSSFCell.CELL_TYPE_ERROR:
|
||||
return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()), false);
|
||||
return new Ref2DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
|
||||
}
|
||||
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
||||
}
|
||||
@ -657,21 +687,21 @@ public class HSSFFormulaEvaluator {
|
||||
private static Ref3DEval createRef3DEval(Ref3DPtg ptg, HSSFCell cell,
|
||||
HSSFRow row, HSSFSheet sheet, HSSFWorkbook workbook) {
|
||||
if (cell == null) {
|
||||
return new Ref3DEval(ptg, BlankEval.INSTANCE, false);
|
||||
return new Ref3DEval(ptg, BlankEval.INSTANCE);
|
||||
}
|
||||
switch (cell.getCellType()) {
|
||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
||||
return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()), false);
|
||||
return new Ref3DEval(ptg, new NumberEval(cell.getNumericCellValue()));
|
||||
case HSSFCell.CELL_TYPE_STRING:
|
||||
return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()), false);
|
||||
return new Ref3DEval(ptg, new StringEval(cell.getRichStringCellValue().getString()));
|
||||
case HSSFCell.CELL_TYPE_FORMULA:
|
||||
return new Ref3DEval(ptg, internalEvaluate(cell, row, sheet, workbook), true);
|
||||
return new Ref3DEval(ptg, internalEvaluate(cell, row, sheet, workbook));
|
||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
||||
return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()), false);
|
||||
return new Ref3DEval(ptg, BoolEval.valueOf(cell.getBooleanCellValue()));
|
||||
case HSSFCell.CELL_TYPE_BLANK:
|
||||
return new Ref3DEval(ptg, BlankEval.INSTANCE, false);
|
||||
return new Ref3DEval(ptg, BlankEval.INSTANCE);
|
||||
case HSSFCell.CELL_TYPE_ERROR:
|
||||
return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()), false);
|
||||
return new Ref3DEval(ptg, ErrorEval.valueOf(cell.getErrorCellValue()));
|
||||
}
|
||||
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
||||
}
|
||||
|
@ -16,14 +16,14 @@
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.eventusermodel;
|
||||
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
|
||||
import org.apache.poi.hssf.eventusermodel.HSSFListener;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
|
||||
import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
|
||||
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
|
||||
import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord;
|
||||
@ -31,17 +31,15 @@ import org.apache.poi.hssf.record.LabelSSTRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.RowRecord;
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
/**
|
||||
* Tests for MissingRecordAwareHSSFListener
|
||||
*/
|
||||
public final class TestMissingRecordAwareHSSFListener extends TestCase {
|
||||
|
||||
import junit.framework.TestCase;
|
||||
private Record[] r;
|
||||
|
||||
public class TestMissingRecordAwareHSSFListener extends TestCase {
|
||||
private String dirname;
|
||||
|
||||
public TestMissingRecordAwareHSSFListener() {
|
||||
dirname = System.getProperty("HSSF.testdata.path");
|
||||
}
|
||||
|
||||
public void testMissingRowRecords() throws Exception {
|
||||
public void setUp() {
|
||||
String dirname = System.getProperty("HSSF.testdata.path");
|
||||
File f = new File(dirname + "/MissingBits.xls");
|
||||
|
||||
HSSFRequest req = new HSSFRequest();
|
||||
@ -49,13 +47,17 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
|
||||
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
|
||||
req.addListenerForAllRecords(listener);
|
||||
|
||||
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
|
||||
HSSFEventFactory factory = new HSSFEventFactory();
|
||||
factory.processWorkbookEvents(req, fs);
|
||||
try {
|
||||
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
|
||||
factory.processWorkbookEvents(req, fs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
r = mockListen.getRecords();
|
||||
}
|
||||
|
||||
// Check we got the dummy records
|
||||
Record[] r = (Record[])
|
||||
mockListen.records.toArray(new Record[mockListen.records.size()]);
|
||||
public void testMissingRowRecords() throws Exception {
|
||||
|
||||
// We have rows 0, 1, 2, 20 and 21
|
||||
int row0 = -1;
|
||||
@ -105,20 +107,6 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
|
||||
}
|
||||
|
||||
public void testEndOfRowRecords() throws Exception {
|
||||
File f = new File(dirname + "/MissingBits.xls");
|
||||
|
||||
HSSFRequest req = new HSSFRequest();
|
||||
MockHSSFListener mockListen = new MockHSSFListener();
|
||||
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
|
||||
req.addListenerForAllRecords(listener);
|
||||
|
||||
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
|
||||
HSSFEventFactory factory = new HSSFEventFactory();
|
||||
factory.processWorkbookEvents(req, fs);
|
||||
|
||||
// Check we got the dummy records
|
||||
Record[] r = (Record[])
|
||||
mockListen.records.toArray(new Record[mockListen.records.size()]);
|
||||
|
||||
// Find the cell at 0,0
|
||||
int cell00 = -1;
|
||||
@ -240,20 +228,6 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
|
||||
|
||||
|
||||
public void testMissingCellRecords() throws Exception {
|
||||
File f = new File(dirname + "/MissingBits.xls");
|
||||
|
||||
HSSFRequest req = new HSSFRequest();
|
||||
MockHSSFListener mockListen = new MockHSSFListener();
|
||||
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(mockListen);
|
||||
req.addListenerForAllRecords(listener);
|
||||
|
||||
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(f));
|
||||
HSSFEventFactory factory = new HSSFEventFactory();
|
||||
factory.processWorkbookEvents(req, fs);
|
||||
|
||||
// Check we got the dummy records
|
||||
Record[] r = (Record[])
|
||||
mockListen.records.toArray(new Record[mockListen.records.size()]);
|
||||
|
||||
// Find the cell at 0,0
|
||||
int cell00 = -1;
|
||||
@ -352,25 +326,35 @@ public class TestMissingRecordAwareHSSFListener extends TestCase {
|
||||
assertEquals(10, mc.getColumn());
|
||||
}
|
||||
|
||||
private static class MockHSSFListener implements HSSFListener {
|
||||
private MockHSSFListener() {}
|
||||
private ArrayList records = new ArrayList();
|
||||
private static final class MockHSSFListener implements HSSFListener {
|
||||
public MockHSSFListener() {}
|
||||
private final List _records = new ArrayList();
|
||||
|
||||
public void processRecord(Record record) {
|
||||
records.add(record);
|
||||
_records.add(record);
|
||||
|
||||
if(record instanceof MissingRowDummyRecord) {
|
||||
MissingRowDummyRecord mr = (MissingRowDummyRecord)record;
|
||||
System.out.println("Got dummy row " + mr.getRowNumber());
|
||||
log("Got dummy row " + mr.getRowNumber());
|
||||
}
|
||||
if(record instanceof MissingCellDummyRecord) {
|
||||
MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
|
||||
System.out.println("Got dummy cell " + mc.getRow() + " " + mc.getColumn());
|
||||
log("Got dummy cell " + mc.getRow() + " " + mc.getColumn());
|
||||
}
|
||||
if(record instanceof LastCellOfRowDummyRecord) {
|
||||
LastCellOfRowDummyRecord lc = (LastCellOfRowDummyRecord)record;
|
||||
System.out.println("Got end-of row, row was " + lc.getRow() + ", last column was " + lc.getLastColumnNumber());
|
||||
log("Got end-of row, row was " + lc.getRow() + ", last column was " + lc.getLastColumnNumber());
|
||||
}
|
||||
}
|
||||
private static void log(String msg) {
|
||||
if(false) { // successful tests should be quiet
|
||||
System.out.println(msg);
|
||||
}
|
||||
}
|
||||
public Record[] getRecords() {
|
||||
Record[] result = new Record[_records.size()];
|
||||
_records.toArray(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -31,10 +31,13 @@ public final class AllIndividualFunctionEvaluationTests {
|
||||
// TODO - have this suite incorporated into a higher level one
|
||||
public static Test suite() {
|
||||
TestSuite result = new TestSuite("Tests for org.apache.poi.hssf.record.formula.functions");
|
||||
result.addTestSuite(TestAverage.class);
|
||||
result.addTestSuite(TestCountFuncs.class);
|
||||
result.addTestSuite(TestDate.class);
|
||||
result.addTestSuite(TestFinanceLib.class);
|
||||
result.addTestSuite(TestIndex.class);
|
||||
result.addTestSuite(TestIsBlank.class);
|
||||
result.addTestSuite(TestLen.class);
|
||||
result.addTestSuite(TestMid.class);
|
||||
result.addTestSuite(TestMathX.class);
|
||||
result.addTestSuite(TestMatch.class);
|
||||
@ -43,6 +46,8 @@ public final class AllIndividualFunctionEvaluationTests {
|
||||
result.addTestSuite(TestSumproduct.class);
|
||||
result.addTestSuite(TestStatsLib.class);
|
||||
result.addTestSuite(TestTFunc.class);
|
||||
result.addTestSuite(TestTrim.class);
|
||||
result.addTestSuite(TestXYNumericFunction.class);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,6 @@ final class EvalFactory {
|
||||
* Creates a single RefEval (with value zero)
|
||||
*/
|
||||
public static RefEval createRefEval(String refStr) {
|
||||
return new Ref2DEval(new ReferencePtg(refStr), ZERO, true);
|
||||
return new Ref2DEval(new ReferencePtg(refStr), ZERO);
|
||||
}
|
||||
}
|
||||
|
@ -111,9 +111,6 @@ public final class NumericFunctionInvoker {
|
||||
if(errorCodesAreEqual(ee, ErrorEval.FUNCTION_NOT_IMPLEMENTED)) {
|
||||
return "Function not implemented";
|
||||
}
|
||||
if(errorCodesAreEqual(ee, ErrorEval.UNKNOWN_ERROR)) {
|
||||
return "Unknown error";
|
||||
}
|
||||
if(errorCodesAreEqual(ee, ErrorEval.VALUE_INVALID)) {
|
||||
return "Error code: #VALUE! (invalid value)";
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
@ -125,7 +125,7 @@ public final class TestCountFuncs extends TestCase {
|
||||
};
|
||||
Area2DEval arg0 = new Area2DEval(new AreaPtg("C1:C6"), values);
|
||||
|
||||
Ref2DEval criteriaArg = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(25), true);
|
||||
Ref2DEval criteriaArg = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(25));
|
||||
Eval[] args= { arg0, criteriaArg, };
|
||||
|
||||
double actual = NumericFunctionInvoker.invoke(new Countif(), args);
|
||||
|
@ -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());
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -77,13 +77,14 @@ public final class TestMid extends TestCase {
|
||||
|
||||
// startPos is 1x1 area ref, numChars is cell ref
|
||||
AreaEval aeStart = new Area2DEval(new AreaPtg("A1:A1"), new ValueEval[] { new NumberEval(2), } );
|
||||
RefEval reNumChars = new Ref2DEval(new ReferencePtg("B1"), new NumberEval(3),false);
|
||||
RefEval reNumChars = new Ref2DEval(new ReferencePtg("B1"), new NumberEval(3));
|
||||
confirmMid(new StringEval("galactic"), aeStart, reNumChars, "ala");
|
||||
|
||||
confirmMid(new StringEval("galactic"), new NumberEval(3.1), BlankEval.INSTANCE, "");
|
||||
|
||||
confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.FALSE, "");
|
||||
confirmMid(new StringEval("galactic"), new NumberEval(3), BoolEval.TRUE, "l");
|
||||
confirmMid(BlankEval.INSTANCE, new NumberEval(3), BoolEval.TRUE, "");
|
||||
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ public final class TestSumproduct extends TestCase {
|
||||
|
||||
public void testScalarSimple() {
|
||||
|
||||
RefEval refEval = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(3), true);
|
||||
RefEval refEval = new Ref2DEval(new ReferencePtg("A1"), new NumberEval(3));
|
||||
Eval[] args = {
|
||||
refEval,
|
||||
new NumberEval(2),
|
||||
|
@ -50,7 +50,7 @@ public final class TestTFunc extends TestCase {
|
||||
* where cell A1 has the specified innerValue
|
||||
*/
|
||||
private Eval invokeTWithReference(ValueEval innerValue) {
|
||||
Eval arg = new Ref2DEval(new ReferencePtg((short)1, (short)1, false, false), innerValue, true);
|
||||
Eval arg = new Ref2DEval(new ReferencePtg((short)1, (short)1, false, false), innerValue);
|
||||
return invokeT(arg);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
@ -25,80 +25,8 @@ import org.apache.poi.hssf.eventmodel.TestModelFactory;
|
||||
import org.apache.poi.hssf.model.TestDrawingManager;
|
||||
import org.apache.poi.hssf.model.TestFormulaParser;
|
||||
import org.apache.poi.hssf.model.TestSheet;
|
||||
import org.apache.poi.hssf.record.TestAreaFormatRecord;
|
||||
import org.apache.poi.hssf.record.TestAreaRecord;
|
||||
import org.apache.poi.hssf.record.TestAxisLineFormatRecord;
|
||||
import org.apache.poi.hssf.record.TestAxisOptionsRecord;
|
||||
import org.apache.poi.hssf.record.TestAxisParentRecord;
|
||||
import org.apache.poi.hssf.record.TestAxisRecord;
|
||||
import org.apache.poi.hssf.record.TestAxisUsedRecord;
|
||||
import org.apache.poi.hssf.record.TestBarRecord;
|
||||
import org.apache.poi.hssf.record.TestBoundSheetRecord;
|
||||
import org.apache.poi.hssf.record.TestCategorySeriesAxisRecord;
|
||||
import org.apache.poi.hssf.record.TestChartRecord;
|
||||
import org.apache.poi.hssf.record.TestDatRecord;
|
||||
import org.apache.poi.hssf.record.TestDataFormatRecord;
|
||||
import org.apache.poi.hssf.record.TestDefaultDataLabelTextPropertiesRecord;
|
||||
import org.apache.poi.hssf.record.TestFontBasisRecord;
|
||||
import org.apache.poi.hssf.record.TestFontIndexRecord;
|
||||
import org.apache.poi.hssf.record.TestFormulaRecord;
|
||||
import org.apache.poi.hssf.record.TestFrameRecord;
|
||||
import org.apache.poi.hssf.record.TestLegendRecord;
|
||||
import org.apache.poi.hssf.record.TestLineFormatRecord;
|
||||
import org.apache.poi.hssf.record.TestLinkedDataRecord;
|
||||
import org.apache.poi.hssf.record.TestNameRecord;
|
||||
import org.apache.poi.hssf.record.TestNumberFormatIndexRecord;
|
||||
import org.apache.poi.hssf.record.TestObjectLinkRecord;
|
||||
import org.apache.poi.hssf.record.TestPaletteRecord;
|
||||
import org.apache.poi.hssf.record.TestPlotAreaRecord;
|
||||
import org.apache.poi.hssf.record.TestPlotGrowthRecord;
|
||||
import org.apache.poi.hssf.record.TestRecordFactory;
|
||||
import org.apache.poi.hssf.record.TestSCLRecord;
|
||||
import org.apache.poi.hssf.record.TestSSTDeserializer;
|
||||
import org.apache.poi.hssf.record.TestSSTRecord;
|
||||
import org.apache.poi.hssf.record.TestSSTRecordSizeCalculator;
|
||||
import org.apache.poi.hssf.record.TestSeriesChartGroupIndexRecord;
|
||||
import org.apache.poi.hssf.record.TestSeriesIndexRecord;
|
||||
import org.apache.poi.hssf.record.TestSeriesLabelsRecord;
|
||||
import org.apache.poi.hssf.record.TestSeriesListRecord;
|
||||
import org.apache.poi.hssf.record.TestSeriesRecord;
|
||||
import org.apache.poi.hssf.record.TestSeriesTextRecord;
|
||||
import org.apache.poi.hssf.record.TestSeriesToChartGroupRecord;
|
||||
import org.apache.poi.hssf.record.TestSheetPropertiesRecord;
|
||||
import org.apache.poi.hssf.record.TestStringRecord;
|
||||
import org.apache.poi.hssf.record.TestSupBookRecord;
|
||||
import org.apache.poi.hssf.record.TestTextRecord;
|
||||
import org.apache.poi.hssf.record.TestTickRecord;
|
||||
import org.apache.poi.hssf.record.TestUnicodeString;
|
||||
import org.apache.poi.hssf.record.TestUnitsRecord;
|
||||
import org.apache.poi.hssf.record.TestValueRangeRecord;
|
||||
import org.apache.poi.hssf.record.aggregates.TestRowRecordsAggregate;
|
||||
import org.apache.poi.hssf.record.aggregates.TestValueRecordsAggregate;
|
||||
import org.apache.poi.hssf.record.formula.AllFormulaTests;
|
||||
import org.apache.poi.hssf.usermodel.TestBugs;
|
||||
import org.apache.poi.hssf.usermodel.TestCellStyle;
|
||||
import org.apache.poi.hssf.usermodel.TestCloneSheet;
|
||||
import org.apache.poi.hssf.usermodel.TestEscherGraphics;
|
||||
import org.apache.poi.hssf.usermodel.TestEscherGraphics2d;
|
||||
import org.apache.poi.hssf.usermodel.TestFontDetails;
|
||||
import org.apache.poi.hssf.usermodel.TestFormulas;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFClientAnchor;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFComment;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFDateUtil;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFHeaderFooter;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFPalette;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFRichTextString;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFSheetOrder;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFSheetSetOrder;
|
||||
import org.apache.poi.hssf.usermodel.TestHSSFWorkbook;
|
||||
import org.apache.poi.hssf.usermodel.TestNamedRange;
|
||||
import org.apache.poi.hssf.usermodel.TestReadWriteChart;
|
||||
import org.apache.poi.hssf.usermodel.TestSanityChecker;
|
||||
import org.apache.poi.hssf.usermodel.TestSheetShiftRows;
|
||||
import org.apache.poi.hssf.usermodel.TestWorkbook;
|
||||
import org.apache.poi.hssf.record.AllRecordTests;
|
||||
import org.apache.poi.hssf.usermodel.AllUserModelTests;
|
||||
import org.apache.poi.hssf.util.TestAreaReference;
|
||||
import org.apache.poi.hssf.util.TestCellReference;
|
||||
import org.apache.poi.hssf.util.TestRKUtil;
|
||||
@ -112,113 +40,30 @@ import org.apache.poi.hssf.util.TestSheetReferences;
|
||||
*
|
||||
* @author Andrew C. Oliver acoliver@apache.org
|
||||
*/
|
||||
public final class HSSFTests
|
||||
{
|
||||
public final class HSSFTests {
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(suite());
|
||||
}
|
||||
|
||||
public static Test suite()
|
||||
{
|
||||
TestSuite suite =
|
||||
new TestSuite("Tests for org.apache.poi.hssf");
|
||||
//$JUnit-BEGIN$
|
||||
|
||||
suite.addTest(new TestSuite(TestBugs.class));
|
||||
suite.addTest(new TestSuite(TestCloneSheet.class));
|
||||
suite.addTest(new TestSuite(TestEscherGraphics.class));
|
||||
suite.addTest(new TestSuite(TestEscherGraphics2d.class));
|
||||
suite.addTest(new TestSuite(TestFontDetails.class));
|
||||
suite.addTest(new TestSuite(TestHSSFClientAnchor.class));
|
||||
suite.addTest(new TestSuite(TestHSSFHeaderFooter.class));
|
||||
suite.addTest(new TestSuite(TestHSSFRichTextString.class));
|
||||
suite.addTest(new TestSuite(TestHSSFSheetOrder.class));
|
||||
suite.addTest(new TestSuite(TestHSSFSheetSetOrder.class));
|
||||
suite.addTest(new TestSuite(TestHSSFWorkbook.class));
|
||||
suite.addTest(new TestSuite(TestSanityChecker.class));
|
||||
suite.addTest(new TestSuite(TestSheetShiftRows.class));
|
||||
|
||||
suite.addTest(new TestSuite(TestCellStyle.class));
|
||||
suite.addTest(new TestSuite(TestFormulas.class));
|
||||
suite.addTest(new TestSuite(TestHSSFCell.class));
|
||||
suite.addTest(new TestSuite(TestHSSFDateUtil.class));
|
||||
suite.addTest(new TestSuite(TestHSSFPalette.class));
|
||||
suite.addTest(new TestSuite(TestHSSFRow.class));
|
||||
suite.addTest(new TestSuite(TestHSSFSheet.class));
|
||||
suite.addTest(new TestSuite(TestNamedRange.class));
|
||||
suite.addTest(new TestSuite(TestReadWriteChart.class));
|
||||
suite.addTest(new TestSuite(TestWorkbook.class));
|
||||
|
||||
public static Test suite() {
|
||||
TestSuite suite = new TestSuite("Tests for org.apache.poi.hssf");
|
||||
// $JUnit-BEGIN$
|
||||
|
||||
suite.addTest(AllUserModelTests.suite());
|
||||
suite.addTest(AllRecordTests.suite());
|
||||
|
||||
suite.addTest(new TestSuite(TestFormulaParser.class));
|
||||
suite.addTest(new TestSuite(TestAreaFormatRecord.class));
|
||||
suite.addTest(new TestSuite(TestAreaRecord.class));
|
||||
suite.addTest(new TestSuite(TestAxisLineFormatRecord.class));
|
||||
suite.addTest(new TestSuite(TestAxisOptionsRecord.class));
|
||||
suite.addTest(new TestSuite(TestAxisParentRecord.class));
|
||||
suite.addTest(new TestSuite(TestAxisRecord.class));
|
||||
suite.addTest(new TestSuite(TestAxisUsedRecord.class));
|
||||
suite.addTest(new TestSuite(TestBarRecord.class));
|
||||
suite.addTest(new TestSuite(TestBoundSheetRecord.class));
|
||||
suite.addTest(new TestSuite(TestCategorySeriesAxisRecord.class));
|
||||
suite.addTest(new TestSuite(TestChartRecord.class));
|
||||
suite.addTest(new TestSuite(TestDatRecord.class));
|
||||
suite.addTest(new TestSuite(TestDataFormatRecord.class));
|
||||
suite.addTest(
|
||||
new TestSuite(TestDefaultDataLabelTextPropertiesRecord.class));
|
||||
suite.addTest(new TestSuite(TestFontBasisRecord.class));
|
||||
suite.addTest(new TestSuite(TestFontIndexRecord.class));
|
||||
suite.addTest(new TestSuite(TestFormulaRecord.class));
|
||||
suite.addTest(new TestSuite(TestFrameRecord.class));
|
||||
suite.addTest(new TestSuite(TestLegendRecord.class));
|
||||
suite.addTest(new TestSuite(TestLineFormatRecord.class));
|
||||
suite.addTest(new TestSuite(TestLinkedDataRecord.class));
|
||||
suite.addTest(new TestSuite(TestNumberFormatIndexRecord.class));
|
||||
suite.addTest(new TestSuite(TestObjectLinkRecord.class));
|
||||
suite.addTest(new TestSuite(TestPaletteRecord.class));
|
||||
suite.addTest(new TestSuite(TestPlotAreaRecord.class));
|
||||
suite.addTest(new TestSuite(TestPlotGrowthRecord.class));
|
||||
suite.addTest(new TestSuite(TestRecordFactory.class));
|
||||
suite.addTest(new TestSuite(TestSCLRecord.class));
|
||||
suite.addTest(new TestSuite(TestSSTDeserializer.class));
|
||||
suite.addTest(new TestSuite(TestSSTRecord.class));
|
||||
suite.addTest(new TestSuite(TestSSTRecordSizeCalculator.class));
|
||||
suite.addTest(new TestSuite(TestSeriesChartGroupIndexRecord.class));
|
||||
suite.addTest(new TestSuite(TestSeriesIndexRecord.class));
|
||||
suite.addTest(new TestSuite(TestSeriesLabelsRecord.class));
|
||||
suite.addTest(new TestSuite(TestSeriesListRecord.class));
|
||||
suite.addTest(new TestSuite(TestSeriesRecord.class));
|
||||
suite.addTest(new TestSuite(TestSeriesTextRecord.class));
|
||||
suite.addTest(new TestSuite(TestSeriesToChartGroupRecord.class));
|
||||
suite.addTest(new TestSuite(TestSheetPropertiesRecord.class));
|
||||
suite.addTest(new TestSuite(TestStringRecord.class));
|
||||
suite.addTest(new TestSuite(TestSupBookRecord.class));
|
||||
suite.addTest(new TestSuite(TestTextRecord.class));
|
||||
suite.addTest(new TestSuite(TestTickRecord.class));
|
||||
suite.addTest(new TestSuite(TestUnicodeString.class));
|
||||
suite.addTest(new TestSuite(TestUnitsRecord.class));
|
||||
suite.addTest(new TestSuite(TestValueRangeRecord.class));
|
||||
suite.addTest(new TestSuite(TestRowRecordsAggregate.class));
|
||||
suite.addTest(new TestSuite(TestAreaReference.class));
|
||||
suite.addTest(new TestSuite(TestCellReference.class));
|
||||
suite.addTest(new TestSuite(TestRangeAddress.class));
|
||||
suite.addTest(new TestSuite(TestRangeAddress.class));
|
||||
suite.addTest(new TestSuite(TestRKUtil.class));
|
||||
suite.addTest(new TestSuite(TestSheetReferences.class));
|
||||
|
||||
|
||||
suite.addTest(AllFormulaTests.suite());
|
||||
suite.addTest(new TestSuite(TestValueRecordsAggregate.class));
|
||||
suite.addTest(new TestSuite(TestNameRecord.class));
|
||||
suite.addTest(new TestSuite(TestEventRecordFactory.class));
|
||||
suite.addTest(new TestSuite(TestModelFactory.class));
|
||||
suite.addTest(new TestSuite(TestDrawingManager.class));
|
||||
suite.addTest(new TestSuite(TestSheet.class));
|
||||
|
||||
suite.addTest(new TestSuite(TestHSSFComment.class));
|
||||
//$JUnit-END$
|
||||
suite.addTest(new TestSuite(TestEventRecordFactory.class));
|
||||
suite.addTest(new TestSuite(TestModelFactory.class));
|
||||
suite.addTest(new TestSuite(TestDrawingManager.class));
|
||||
suite.addTest(new TestSuite(TestSheet.class));
|
||||
// $JUnit-END$
|
||||
return suite;
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
BIN
src/testcases/org/apache/poi/hssf/data/externalFunctionExample.xls
Executable file
BIN
src/testcases/org/apache/poi/hssf/data/externalFunctionExample.xls
Executable file
Binary file not shown.
BIN
src/testcases/org/apache/poi/hssf/data/logoKarmokar4.png
Executable file
BIN
src/testcases/org/apache/poi/hssf/data/logoKarmokar4.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@ -18,27 +18,34 @@
|
||||
|
||||
package org.apache.poi.hssf.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
|
||||
import org.apache.poi.hssf.record.formula.AddPtg;
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||
import org.apache.poi.hssf.record.formula.BoolPtg;
|
||||
import org.apache.poi.hssf.record.formula.ConcatPtg;
|
||||
import org.apache.poi.hssf.record.formula.DividePtg;
|
||||
import org.apache.poi.hssf.record.formula.EqualPtg;
|
||||
import org.apache.poi.hssf.record.formula.ErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.IntPtg;
|
||||
import org.apache.poi.hssf.record.formula.LessEqualPtg;
|
||||
import org.apache.poi.hssf.record.formula.LessThanPtg;
|
||||
import org.apache.poi.hssf.record.formula.MissingArgPtg;
|
||||
import org.apache.poi.hssf.record.formula.MultiplyPtg;
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.NotEqualPtg;
|
||||
import org.apache.poi.hssf.record.formula.NumberPtg;
|
||||
import org.apache.poi.hssf.record.formula.PercentPtg;
|
||||
import org.apache.poi.hssf.record.formula.PowerPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.ReferencePtg;
|
||||
import org.apache.poi.hssf.record.formula.StringPtg;
|
||||
import org.apache.poi.hssf.record.formula.SubtractPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
@ -64,15 +71,25 @@ public class TestFormulaParser extends TestCase {
|
||||
public void tearDown() {
|
||||
|
||||
}
|
||||
/**
|
||||
* @return parsed token array already confirmed not <code>null</code>
|
||||
*/
|
||||
private static Ptg[] parseFormula(String s) {
|
||||
FormulaParser fp = new FormulaParser(s, null);
|
||||
fp.parse();
|
||||
Ptg[] result = fp.getRPNPtg();
|
||||
assertNotNull("Ptg array should not be null", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void testSimpleFormula() {
|
||||
FormulaParser fp = new FormulaParser("2+2;",null);
|
||||
FormulaParser fp = new FormulaParser("2+2",null);
|
||||
fp.parse();
|
||||
Ptg[] ptgs = fp.getRPNPtg();
|
||||
assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
|
||||
}
|
||||
public void testFormulaWithSpace1() {
|
||||
FormulaParser fp = new FormulaParser(" 2 + 2 ;",null);
|
||||
FormulaParser fp = new FormulaParser(" 2 + 2 ",null);
|
||||
fp.parse();
|
||||
Ptg[] ptgs = fp.getRPNPtg();
|
||||
assertTrue("three tokens expected, got "+ptgs.length,ptgs.length == 3);
|
||||
@ -85,7 +102,7 @@ public class TestFormulaParser extends TestCase {
|
||||
public void testFormulaWithSpace2() {
|
||||
Ptg[] ptgs;
|
||||
FormulaParser fp;
|
||||
fp = new FormulaParser("2+ sum( 3 , 4) ;",null);
|
||||
fp = new FormulaParser("2+ sum( 3 , 4) ",null);
|
||||
fp.parse();
|
||||
ptgs = fp.getRPNPtg();
|
||||
assertTrue("five tokens expected, got "+ptgs.length,ptgs.length == 5);
|
||||
@ -94,7 +111,7 @@ public class TestFormulaParser extends TestCase {
|
||||
public void testFormulaWithSpaceNRef() {
|
||||
Ptg[] ptgs;
|
||||
FormulaParser fp;
|
||||
fp = new FormulaParser("sum( A2:A3 );",null);
|
||||
fp = new FormulaParser("sum( A2:A3 )",null);
|
||||
fp.parse();
|
||||
ptgs = fp.getRPNPtg();
|
||||
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
|
||||
@ -103,7 +120,7 @@ public class TestFormulaParser extends TestCase {
|
||||
public void testFormulaWithString() {
|
||||
Ptg[] ptgs;
|
||||
FormulaParser fp;
|
||||
fp = new FormulaParser("\"hello\" & \"world\" ;",null);
|
||||
fp = new FormulaParser("\"hello\" & \"world\" ",null);
|
||||
fp.parse();
|
||||
ptgs = fp.getRPNPtg();
|
||||
assertTrue("three token expected, got " + ptgs.length, ptgs.length == 3);
|
||||
@ -276,20 +293,21 @@ public class TestFormulaParser extends TestCase {
|
||||
}
|
||||
|
||||
public void testMacroFunction() {
|
||||
Workbook w = new Workbook();
|
||||
Workbook w = Workbook.createWorkbook();
|
||||
FormulaParser fp = new FormulaParser("FOO()", w);
|
||||
fp.parse();
|
||||
Ptg[] ptg = fp.getRPNPtg();
|
||||
|
||||
AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[0];
|
||||
assertEquals("externalflag", tfunc.getName());
|
||||
|
||||
NamePtg tname = (NamePtg) ptg[1];
|
||||
// the name gets encoded as the first arg
|
||||
NamePtg tname = (NamePtg) ptg[0];
|
||||
assertEquals("FOO", tname.toFormulaString(w));
|
||||
|
||||
AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1];
|
||||
assertEquals("externalflag", tfunc.getName());
|
||||
}
|
||||
|
||||
public void testEmbeddedSlash() {
|
||||
FormulaParser fp = new FormulaParser("HYPERLINK(\"http://www.jakarta.org\",\"Jakarta\");",null);
|
||||
FormulaParser fp = new FormulaParser("HYPERLINK(\"http://www.jakarta.org\",\"Jakarta\")",null);
|
||||
fp.parse();
|
||||
Ptg[] ptg = fp.getRPNPtg();
|
||||
assertTrue("first ptg is string",ptg[0] instanceof StringPtg);
|
||||
@ -589,4 +607,234 @@ public class TestFormulaParser extends TestCase {
|
||||
};
|
||||
assertEquals("NA()", FormulaParser.toFormulaString(book, ptgs));
|
||||
}
|
||||
|
||||
public void testPercent() {
|
||||
Ptg[] ptgs;
|
||||
ptgs = parseFormula("5%");
|
||||
assertEquals(2, ptgs.length);
|
||||
assertEquals(ptgs[0].getClass(), IntPtg.class);
|
||||
assertEquals(ptgs[1].getClass(), PercentPtg.class);
|
||||
|
||||
// spaces OK
|
||||
ptgs = parseFormula(" 250 % ");
|
||||
assertEquals(2, ptgs.length);
|
||||
assertEquals(ptgs[0].getClass(), IntPtg.class);
|
||||
assertEquals(ptgs[1].getClass(), PercentPtg.class);
|
||||
|
||||
|
||||
// double percent OK
|
||||
ptgs = parseFormula("12345.678%%");
|
||||
assertEquals(3, ptgs.length);
|
||||
assertEquals(ptgs[0].getClass(), NumberPtg.class);
|
||||
assertEquals(ptgs[1].getClass(), PercentPtg.class);
|
||||
assertEquals(ptgs[2].getClass(), PercentPtg.class);
|
||||
|
||||
// percent of a bracketed expression
|
||||
ptgs = parseFormula("(A1+35)%*B1%");
|
||||
assertEquals(8, ptgs.length);
|
||||
assertEquals(ptgs[4].getClass(), PercentPtg.class);
|
||||
assertEquals(ptgs[6].getClass(), PercentPtg.class);
|
||||
|
||||
// percent of a text quantity
|
||||
ptgs = parseFormula("\"8.75\"%");
|
||||
assertEquals(2, ptgs.length);
|
||||
assertEquals(ptgs[0].getClass(), StringPtg.class);
|
||||
assertEquals(ptgs[1].getClass(), PercentPtg.class);
|
||||
|
||||
// percent to the power of
|
||||
ptgs = parseFormula("50%^3");
|
||||
assertEquals(4, ptgs.length);
|
||||
assertEquals(ptgs[0].getClass(), IntPtg.class);
|
||||
assertEquals(ptgs[1].getClass(), PercentPtg.class);
|
||||
assertEquals(ptgs[2].getClass(), IntPtg.class);
|
||||
assertEquals(ptgs[3].getClass(), PowerPtg.class);
|
||||
|
||||
//
|
||||
// things that parse OK but would *evaluate* to an error
|
||||
|
||||
ptgs = parseFormula("\"abc\"%");
|
||||
assertEquals(2, ptgs.length);
|
||||
assertEquals(ptgs[0].getClass(), StringPtg.class);
|
||||
assertEquals(ptgs[1].getClass(), PercentPtg.class);
|
||||
|
||||
ptgs = parseFormula("#N/A%");
|
||||
assertEquals(2, ptgs.length);
|
||||
assertEquals(ptgs[0].getClass(), ErrPtg.class);
|
||||
assertEquals(ptgs[1].getClass(), PercentPtg.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests combinations of various operators in the absence of brackets
|
||||
*/
|
||||
public void testPrecedenceAndAssociativity() {
|
||||
|
||||
Class[] expClss;
|
||||
|
||||
// TRUE=TRUE=2=2 evaluates to FALSE
|
||||
expClss = new Class[] { BoolPtg.class, BoolPtg.class, EqualPtg.class,
|
||||
IntPtg.class, EqualPtg.class, IntPtg.class, EqualPtg.class, };
|
||||
confirmTokenClasses("TRUE=TRUE=2=2", expClss);
|
||||
|
||||
|
||||
// 2^3^2 evaluates to 64 not 512
|
||||
expClss = new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class,
|
||||
IntPtg.class, PowerPtg.class, };
|
||||
confirmTokenClasses("2^3^2", expClss);
|
||||
|
||||
// "abc" & 2 + 3 & "def" evaluates to "abc5def"
|
||||
expClss = new Class[] { StringPtg.class, IntPtg.class, IntPtg.class,
|
||||
AddPtg.class, ConcatPtg.class, StringPtg.class, ConcatPtg.class, };
|
||||
confirmTokenClasses("\"abc\"&2+3&\"def\"", expClss);
|
||||
|
||||
|
||||
// (1 / 2) - (3 * 4)
|
||||
expClss = new Class[] { IntPtg.class, IntPtg.class, DividePtg.class,
|
||||
IntPtg.class, IntPtg.class, MultiplyPtg.class, SubtractPtg.class, };
|
||||
confirmTokenClasses("1/2-3*4", expClss);
|
||||
|
||||
// 2 * (2^2)
|
||||
expClss = new Class[] { IntPtg.class, IntPtg.class, IntPtg.class, PowerPtg.class, MultiplyPtg.class, };
|
||||
// NOT: (2 *2) ^ 2 -> int int multiply int power
|
||||
confirmTokenClasses("2*2^2", expClss);
|
||||
|
||||
// 2^200% -> 2 not 1.6E58
|
||||
expClss = new Class[] { IntPtg.class, IntPtg.class, PercentPtg.class, PowerPtg.class, };
|
||||
confirmTokenClasses("2^200%", expClss);
|
||||
}
|
||||
|
||||
private static void confirmTokenClasses(String formula, Class[] expectedClasses) {
|
||||
Ptg[] ptgs = parseFormula(formula);
|
||||
assertEquals(expectedClasses.length, ptgs.length);
|
||||
for (int i = 0; i < expectedClasses.length; i++) {
|
||||
if(expectedClasses[i] != ptgs[i].getClass()) {
|
||||
fail("difference at token[" + i + "]: expected ("
|
||||
+ expectedClasses[i].getName() + ") but got ("
|
||||
+ ptgs[i].getClass().getName() + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testPower() {
|
||||
confirmTokenClasses("2^5", new Class[] { IntPtg.class, IntPtg.class, PowerPtg.class, });
|
||||
}
|
||||
|
||||
private static Ptg parseSingleToken(String formula, Class ptgClass) {
|
||||
Ptg[] ptgs = parseFormula(formula);
|
||||
assertEquals(1, ptgs.length);
|
||||
Ptg result = ptgs[0];
|
||||
assertEquals(ptgClass, result.getClass());
|
||||
return result;
|
||||
}
|
||||
|
||||
public void testParseNumber() {
|
||||
IntPtg ip;
|
||||
|
||||
// bug 33160
|
||||
ip = (IntPtg) parseSingleToken("40", IntPtg.class);
|
||||
assertEquals(40, ip.getValue());
|
||||
ip = (IntPtg) parseSingleToken("40000", IntPtg.class);
|
||||
assertEquals(40000, ip.getValue());
|
||||
|
||||
// check the upper edge of the IntPtg range:
|
||||
ip = (IntPtg) parseSingleToken("65535", IntPtg.class);
|
||||
assertEquals(65535, ip.getValue());
|
||||
NumberPtg np = (NumberPtg) parseSingleToken("65536", NumberPtg.class);
|
||||
assertEquals(65536, np.getValue(), 0);
|
||||
|
||||
np = (NumberPtg) parseSingleToken("65534.6", NumberPtg.class);
|
||||
assertEquals(65534.6, np.getValue(), 0);
|
||||
}
|
||||
|
||||
public void testMissingArgs() {
|
||||
|
||||
Class[] expClss;
|
||||
|
||||
expClss = new Class[] { ReferencePtg.class, MissingArgPtg.class, ReferencePtg.class,
|
||||
FuncVarPtg.class, };
|
||||
confirmTokenClasses("if(A1, ,C1)", expClss);
|
||||
|
||||
expClss = new Class[] { MissingArgPtg.class, AreaPtg.class, MissingArgPtg.class,
|
||||
FuncVarPtg.class, };
|
||||
confirmTokenClasses("counta( , A1:B2, )", expClss);
|
||||
}
|
||||
|
||||
public void testParseErrorLiterals() {
|
||||
|
||||
confirmParseErrorLiteral(ErrPtg.NULL_INTERSECTION, "#NULL!");
|
||||
confirmParseErrorLiteral(ErrPtg.DIV_ZERO, "#DIV/0!");
|
||||
confirmParseErrorLiteral(ErrPtg.VALUE_INVALID, "#VALUE!");
|
||||
confirmParseErrorLiteral(ErrPtg.REF_INVALID, "#REF!");
|
||||
confirmParseErrorLiteral(ErrPtg.NAME_INVALID, "#NAME?");
|
||||
confirmParseErrorLiteral(ErrPtg.NUM_ERROR, "#NUM!");
|
||||
confirmParseErrorLiteral(ErrPtg.N_A, "#N/A");
|
||||
}
|
||||
|
||||
private static void confirmParseErrorLiteral(ErrPtg expectedToken, String formula) {
|
||||
assertEquals(expectedToken, parseSingleToken(formula, ErrPtg.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* To aid readability the parameters have been encoded with single quotes instead of double
|
||||
* quotes. This method converts single quotes to double quotes before performing the parse
|
||||
* and result check.
|
||||
*/
|
||||
private static void confirmStringParse(String singleQuotedValue) {
|
||||
// formula: internal quotes become double double, surround with double quotes
|
||||
String formula = '"' + singleQuotedValue.replaceAll("'", "\"\"") + '"';
|
||||
String expectedValue = singleQuotedValue.replace('\'', '"');
|
||||
|
||||
StringPtg sp = (StringPtg) parseSingleToken(formula, StringPtg.class);
|
||||
assertEquals(expectedValue, sp.getValue());
|
||||
}
|
||||
|
||||
public void testPaseStringLiterals() {
|
||||
confirmStringParse("goto considered harmful");
|
||||
|
||||
confirmStringParse("goto 'considered' harmful");
|
||||
|
||||
confirmStringParse("");
|
||||
confirmStringParse("'");
|
||||
confirmStringParse("''");
|
||||
confirmStringParse("' '");
|
||||
confirmStringParse(" ' ");
|
||||
}
|
||||
|
||||
public void testParseSumIfSum() {
|
||||
String formulaString;
|
||||
Ptg[] ptgs;
|
||||
ptgs = parseFormula("sum(5, 2, if(3>2, sum(A1:A2), 6))");
|
||||
formulaString = FormulaParser.toFormulaString(null, ptgs);
|
||||
assertEquals("SUM(5,2,IF(3>2,SUM(A1:A2),6))", formulaString);
|
||||
|
||||
ptgs = parseFormula("if(1<2,sum(5, 2, if(3>2, sum(A1:A2), 6)),4)");
|
||||
formulaString = FormulaParser.toFormulaString(null, ptgs);
|
||||
assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString);
|
||||
}
|
||||
public void testParserErrors() {
|
||||
parseExpectedException("1 2");
|
||||
parseExpectedException(" 12 . 345 ");
|
||||
parseExpectedException("1 .23 ");
|
||||
|
||||
parseExpectedException("sum(#NAME)");
|
||||
parseExpectedException("1 + #N / A * 2");
|
||||
parseExpectedException("#value?");
|
||||
parseExpectedException("#DIV/ 0+2");
|
||||
|
||||
|
||||
if (false) { // TODO - add functionality to detect func arg count mismatch
|
||||
parseExpectedException("IF(TRUE)");
|
||||
parseExpectedException("countif(A1:B5, C1, D1)");
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseExpectedException(String formula) {
|
||||
try {
|
||||
parseFormula(formula);
|
||||
throw new AssertionFailedError("expected parse exception");
|
||||
} catch (RuntimeException e) {
|
||||
// TODO - catch more specific exception
|
||||
// expected during successful test
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
104
src/testcases/org/apache/poi/hssf/record/AllRecordTests.java
Executable file
104
src/testcases/org/apache/poi/hssf/record/AllRecordTests.java
Executable 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;
|
||||
}
|
||||
}
|
@ -29,15 +29,25 @@ import junit.framework.TestCase;
|
||||
*
|
||||
* @author Andrew C. Oliver (acoliver at apache dot org)
|
||||
*/
|
||||
public class TestSupBookRecord
|
||||
extends TestCase
|
||||
{
|
||||
public final class TestSupBookRecord extends TestCase {
|
||||
/**
|
||||
* This contains a fake data section of a SubBookRecord
|
||||
*/
|
||||
byte[] data = new byte[] {
|
||||
(byte)0x04,(byte)0x00,(byte)0x01,(byte)0x04
|
||||
byte[] dataIR = new byte[] {
|
||||
(byte)0x04,(byte)0x00,(byte)0x01,(byte)0x04,
|
||||
};
|
||||
byte[] dataAIF = new byte[] {
|
||||
(byte)0x01,(byte)0x00,(byte)0x01,(byte)0x3A,
|
||||
};
|
||||
byte[] dataER = new byte[] {
|
||||
(byte)0x02,(byte)0x00,
|
||||
(byte)0x07,(byte)0x00, (byte)0x00,
|
||||
(byte)'t', (byte)'e', (byte)'s', (byte)'t', (byte)'U', (byte)'R', (byte)'L',
|
||||
(byte)0x06,(byte)0x00, (byte)0x00,
|
||||
(byte)'S', (byte)'h', (byte)'e', (byte)'e', (byte)'t', (byte)'1',
|
||||
(byte)0x06,(byte)0x00, (byte)0x00,
|
||||
(byte)'S', (byte)'h', (byte)'e', (byte)'e', (byte)'t', (byte)'2',
|
||||
};
|
||||
|
||||
public TestSupBookRecord(String name)
|
||||
{
|
||||
@ -47,36 +57,67 @@ public class TestSupBookRecord
|
||||
/**
|
||||
* tests that we can load the record
|
||||
*/
|
||||
public void testLoad()
|
||||
throws Exception
|
||||
{
|
||||
public void testLoadIR() {
|
||||
|
||||
SupBookRecord record = new SupBookRecord(new TestcaseRecordInputStream((short)0x01AE, (short)data.length, data));
|
||||
assertEquals( 0x401, record.getFlag()); //expected flag
|
||||
SupBookRecord record = new SupBookRecord(new TestcaseRecordInputStream((short)0x01AE, dataIR));
|
||||
assertTrue( record.isInternalReferences() ); //expected flag
|
||||
assertEquals( 0x4, record.getNumberOfSheets() ); //expected # of sheets
|
||||
|
||||
assertEquals( 8, record.getRecordSize() ); //sid+size+data
|
||||
|
||||
record.validateSid((short)0x01AE);
|
||||
}
|
||||
/**
|
||||
* tests that we can load the record
|
||||
*/
|
||||
public void testLoadER() {
|
||||
|
||||
SupBookRecord record = new SupBookRecord(new TestcaseRecordInputStream((short)0x01AE, dataER));
|
||||
assertTrue( record.isExternalReferences() ); //expected flag
|
||||
assertEquals( 0x2, record.getNumberOfSheets() ); //expected # of sheets
|
||||
|
||||
assertEquals( 34, record.getRecordSize() ); //sid+size+data
|
||||
|
||||
assertEquals("testURL", record.getURL().getString());
|
||||
UnicodeString[] sheetNames = record.getSheetNames();
|
||||
assertEquals(2, sheetNames.length);
|
||||
assertEquals("Sheet1", sheetNames[0].getString());
|
||||
assertEquals("Sheet2", sheetNames[1].getString());
|
||||
|
||||
record.validateSid((short)0x01AE);
|
||||
}
|
||||
|
||||
/**
|
||||
* tests that we can load the record
|
||||
*/
|
||||
public void testLoadAIF() {
|
||||
|
||||
SupBookRecord record = new SupBookRecord(new TestcaseRecordInputStream((short)0x01AE, dataAIF));
|
||||
assertTrue( record.isAddInFunctions() ); //expected flag
|
||||
assertEquals( 0x1, record.getNumberOfSheets() ); //expected # of sheets
|
||||
assertEquals( 8, record.getRecordSize() ); //sid+size+data
|
||||
record.validateSid((short)0x01AE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can store the record
|
||||
*
|
||||
*/
|
||||
public void testStore()
|
||||
{
|
||||
SupBookRecord record = new SupBookRecord();
|
||||
record.setFlag( (short) 0x401 );
|
||||
record.setNumberOfSheets( (short)0x4 );
|
||||
public void testStoreIR() {
|
||||
SupBookRecord record = SupBookRecord.createInternalReferences((short)4);
|
||||
|
||||
TestcaseRecordInputStream.confirmRecordEncoding(0x01AE, dataIR, record.serialize());
|
||||
}
|
||||
|
||||
public void testStoreER() {
|
||||
UnicodeString url = new UnicodeString("testURL");
|
||||
UnicodeString[] sheetNames = {
|
||||
new UnicodeString("Sheet1"),
|
||||
new UnicodeString("Sheet2"),
|
||||
};
|
||||
SupBookRecord record = SupBookRecord.createExternalReferences(url, sheetNames);
|
||||
|
||||
byte [] recordBytes = record.serialize();
|
||||
assertEquals(recordBytes.length - 4, data.length);
|
||||
for (int i = 0; i < data.length; i++)
|
||||
assertEquals("At offset " + i, data[i], recordBytes[i+4]);
|
||||
TestcaseRecordInputStream.confirmRecordEncoding(0x01AE, dataER, record.serialize());
|
||||
}
|
||||
|
||||
public static void main(String [] args) {
|
||||
@ -84,6 +125,4 @@ public class TestSupBookRecord
|
||||
.println("Testing org.apache.poi.hssf.record.SupBookRecord");
|
||||
junit.textui.TestRunner.run(TestSupBookRecord.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
@ -16,11 +15,12 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.apache.poi.util.LittleEndian;
|
||||
|
||||
/**
|
||||
@ -33,6 +33,14 @@ import org.apache.poi.util.LittleEndian;
|
||||
public class TestcaseRecordInputStream
|
||||
extends RecordInputStream
|
||||
{
|
||||
/**
|
||||
* Convenience constructor
|
||||
*/
|
||||
public TestcaseRecordInputStream(int sid, byte[] data)
|
||||
{
|
||||
super(new ByteArrayInputStream(mergeDataAndSid((short)sid, (short)data.length, data)));
|
||||
nextRecord();
|
||||
}
|
||||
public TestcaseRecordInputStream(short sid, short length, byte[] data)
|
||||
{
|
||||
super(new ByteArrayInputStream(mergeDataAndSid(sid, length, data)));
|
||||
@ -46,4 +54,18 @@ public class TestcaseRecordInputStream
|
||||
System.arraycopy(data, 0, result, 4, data.length);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Confirms data sections are equal
|
||||
* @param expectedData - just raw data (without sid or size short ints)
|
||||
* @param actualRecordBytes this includes 4 prefix bytes (sid & size)
|
||||
*/
|
||||
public static void confirmRecordEncoding(int expectedSid, byte[] expectedData, byte[] actualRecordBytes) {
|
||||
int expectedDataSize = expectedData.length;
|
||||
Assert.assertEquals(actualRecordBytes.length - 4, expectedDataSize);
|
||||
Assert.assertEquals(expectedSid, LittleEndian.getShort(actualRecordBytes, 0));
|
||||
Assert.assertEquals(expectedDataSize, LittleEndian.getShort(actualRecordBytes, 2));
|
||||
for (int i = 0; i < expectedDataSize; i++)
|
||||
Assert.assertEquals("At offset " + i, expectedData[i], actualRecordBytes[i+4]);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
@ -28,6 +27,7 @@ import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.model.Workbook;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
|
||||
/**
|
||||
* Convenient abstract class to reduce the amount of boilerplate code needed
|
||||
@ -35,8 +35,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
*
|
||||
* @author Daniel Noll (daniel at nuix dot com dot au)
|
||||
*/
|
||||
public abstract class AbstractPtgTestCase extends TestCase
|
||||
{
|
||||
public abstract class AbstractPtgTestCase extends TestCase {
|
||||
/** Directory containing the test data. */
|
||||
private static String dataDir = System.getProperty("HSSF.testdata.path");
|
||||
|
||||
@ -51,12 +50,12 @@ public abstract class AbstractPtgTestCase extends TestCase
|
||||
throws IOException {
|
||||
File file = new File(dataDir, filename);
|
||||
InputStream stream = new BufferedInputStream(new FileInputStream(file));
|
||||
try
|
||||
{
|
||||
return new HSSFWorkbook(stream);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// TODO - temp workaround to keep stdout quiet due to warning msg in POIFS
|
||||
// When that warning msg is disabled, remove this wrapper and the close() call,
|
||||
InputStream wrappedStream = POIFSFileSystem.createNonClosingInputStream(stream);
|
||||
try {
|
||||
return new HSSFWorkbook(wrappedStream);
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
@ -73,5 +72,4 @@ public abstract class AbstractPtgTestCase extends TestCase
|
||||
book.setSheetName(0, sheetName);
|
||||
return book;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import junit.framework.Test;
|
||||
@ -33,7 +32,8 @@ public class AllFormulaTests {
|
||||
result.addTestSuite(TestArea3DPtg.class);
|
||||
result.addTestSuite(TestAreaErrPtg.class);
|
||||
result.addTestSuite(TestAreaPtg.class);
|
||||
result.addTestSuite(TestErrPtg.class);
|
||||
result.addTestSuite(TestErrPtg.class);
|
||||
result.addTestSuite(TestExternalFunctionFormulas.class);
|
||||
result.addTestSuite(TestFuncPtg.class);
|
||||
result.addTestSuite(TestIntersectionPtg.class);
|
||||
result.addTestSuite(TestPercentPtg.class);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
71
src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java
Executable file
71
src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java
Executable 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;
|
||||
}
|
||||
}
|
@ -16,25 +16,28 @@
|
||||
*/
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Test <code>HSSFPicture</code>.
|
||||
*
|
||||
* @author Yegor Kozlov (yegor at apache.org)
|
||||
*/
|
||||
public class TestHSSFPicture extends TestCase{
|
||||
public final class TestHSSFPicture extends TestCase{
|
||||
|
||||
public void testResize() throws Exception {
|
||||
public void testResize() {
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
HSSFSheet sh1 = wb.createSheet();
|
||||
HSSFPatriarch p1 = sh1.createDrawingPatriarch();
|
||||
|
||||
int idx1 = loadPicture( "src/resources/logos/logoKarmokar4.png", wb);
|
||||
byte[] pictureData = getTestDataFileContent("logoKarmokar4.png");
|
||||
int idx1 = wb.addPicture( pictureData, HSSFWorkbook.PICTURE_TYPE_PNG );
|
||||
HSSFPicture picture1 = p1.createPicture(new HSSFClientAnchor(), idx1);
|
||||
HSSFClientAnchor anchor1 = picture1.getPreferredSize();
|
||||
|
||||
@ -52,28 +55,25 @@ public class TestHSSFPicture extends TestCase{
|
||||
/**
|
||||
* Copied from org.apache.poi.hssf.usermodel.examples.OfficeDrawing
|
||||
*/
|
||||
private static int loadPicture( String path, HSSFWorkbook wb ) throws IOException
|
||||
{
|
||||
int pictureIndex;
|
||||
FileInputStream fis = null;
|
||||
ByteArrayOutputStream bos = null;
|
||||
try
|
||||
{
|
||||
fis = new FileInputStream( path);
|
||||
bos = new ByteArrayOutputStream( );
|
||||
int c;
|
||||
while ( (c = fis.read()) != -1)
|
||||
bos.write( c );
|
||||
pictureIndex = wb.addPicture( bos.toByteArray(), HSSFWorkbook.PICTURE_TYPE_PNG );
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (fis != null)
|
||||
fis.close();
|
||||
if (bos != null)
|
||||
bos.close();
|
||||
}
|
||||
return pictureIndex;
|
||||
}
|
||||
private static byte[] getTestDataFileContent(String fileName) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
||||
String readFilename = System.getProperty("HSSF.testdata.path");
|
||||
try {
|
||||
InputStream fis = new FileInputStream(readFilename+File.separator+fileName);
|
||||
|
||||
byte[] buf = new byte[512];
|
||||
while(true) {
|
||||
int bytesRead = fis.read(buf);
|
||||
if(bytesRead < 1) {
|
||||
break;
|
||||
}
|
||||
bos.write(buf, 0, bytesRead);
|
||||
}
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return bos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user