OperandResolver is your friend. This utility encapsulates most of the
logic needed to convert input arguments to concrete types. See the
implementation of basic numeric functions in NumericFunction.
A prototype of a UDF that operates on two numbers looks as follows:
static class H2_ZFactor implements FreeRefFunction {
@Override
public ValueEval evaluate(ValueEval[] args,
OperationEvaluationContext ec) {
if (args.length != 2) { // Must be given two arguments.
return ErrorEval.VALUE_INVALID;
}
double var1, var2, result;
try {
ValueEval v1 = OperandResolver.getSingleValue(args[0],
ec.getRowIndex(), ec.getColumnIndex());
ValueEval v2 = OperandResolver.getSingleValue(args[1],
ec.getRowIndex(), ec.getColumnIndex());
var1 = OperandResolver.coerceValueToDouble(v1);
var2 = OperandResolver.coerceValueToDouble(v2);
result = calculateH2_ZFactor( var1, var2 ) ;
// Excel's implementation of floating-point numbers
does not support the notion
// of Not-a-Number (NaN) and Positive/Negative Infinities.
// The code must check the result and return
ErrorEval.NUM_ERROR.
checkValue(result);
} catch (EvaluationException e) {
return e.getErrorEval();
}
return new NumberEval( result ) ;
}
/**
* @throws EvaluationException (#NUM!) if <tt>result</tt> is
<tt>NaN</> or <tt>Infinity</tt>
*/
static final void checkValue(double result) throws
EvaluationException {
if (Double.isNaN(result) || Double.isInfinite(result)) {
throw new EvaluationException(ErrorEval.NUM_ERROR);
}
}
double calculateH2_ZFactor(double var1, double var2){
return var1*var2;
}
}
Regards,
Yegor
On 10/6/2010 8:16 AM, Jon Svede wrote:
I was able to set up my own UDF but I noticed something that I can't explain
clearly, I am hoping someone can help me. This is again related to documenting
this feature.
In my FreeRefFunction implementation class, for the array of ValueEval objects,
I get a mix of things. In some cases I get an instance of NumberEval but in
others I've gotten a LazyRefEval. This confused me for a minute, then I
realized I could cast it to RefEval, call the getInnerType() method which could
then be cast to the NumberEval.
This seems a little confusing, am I doing this right? Or is there a better
way? Here is the code that I wrote:
@Override
public ValueEval evaluate( ValueEval[] args, OperationEvaluationContext ec )
{
if (args.length != 2 ) {
return ErrorEval.VALUE_INVALID;
}
NumberEval numEval1 = getNumberEval( args[0] );
NumberEval numEval2 = getNumberEval(args[1]) ;
double var1 = numEval1.getNumberValue() ;
double var2 = numEval2.getNumberValue() ;
double result = calculateH2_ZFactor( var1, var2 ) ;
return new NumberEval( result ) ;
}
/**
* In order to deal with my functions I need two numbers and they
* get passed differently depending on where they are called from. This
* function encapsulates the steps needed to convert the input to the
* NumberEval object. (this may not even be right)
*
* @param vEval
* @return
*/
private NumberEval getNumberEval( ValueEval vEval ) {
if( vEval != null ) {
if( vEval instanceof RefEval ) {
RefEval rEval = (RefEval)vEval ;
return( NumberEval)rEval.getInnerValueEval() ;
} else if( vEval instanceof NumberEval ) {
return (NumberEval)vEval ;
}
}
return null ;
}
It's the getNumberEval() method where all this casting happens and I think that
has to be wrong.
If it's not wrong, is there an explanation I can provide?
Another question is, I assume that the implementation of the evaluate() method
does not throw any exceptions, rather the values from ErrorEval should be used
to express a problem?
Thanks,
Jon
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]