package org.exist.xquery.functions.fn; import org.exist.dom.QName; import org.exist.xquery.*; import org.exist.xquery.value.FunctionParameterSequenceType; import org.exist.xquery.value.FunctionReference; import org.exist.xquery.value.FunctionReturnSequenceType; import org.exist.xquery.value.IntegerValue; import org.exist.xquery.value.QNameValue; import org.exist.xquery.value.Sequence; import org.exist.xquery.value.SequenceType; import org.exist.xquery.value.Type; public class FunOnFunctions extends BasicFunction { public final static FunctionSignature signatures[] = { new FunctionSignature( new QName("function-lookup", Function.BUILTIN_FUNCTION_NS), "Returns a reference to the function having a given name and arity, if there is one," + " the empty sequence otherwise", new SequenceType[] { new FunctionParameterSequenceType("name", Type.QNAME, Cardinality.EXACTLY_ONE, "Qualified name of the function"), new FunctionParameterSequenceType("arity", Type.INTEGER, Cardinality.EXACTLY_ONE, "The arity (number of arguments) of the function") }, new FunctionReturnSequenceType(Type.FUNCTION_REFERENCE, Cardinality.ZERO_OR_ONE, "The function if found, empty sequence otherwise")), new FunctionSignature( new QName("function-name", Function.BUILTIN_FUNCTION_NS), "Returns the name of the function identified by a function item.", new SequenceType[] { new FunctionParameterSequenceType("function", Type.FUNCTION_REFERENCE, Cardinality.EXACTLY_ONE, "The function item") }, new FunctionReturnSequenceType(Type.QNAME, Cardinality.ZERO_OR_ONE, "The name of the function or the empty sequence if $function is an anonymous function.")), new FunctionSignature( new QName("function-arity", Function.BUILTIN_FUNCTION_NS), "Returns the arity of the function identified by a function item.", new SequenceType[] { new FunctionParameterSequenceType("function", Type.FUNCTION_REFERENCE, Cardinality.EXACTLY_ONE, "The function item") }, new FunctionReturnSequenceType(Type.INTEGER, Cardinality.EXACTLY_ONE, "The arity of the function.")) }; public FunOnFunctions(XQueryContext context, FunctionSignature signature) { super(context, signature); } @Override public void analyze(AnalyzeContextInfo contextInfo) throws XPathException { super.analyze(contextInfo); if (getContext().getXQueryVersion()<30) { throw new XPathException(this, ErrorCodes.EXXQDY0003, "Function '" + getSignature().getName() + "' is only supported for xquery version \"3.0\" and later."); } } @Override public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException { try { if (isCalledAs("function-lookup")) { final QName fname = ((QNameValue) args[0].itemAt(0)).getQName(); final int arity = ((IntegerValue)args[1].itemAt(0)).getInt(); FunctionCall call; try { call = NamedFunctionReference.lookupFunction(this, context, fname, arity); } catch (final XPathException e) { if (e.getErrorCode() == ErrorCodes.XPST0017) { // return empty sequence for all "function not found" related errors return Sequence.EMPTY_SEQUENCE; } throw e; } return call == null ? Sequence.EMPTY_SEQUENCE : new FunctionReference(call); } else if (isCalledAs("function-name")) { final FunctionReference ref = (FunctionReference) args[0].itemAt(0); final QName qname = ref.getSignature().getName(); if (qname == null || qname == InlineFunction.INLINE_FUNCTION_QNAME) {return Sequence.EMPTY_SEQUENCE;} else {return new QNameValue(context, qname);} } else { // isCalledAs("function-arity") final FunctionReference ref = (FunctionReference) args[0].itemAt(0); return new IntegerValue(ref.getSignature().getArgumentCount()); } } catch (final Exception e) { if (e instanceof XPathException) {throw (XPathException)e;} else {throw new XPathException(this, ErrorCodes.XPST0017, e.getMessage());} } } public static FunctionCall lookupFunction(Expression parent, QName qname, int arity) throws XPathException { // check if the function is from a module final Module module = parent.getContext().getModule(qname.getNamespaceURI()); try { UserDefinedFunction func; if(module == null) { func = parent.getContext().resolveFunction(qname, arity); } else { if(module.isInternalModule()) { throw new XPathException(parent, ErrorCodes.XPST0017, "Cannot create a reference to an internal Java function"); } func = ((ExternalModule)module).getFunction(qname, arity, parent.getContext()); } if (func == null) {return null;} final FunctionCall funcCall = new FunctionCall(parent.getContext(), func); funcCall.setLocation(parent.getLine(), parent.getColumn()); return funcCall; } catch (final XPathException e) { // function not found: return empty sequence return null; } } }