api.Saxon.Api.XQuery.cs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of saxon Show documentation
Show all versions of saxon Show documentation
Saxon a complete and conformant implementation of the XSLT 2.0, XQuery 1.0, and XPath 2.0 Recommendations published on 23 January 2007 by W3C
The newest version!
using System;
using System.IO;
using System.Xml;
using System.Collections;
using JConfiguration = net.sf.saxon.Configuration;
using net.sf.saxon.om;
using net.sf.saxon.value;
using net.sf.saxon.query;
using net.sf.saxon.dotnet;
using JXPathException = net.sf.saxon.trans.XPathException;
using JStreamSource = javax.xml.transform.stream.StreamSource;
namespace Saxon.Api
{
///
/// An XQueryCompiler object allows XQuery queries to be compiled.
///
///
/// To construct an XQueryCompiler , use the factory method
/// newXQueryCompiler on the Processor object.
/// The XQueryCompiler holds information that represents the static context
/// for the queries that it compiles. This information remains intact after performing
/// a compilation. An XQueryCompiler may therefore be used repeatedly to compile multiple
/// queries. Any changes made to the XQueryCompiler (that is, to the
/// static context) do not affect queries that have already been compiled.
/// An XQueryCompiler may be used concurrently in multiple threads, but
/// it should not then be modified once initialized.
///
///
[Serializable]
public class XQueryCompiler
{
private JConfiguration config;
private StaticQueryContext env;
private IQueryResolver moduleResolver;
private IList errorList;
// internal constructor: the public interface is a factory method
// on the Processor object
internal XQueryCompiler(Processor processor)
{
this.config = processor.config;
this.env = new StaticQueryContext(config);
env.setModuleURIResolver(new DotNetStandardModuleURIResolver(processor.XmlResolver));
}
///
/// Declare a namespace for use by the query. This has the same
/// status as a namespace appearing within the query prolog (though
/// a declaration in the query prolog of the same prefix will take
/// precedence)
///
/// The namespace prefix to be declared. Use
/// a zero-length string to declare the default namespace (that is, the
/// default namespace for elements and types).
/// The namespace URI. It is possible to specify
/// a zero-length string to "undeclare" a namespace.
public void DeclareNamespace(String prefix, String uri)
{
env.declareNamespace(prefix, uri);
}
///
/// The base URI of the query, which forms part of the static context
/// of the query. This is used for resolving any relative URIs appearing
/// within the query, for example in references to library modules, schema
/// locations, or as an argument to the doc() function.
///
public String BaseUri
{
get { return env.getBaseURI(); }
set { env.setBaseURI(value); }
}
///
/// This property indicates whether XQuery Update syntax is accepted. The default
/// value is false. This property must be set to true before compiling a query that
/// uses update syntax.
///
///
/// This propery must be set to true before any query can be compiled
/// that uses updating syntax. This applies even if the query is not actually an updating
/// query (for example, a copy-modify expression). XQuery Update syntax is accepted
/// only by Saxon-SA. Non-updating queries are accepted regardless of the value of this
/// property.
/// Property added in Saxon 9.1
public bool UpdatingEnabled
{
get { return env.isUpdatingEnabled(); }
set { env.setUpdatingEnabled(value); }
}
///
/// A user-supplied IQueryResolver used to resolve location hints appearing in an
/// import module declaration.
///
///
/// In the absence of a user-supplied QueryResolver , an import module declaration
/// is interpreted as follows. First, if the module URI identifies an already loaded module, that module
/// is used and the location hints are ignored. Otherwise, each URI listed in the location hints is
/// resolved using the XmlResolver registered with the Processor .
///
public IQueryResolver QueryResolver
{
get { return moduleResolver; }
set
{
moduleResolver = value;
env.setModuleURIResolver(new DotNetModuleURIResolver(value));
}
}
///
/// List of errors. The caller should supply an empty list before calling Compile;
/// the processor will then populate the list with error information obtained during
/// the compilation. Each error will be included as an object of type StaticError.
/// If no error list is supplied by the caller, error information will be written to
/// the standard error stream.
///
///
/// By supplying a custom List with a user-written add() method, it is possible to
/// intercept error conditions as they occur.
///
public IList ErrorList
{
set
{
errorList = value;
env.setErrorListener(new ErrorGatherer(value));
}
get
{
return errorList;
}
}
///
/// Compile a query supplied as a Stream.
///
///
/// The XQuery processor attempts to deduce the encoding of the query
/// by looking for a byte-order-mark, or if none is present, by looking
/// for the encoding declaration in the XQuery version declaration.
/// For this to work, the stream must have the CanSeek property.
/// If no encoding information is present, UTF-8 is assumed.
/// The base URI of the query is set to the value of the BaseUri
/// property. If this has not been set, then the base URI will be undefined, which
/// means that any use of an expression that depends on the base URI will cause
/// an error.
///
///
///
/// XQueryExecutable q = compiler.Compile(new FileStream("input.xq", FileMode.Open, FileAccess.Read));
///
///
/// A stream containing the source text of the query
/// An XQueryExecutable which represents the compiled query object.
/// The XQueryExecutable may be run as many times as required, in the same or a different
/// thread. The XQueryExecutable is not affected by any changes made to the XQueryCompiler
/// once it has been compiled.
/// Throws a StaticError if errors were detected
/// during static analysis of the query. Details of the errors will be added as StaticError
/// objects to the ErrorList if supplied; otherwise they will be written to the standard
/// error stream. The exception that is returned is merely a summary indicating the
/// status.
public XQueryExecutable Compile(Stream query)
{
try
{
XQueryExpression exp = env.compileQuery(new DotNetInputStream(query), null);
return new XQueryExecutable(exp);
}
catch (JXPathException e)
{
throw new StaticError(e);
}
}
///
/// Compile a query supplied as a String.
///
///
/// Using this method the query processor is provided with a string of Unicode
/// characters, so no decoding is necessary. Any encoding information present in the
/// version declaration is therefore ignored.
///
///
///
/// XQueryExecutable q = compiler.Compile("distinct-values(//*/node-name()");
///
///
/// A string containing the source text of the query
/// An XQueryExecutable which represents the compiled query object.
/// The XQueryExecutable may be run as many times as required, in the same or a different
/// thread. The XQueryExecutable is not affected by any changes made to the XQueryCompiler
/// once it has been compiled.
/// Throws a StaticError if errors were detected
/// during static analysis of the query. Details of the errors will be added as StaticError
/// objects to the ErrorList if supplied; otherwise they will be written to the standard
/// error stream. The exception that is returned is merely a summary indicating the
/// status.
public XQueryExecutable Compile(String query)
{
try
{
XQueryExpression exp = env.compileQuery(query);
return new XQueryExecutable(exp);
}
catch (JXPathException e)
{
throw new StaticError(e);
}
}
///
/// Escape hatch to the underying Java implementation
///
public net.sf.saxon.query.StaticQueryContext Implementation
{
get { return env; }
}
}
///
/// An XQueryExecutable represents the compiled form of a query. To execute the query,
/// it must first be loaded to form an XQueryEvaluator .
///
///
/// An XQueryExecutable is immutable, and therefore thread-safe. It is simplest to
/// load a new XQueryEvaluator each time the query is to be run. However, the
/// XQueryEvaluator is serially reusable within a single thread.
/// An XQueryExecutable is created by using one of the Compile
/// methods on the XQueryCompiler class.
///
[Serializable]
public class XQueryExecutable
{
private XQueryExpression exp;
// internal constructor
internal XQueryExecutable(XQueryExpression exp)
{
this.exp = exp;
}
/// Ask whether this is an updating query (that is, one that returns a pending
/// update list rather than a convensional value).
/// Property added in Saxon 9.1
public bool IsUpdateQuery
{
get { return exp.isUpdateQuery(); }
}
///
/// Load the query to prepare it for execution.
///
///
/// An XQueryEvaluator . The returned XQueryEvaluator can be used to
/// set up the dynamic context for query evaluation, and to run the query.
///
public XQueryEvaluator Load()
{
return new XQueryEvaluator(exp);
}
}
///
/// An XQueryEvaluator represents a compiled and loaded query ready for execution.
/// The XQueryEvaluator holds details of the dynamic evaluation context for the query.
///
///
/// An XQueryEvaluator should not be used concurrently in multiple threads. It is safe,
/// however, to reuse the object within a single thread to run the same query several times.
/// Running the query does not change the context that has been established.
/// An XQueryEvaluator is always constructed by running the Load method of
/// an XQueryExecutable .
///
[Serializable]
public class XQueryEvaluator : IEnumerable
{
private XQueryExpression exp;
private DynamicQueryContext context;
private Stream traceFunctionDestination;
// internal constructor
internal XQueryEvaluator(XQueryExpression exp)
{
this.exp = exp;
this.context =
new DynamicQueryContext(exp.getStaticContext().getConfiguration());
}
///
/// The context item for the query.
///
/// This may be either a node or an atomic
/// value. Most commonly it will be a document node, which might be constructed
/// using the LoadDocument method of the Processor object.
///
public XdmItem ContextItem
{
get { return (XdmItem)XdmValue.Wrap(context.getContextItem()); }
set { context.setContextItem((Item)value.Unwrap()); }
}
///
/// The XmlResolver
to be used at run-time to resolve and dereference URIs
/// supplied to the doc() function.
///
public XmlResolver InputXmlResolver
{
get
{
return ((DotNetURIResolver)context.getURIResolver()).getXmlResolver();
}
set
{
context.setURIResolver(new DotNetURIResolver(value));
}
}
///
/// Set the value of an external variable declared in the query.
///
/// The name of the external variable, expressed
/// as a QName. If an external variable of this name has been declared in the
/// query prolog, the given value will be assigned to the variable. If the
/// variable has not been declared, calling this method has no effect (it is
/// not an error).
/// The value to be given to the external variable.
/// If the variable declaration defines a required type for the variable, then
/// this value must match the required type: no conversions are applied.
public void SetExternalVariable(QName name, XdmValue value)
{
context.setParameter(name.ClarkName, value.Unwrap());
}
///
/// Destination for output of messages produced using <trace()>.
/// If no specific destination is supplied by the caller, message information will be written to
/// the standard error stream.
///
///
/// The supplied destination is ignored if a TraceListener is in use.
/// Property added in Saxon 9.1
///
public Stream TraceFunctionDestination
{
set
{
traceFunctionDestination = value;
context.setTraceFunctionDestination(
new java.io.PrintStream(new DotNetOutputStream(value)));
}
get
{
return traceFunctionDestination;
}
}
///
/// Evaluate the query, returning the result as an XdmValue (that is,
/// a sequence of nodes and/or atomic values).
///
///
/// An XdmValue representing the results of the query
///
/// Throws a DynamicError if any run-time failure
/// occurs while evaluating the query.
public XdmValue Evaluate()
{
try
{
ValueRepresentation value = SequenceExtent.makeSequenceExtent(exp.iterator(context));
return XdmValue.Wrap(value);
}
catch (JXPathException err)
{
throw new DynamicError(err);
}
}
///
/// Evaluate the query, returning the result as an XdmItem (that is,
/// a single node or atomic value).
///
///
/// An XdmItem representing the result of the query, or null if the query
/// returns an empty sequence. If the query returns a sequence of more than one item,
/// any items after the first are ignored.
///
/// Throws a DynamicError if any run-time failure
/// occurs while evaluating the expression.
public XdmItem EvaluateSingle()
{
try
{
return (XdmItem)XdmValue.Wrap(exp.iterator(context).next());
}
catch (JXPathException err)
{
throw new DynamicError(err);
}
}
///
/// Evaluate the query, returning the result as an IEnumerator (that is,
/// an enumerator over a sequence of nodes and/or atomic values).
///
///
/// An enumerator over the sequence that represents the results of the query.
/// Each object in this sequence will be an instance of XdmItem . Note
/// that the query may be evaluated lazily, which means that a successful response
/// from this method does not imply that the query has executed successfully: failures
/// may be reported later while retrieving items from the iterator.
///
/// Throws a DynamicError if any run-time failure
/// occurs while evaluating the expression.
public IEnumerator GetEnumerator()
{
try
{
return new SequenceEnumerator(exp.iterator(context));
}
catch (JXPathException err)
{
throw new DynamicError(err);
}
}
///
/// Evaluate the query, sending the result to a specified destination.
///
///
/// The destination for the results of the query. The class XmlDestination
/// is an abstraction that allows a number of different kinds of destination
/// to be specified.
///
/// Throws a DynamicError if any run-time failure
/// occurs while evaluating the expression.
public void Run(XmlDestination destination)
{
try
{
exp.run(context, destination.GetResult(), destination.GetOutputProperties());
}
catch (JXPathException err)
{
throw new DynamicError(err);
}
destination.Close();
}
///
/// Execute an updating query.
///
/// An array containing the root nodes of documents that have been
/// updated by the query.
/// Throws a DynamicError if any run-time failure
/// occurs while evaluating the expression, or if the expression is not an
/// updating query.
public XdmNode[] RunUpdate()
{
if (!exp.isUpdateQuery())
{
throw new DynamicError("Not an updating query");
}
try
{
java.util.Set updatedDocs = exp.runUpdate(context);
XdmNode[] result = new XdmNode[updatedDocs.size()];
int i = 0;
for (java.util.Iterator iter = updatedDocs.iterator(); iter.hasNext(); )
{
result[i++] = (XdmNode)XdmValue.Wrap((NodeInfo)iter.next());
}
return result;
}
catch (JXPathException err)
{
throw new DynamicError(err);
}
}
}
///
/// Interface defining a user-supplied class used to retrieve XQUery library modules listed
/// in an import module declaration in the query prolog.
///
public interface IQueryResolver
{
///
/// Given a module URI and a set of location hints, return a set of query modules.
///
/// The URI of the required library module as written in the
/// import module declaration
/// The base URI of the module containing the import module
/// declaration
/// The sequence of URIs (if any) listed as location hints
/// in the import module declaration in the query prolog.
/// A set of absolute Uris identifying the query modules to be loaded. There is no requirement
/// that these correspond one-to-one with the URIs defined in the locationHints . The
/// returned URIs will be dereferenced by calling the GetEntity method.
///
Uri[] GetModules(String moduleUri, Uri baseUri, String[] locationHints);
///
/// Dereference a URI returned by GetModules to retrieve a Stream containing
/// the actual query text.
///
/// A URI returned by the GetModules
method.
/// Either a Stream or a String containing the query text.
/// The supplied URI will be used as the base URI of the query module.
Object GetEntity(Uri absoluteUri);
}
// internal class that wraps a (.NET) IQueryResolver to create a (Java) ModuleURIResolver
internal class DotNetModuleURIResolver : net.sf.saxon.query.ModuleURIResolver
{
private IQueryResolver resolver;
public DotNetModuleURIResolver(IQueryResolver resolver)
{
this.resolver = resolver;
}
public JStreamSource[] resolve(String moduleURI, String baseURI, String[] locations)
{
Uri baseU = (baseURI == null ? null : new Uri(baseURI));
Uri[] modules = resolver.GetModules(moduleURI, baseU, locations);
JStreamSource[] ss = new JStreamSource[modules.Length];
for (int i = 0; i < ss.Length; i++)
{
ss[i] = new JStreamSource();
ss[i].setSystemId(modules[i].ToString());
Object query = resolver.GetEntity(modules[i]);
if (query is Stream)
{
ss[i].setInputStream(new DotNetInputStream((Stream)query));
}
else if (query is String)
{
ss[i].setReader(new DotNetReader(new StringReader((String)query)));
}
else
{
throw new ArgumentException("Invalid response from GetEntity()");
}
}
return ss;
}
}
}
//
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the
// License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is: all this file.
//
// The Initial Developer of the Original Code is Michael H. Kay.
//
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
//
// Contributor(s): none.
//