package org.exist.xquery.functions.inspect;
import org.exist.dom.QName;
import org.exist.memtree.MemTreeBuilder;
import org.exist.xquery.functions.util.UtilModule;
import org.exist.xquery.xqdoc.XQDocHelper;
import org.exist.xquery.*;
import org.exist.xquery.value.*;
import org.xml.sax.helpers.AttributesImpl;
import java.util.Map;
import java.util.Set;
public class InspectFunction extends BasicFunction {
public final static FunctionSignature SIGNATURE_DEPRECATED =
new FunctionSignature(
new QName("inspect-function", UtilModule.NAMESPACE_URI, UtilModule.PREFIX),
"Returns an XML fragment describing the function referenced by the passed function item.",
new SequenceType[] {
new FunctionParameterSequenceType("function", Type.FUNCTION_REFERENCE, Cardinality.EXACTLY_ONE, "The function item to inspect"),
},
new FunctionReturnSequenceType(Type.NODE, Cardinality.EXACTLY_ONE, "the signature of the function"));
public final static FunctionSignature SIGNATURE =
new FunctionSignature(
new QName("inspect-function", InspectionModule.NAMESPACE_URI, InspectionModule.PREFIX),
"Returns an XML fragment describing the function referenced by the passed function item.",
new SequenceType[] {
new FunctionParameterSequenceType("function", Type.FUNCTION_REFERENCE, Cardinality.EXACTLY_ONE, "The function item to inspect"),
},
new FunctionReturnSequenceType(Type.NODE, Cardinality.EXACTLY_ONE, "the signature of the function"));
protected final static QName ARGUMENT_QNAME = new QName("argument");
protected final static QName DEPRECATED_QNAME = new QName("deprecated");
protected final static QName DESCRIPTION_QNAME = new QName("description");
protected final static QName RETURN_QNAME = new QName("returns");
protected final static QName FUNCTION_QNAME = new QName("function");
protected final static QName ANNOTATION_QNAME = new QName("annotation");
protected final static QName ANNOTATION_VALUE_QNAME = new QName("value");
protected static final QName VERSION_QNAME = new QName("version");
protected static final QName AUTHOR_QNAME = new QName("author");
protected static final QName CALLS_QNAME = new QName("calls");
public InspectFunction(XQueryContext context, FunctionSignature signature) {
super(context, signature);
}
@Override
public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException {
final FunctionReference ref = (FunctionReference) args[0].itemAt(0);
final FunctionSignature sig = ref.getSignature();
final MemTreeBuilder builder = context.getDocumentBuilder();
final int nodeNr = generateDocs(sig, null, builder);
return builder.getDocument().getNode(nodeNr);
}
/**
* Generate an XML fragment containing information about the function identified by its signature.
*
* @param sig the signature of the function to describe
* @param func the function implementation. If provided, the method will also inspect the function body
* and list all functions called from the current function.
* @param builder builder used to create the XML
* @return nodeNr of the generated element
* @throws XPathException
*/
public static int generateDocs(FunctionSignature sig, UserDefinedFunction func, MemTreeBuilder builder) throws XPathException {
XQDocHelper.parse(sig);
final AttributesImpl attribs = new AttributesImpl();
attribs.addAttribute("", "name", "name", "CDATA", sig.getName().toString());
attribs.addAttribute("", "module", "module", "CDATA", sig.getName().getNamespaceURI());
final int nodeNr = builder.startElement(FUNCTION_QNAME, attribs);
writeParameters(sig, builder);
final SequenceType returnType = sig.getReturnType();
if (returnType != null) {
attribs.clear();
attribs.addAttribute("", "type", "type", "CDATA", Type.getTypeName(returnType.getPrimaryType()));
attribs.addAttribute("", "cardinality", "cardinality", "CDATA", Cardinality.getDescription(returnType.getCardinality()));
builder.startElement(RETURN_QNAME, attribs);
if (returnType instanceof FunctionReturnSequenceType) {
final FunctionReturnSequenceType type = (FunctionReturnSequenceType) returnType;
builder.characters(type.getDescription());
}
builder.endElement();
}
writeAnnotations(sig, builder);
if (sig.getDescription() != null) {
builder.startElement(DESCRIPTION_QNAME, null);
builder.characters(sig.getDescription());
builder.endElement();
}
final Map<String, String> metadata = sig.getMetadata();
if (metadata != null) {
for (final Map.Entry<String, String> meta : metadata.entrySet()) {
builder.startElement(new QName(meta.getKey()), null);
builder.characters(meta.getValue());
builder.endElement();
}
}
if (sig.isDeprecated()) {
builder.startElement(DEPRECATED_QNAME, null);
builder.characters(sig.getDeprecated());
builder.endElement();
}
if (func != null) {
generateDependencies(func, builder);
}
builder.endElement();
return nodeNr;
}
private static void writeParameters(FunctionSignature sig, MemTreeBuilder builder) {
final SequenceType[] arguments = sig.getArgumentTypes();
if (arguments != null) {
final AttributesImpl attribs = new AttributesImpl();
for (final SequenceType type: arguments) {
attribs.clear();
attribs.addAttribute("", "type", "type", "CDATA", Type.getTypeName(type.getPrimaryType()));
attribs.addAttribute("", "cardinality", "cardinality", "CDATA", Cardinality.getDescription(type.getCardinality()));
if (type instanceof FunctionParameterSequenceType)
{attribs.addAttribute("", "var", "var", "CDATA", ((FunctionParameterSequenceType)type).getAttributeName());}
builder.startElement(ARGUMENT_QNAME, attribs);
if (type instanceof FunctionParameterSequenceType) {
builder.characters(((FunctionParameterSequenceType)type).getDescription());
}
builder.endElement();
}
}
}
private static void writeAnnotations(FunctionSignature signature, MemTreeBuilder builder) throws XPathException {
final AttributesImpl attribs = new AttributesImpl();
final Annotation[] annots = signature.getAnnotations();
if (annots != null) {
for (final Annotation annot : annots) {
attribs.clear();
attribs.addAttribute(null, "name", "name", "CDATA", annot.getName().toString());
attribs.addAttribute(null, "namespace", "namespace", "CDATA", annot.getName().getNamespaceURI());
builder.startElement(ANNOTATION_QNAME, attribs);
final LiteralValue[] value = annot.getValue();
if (value != null) {
for (final LiteralValue literal : value) {
builder.startElement(ANNOTATION_VALUE_QNAME, null);
builder.characters(literal.getValue().getStringValue());
builder.endElement();
}
}
builder.endElement();
}
}
}
/**
* Inspect the provided function implementation and return an XML fragment listing all
* functions called from the function.
*
* @param function
* @param builder
*/
public static void generateDependencies(UserDefinedFunction function, MemTreeBuilder builder) {
FunctionCallVisitor visitor = new FunctionCallVisitor();
function.getFunctionBody().accept(visitor);
Set<FunctionSignature> signatures = visitor.getFunctionCalls();
if (signatures.size() == 0) {
return;
}
builder.startElement(CALLS_QNAME, null);
final AttributesImpl attribs = new AttributesImpl();
for (FunctionSignature signature : signatures) {
attribs.clear();
attribs.addAttribute(null, "name", "name", "CDATA", signature.getName().toString());
attribs.addAttribute("", "module", "module", "CDATA", signature.getName().getNamespaceURI());
attribs.addAttribute("", "arity", "arity", "CDATA", Integer.toString(signature.getArgumentCount()));
builder.startElement(FUNCTION_QNAME, attribs);
builder.endElement();
}
builder.endElement();
}
}