package org.exist.xquery;
import java.util.List;
import org.exist.xquery.util.ExpressionDumper;
import org.exist.xquery.value.FunctionReference;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.Type;
public class DynamicFunctionCall extends AbstractExpression {
private Expression functionExpr;
private List<Expression> arguments;
private boolean isPartial = false;
private AnalyzeContextInfo cachedContextInfo;
public DynamicFunctionCall(XQueryContext context, Expression fun, List<Expression> args, boolean partial) {
super(context);
this.functionExpr = fun;
this.arguments = args;
this.isPartial = partial;
}
@Override
public void analyze(AnalyzeContextInfo contextInfo) throws XPathException {
cachedContextInfo = new AnalyzeContextInfo(contextInfo);
functionExpr.analyze(contextInfo);
}
@Override
public void dump(ExpressionDumper dumper) {
functionExpr.dump(dumper);
dumper.display('(');
for (final Expression arg : arguments) {
arg.dump(dumper);
}
dumper.display(')');
}
@Override
public Sequence eval(Sequence contextSequence, Item contextItem)
throws XPathException {
context.proceed(this);
final Sequence funcSeq = functionExpr.eval(contextSequence, contextItem);
if (funcSeq.getCardinality() != Cardinality.EXACTLY_ONE)
{throw new XPathException(this, ErrorCodes.XPTY0004,
"Expected exactly one item for the function to be called, got " + funcSeq.getItemCount() +
". Expression: " + ExpressionDumper.dump(functionExpr));}
final Item item0 = funcSeq.itemAt(0);
if (!Type.subTypeOf(item0.getType(), Type.FUNCTION_REFERENCE))
{throw new XPathException(this, ErrorCodes.XPTY0004,
"Type error: expected function, got " + Type.getTypeName(item0.getType()));}
final FunctionReference ref = (FunctionReference)item0;
// if the call is a partial application, create a new function
if (isPartial) {
try {
final FunctionCall call = ref.getCall();
call.setArguments(arguments);
final PartialFunctionApplication partialApp = new PartialFunctionApplication(context, call);
partialApp.analyze(new AnalyzeContextInfo(cachedContextInfo));
return partialApp.eval(contextSequence, contextItem);
} catch (final XPathException e) {
e.setLocation(line, column, getSource());
throw e;
}
} else {
ref.setArguments(arguments);
// need to create a new AnalyzeContextInfo to avoid memory leak
// cachedContextInfo will stay in memory
ref.analyze(new AnalyzeContextInfo(cachedContextInfo));
// Evaluate the function
final Sequence result = ref.eval(contextSequence);
ref.resetState(false);
return result;
}
}
@Override
public int returnsType() {
return Type.ITEM; // Unknown until the reference is resolved
}
@Override
public void resetState(boolean postOptimization) {
super.resetState(postOptimization);
}
}