All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.query.XQueryFunctionLibrary Maven / Gradle / Ivy

There is a newer version: 10.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 Saxonica Limited.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.query;



import net.sf.saxon.Configuration;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.instruct.UserFunction;
import net.sf.saxon.expr.parser.ExpressionVisitor;
import net.sf.saxon.functions.FunctionLibrary;
import net.sf.saxon.om.FunctionItem;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.FunctionItemType;
import net.sf.saxon.value.SequenceExtent;

import java.util.HashMap;
import java.util.Iterator;

/**
 * An XQueryFunctionLibrary is a function library containing all the user-defined functions available for use within a
 * particular XQuery module: that is, the functions declared in that module, and the functions imported from other
 * modules. It also contains (transiently during compilation) a list of function calls within the module that have not
 * yet been bound to a specific function declaration.
*/

public class XQueryFunctionLibrary implements FunctionLibrary, XQueryFunctionBinder {

	private Configuration config;

    // The functions in this library are represented using a HashMap
    // The key of the hashmap is a String that encodes the QName of the function and its arity
    // The value in the hashmap is an XQueryFunction
    /*@NotNull*/ private HashMap functions =
            new HashMap(20);

	/**
	 * Create an XQueryFunctionLibrary
     * @param config the Saxon configuration
	*/

	public XQueryFunctionLibrary(Configuration config) {
        this.config = config;
	}

    /**
     * Set the Configuration options
     * @param config the Saxon configuration
     */

    public void setConfiguration(Configuration config) {
        this.config = config;
    }

    /**
     * Get the Configuration options
     * @return the Saxon configuration
     */

    public Configuration getConfiguration() {
        return config;
    }

    /**
     * Register a user-defined XQuery function
     * @param function the function to be registered
     * @throws XPathException if there is an existing function with the same name and arity
     */

    public void declareFunction(/*@NotNull*/ XQueryFunction function) throws XPathException {
        String keyObj = function.getIdentificationKey();
        XQueryFunction existing = functions.get(keyObj);
        if (existing == function) {
            return;
        }
        if (existing != null) {
            XPathException err = new XPathException("Duplicate definition of function " +
                    function.getDisplayName() +
                    " (see line " + existing.getLineNumber() + " in " + existing.getSystemId() + ')');
            err.setErrorCode("XQST0034");
            err.setIsStaticError(true);
            err.setLocator(function);
            throw err;
        }
        functions.put(keyObj, function);
    }



    /**
     * Test whether a function with a given name and arity is available
     * 

This supports the function-available() function in XSLT.

* * @param functionName the qualified name of the function being called * @param arity The number of arguments. * @return true if a function of this name and arity is available for calling */ public boolean isAvailable(StructuredQName functionName, int arity) { String functionKey = XQueryFunction.getIdentificationKey(functionName, arity); return functions.get(functionKey) != null; } /** * Inner class containing information about a reference to a function whose declaration * has not yet been encountered. The references gets fixed up later, once information * about all user-declared functions is available. */ public static class UnresolvedCallable implements UserFunctionReference, Callable { StructuredQName name; int arity; UserFunction function; //CallableFunctionItem functionItem; public UnresolvedCallable(StructuredQName name, int arity) { this.name = name; this.arity = arity; } public StructuredQName getFunctionName() { return name; } public int getArity() { return arity; } //public void setFunctionItem(CallableFunctionItem fi) { // this.functionItem = fi; //} /** * Evaluate the expression * * @param context the dynamic evaluation context * @param arguments the values of the arguments, supplied as Sequences * @return the result of the evaluation, in the form of a Sequence * @throws net.sf.saxon.trans.XPathException * if a dynamic error occurs during the evaluation of the expression */ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { if (function == null) { throw new XPathException("Forwards reference to XQuery function has not been resolved"); } Sequence[] args = new Sequence[arguments.length]; for (int i=0; iNote that a function call may appear earlier in the query than the definition * of the function to which it is bound. Unlike XSLT, we cannot search forwards to * find the function definition. Binding of function calls is therefore a two-stage * process; at the time the function call is parsed, we simply register it as * pending; subsequently at the end of query parsing all the pending function * calls are resolved. Another consequence of this is that we cannot tell at the time * a function call is parsed whether it is a call to an internal (XSLT or XQuery) * function or to an extension function written in Java. * @return an Expression representing the function call. This will normally be * a FunctionCall, but it may be rewritten as some other expression. * @throws XPathException if the function call is invalid, either because it is * an unprefixed call to a non-system function, or because it is calling a system * function that is available in XSLT only. A prefixed function call that cannot * be recognized at this stage is assumed to be a forwards reference, and is bound * later when bindUnboundFunctionCalls() is called. */ /*@Nullable*/ public Expression bind(/*@NotNull*/ StructuredQName functionName, /*@NotNull*/ int arity, Expression[] arguments, StaticContext env, Container container) throws XPathException { String functionKey = XQueryFunction.getIdentificationKey( functionName, arguments.length); XQueryFunction fd = functions.get(functionKey); if (fd != null) { if (fd.isPrivate() && fd.getStaticContext() != env) { throw new XPathException("Cannot call the private function " + functionName.getDisplayName() + " from outside its module", "XPST0017"); } UserFunctionCall ufc = new UserFunctionCall(); ufc.setFunctionName(fd.getFunctionName()); ufc.setArguments(arguments); ufc.setStaticType(fd.getResultType()); UserFunction fn = fd.getUserFunction(); if (fn == null) { // not yet compiled fd.registerReference(ufc); } else { ufc.setFunction(fn); ExpressionVisitor visitor = ExpressionVisitor.make(fd.getStaticContext(), fd.getExecutable()); ufc.checkFunctionCall(fn, visitor); } return ufc; } else { return null; } } /** * Get the function declaration corresponding to a given function name and arity * @return the XQueryFunction if there is one, or null if not. */ public XQueryFunction getDeclaration(/*@NotNull*/ StructuredQName functionName, /*@NotNull*/ Expression[] staticArgs) { String functionKey = XQueryFunction.getIdentificationKey( functionName, staticArgs.length); return functions.get(functionKey); } /** * Get the function declaration corresponding to a given function name and arity, supplied * in the form "{uri}local/arity" * @param functionKey a string in the form "{uri}local/arity" identifying the required function * @return the XQueryFunction if there is one, or null if not. */ public XQueryFunction getDeclarationByKey(String functionKey) { return functions.get(functionKey); } /** * Get an iterator over the Functions defined in this module * @return an Iterator, whose items are {@link XQueryFunction} objects. It returns * all function known to this module including those imported from elsewhere; they * can be distinguished by their namespace. */ public Iterator getFunctionDefinitions() { return functions.values().iterator(); } /** * Fixup all references to global functions. This method is called * on completion of query parsing. Each XQueryFunction is required to * bind all references to that function to the object representing the run-time * executable code of the function. *

* This method is for internal use. * @param env the static context for the main query body. * @throws net.sf.saxon.trans.XPathException if an error occurs */ protected void fixupGlobalFunctions(/*@NotNull*/ QueryModule env) throws XPathException { ExpressionVisitor visitor = ExpressionVisitor.make(env, env.getExecutable()); for (XQueryFunction fn : functions.values()) { fn.compile(); } for (XQueryFunction fn : functions.values()) { visitor.setExecutable(fn.getExecutable()); fn.checkReferences(visitor); } } /** * Optimize the body of all global functions. This may involve inlining functions calls * @throws net.sf.saxon.trans.XPathException if an error occurs */ protected void optimizeGlobalFunctions() throws XPathException { for (XQueryFunction fn : functions.values()) { fn.optimize(); } } /** * Output "explain" information about each declared function * @param out the ExpressionPresenter that renders the output */ public void explainGlobalFunctions(/*@NotNull*/ ExpressionPresenter out) { for (XQueryFunction fn : functions.values()) { fn.explain(out); } } /** * Get the function with a given name and arity. This method is provided so that XQuery functions * can be called directly from a Java application. Note that there is no type checking or conversion * of arguments when this is done: the arguments must be provided in exactly the form that the function * signature declares them. * @param uri the uri of the function name * @param localName the local part of the function name * @param arity the number of arguments. * @return the function identified by the URI, local name, and arity; or null if there is no such function */ /*@Nullable*/ public UserFunction getUserDefinedFunction(/*@NotNull*/ String uri, /*@NotNull*/ String localName, int arity) { String functionKey = XQueryFunction.getIdentificationKey(uri, localName, arity); XQueryFunction fd = functions.get(functionKey); if (fd==null) { return null; } return fd.getUserFunction(); } /** * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows * new functions to be added, then additions to this copy will not affect the original, or * vice versa. * * @return a copy of this function library. This must be an instance of the original class. */ /*@NotNull*/ public FunctionLibrary copy() { XQueryFunctionLibrary qfl = new XQueryFunctionLibrary(config); qfl.functions = new HashMap(functions); return qfl; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy