diff --git a/exist-core/pom.xml b/exist-core/pom.xml index af67af24689..69118b5523f 100644 --- a/exist-core/pom.xml +++ b/exist-core/pom.xml @@ -613,6 +613,11 @@ test + + de.bottlecaps + markup-blitz + 1.3 + @@ -1004,6 +1009,7 @@ The BaseX Team. The original license statement is also included below.]]> default-compile + 11 org/exist/storage/lock/EnsureLockingAspect.java diff --git a/exist-core/src/main/java/org/exist/http/RESTServer.java b/exist-core/src/main/java/org/exist/http/RESTServer.java index 76b8868f137..8b4c0fe7b9c 100644 --- a/exist-core/src/main/java/org/exist/http/RESTServer.java +++ b/exist-core/src/main/java/org/exist/http/RESTServer.java @@ -1808,14 +1808,19 @@ private void writeResourceAs(final DocumentImpl resource, final DBBroker broker, outputProperties.setProperty("omit-xml-declaration", "no"); } - final OutputStreamWriter writer = new OutputStreamWriter(response.getOutputStream(), encoding); - sax.setOutput(writer, outputProperties); - serializer.setSAXHandlers(sax, sax); + try { + final OutputStreamWriter writer = new OutputStreamWriter(response.getOutputStream(), encoding); + sax.setOutput(writer, outputProperties); + serializer.setSAXHandlers(sax, sax); - serializer.toSAX(resource); + serializer.toSAX(resource); - writer.flush(); - writer.close(); // DO NOT use in try-write-resources, otherwise ther response stream is always closed, and we can't report the errors + // writer.flush(); // Not needed, because close() will flush the stream. + writer.close(); // DO NOT use in try-with-resources, otherwise the response stream is always closed, and we can't report the errors + } catch (org.eclipse.jetty.io.EofException connectionInterrupted) { + // Don't throw the EofException, which is caused by the client hanging up. + LOG.info("Ignored EofException while writing response to ReST client: {}", connectionInterrupted.getMessage()); + } } catch (final SAXException saxe) { LOG.warn(saxe); throw new BadRequestException("Error while serializing XML: " + saxe.getMessage()); diff --git a/exist-core/src/main/java/org/exist/http/urlrewrite/XQueryURLRewrite.java b/exist-core/src/main/java/org/exist/http/urlrewrite/XQueryURLRewrite.java index dd265781c75..2c9b13ccd53 100644 --- a/exist-core/src/main/java/org/exist/http/urlrewrite/XQueryURLRewrite.java +++ b/exist-core/src/main/java/org/exist/http/urlrewrite/XQueryURLRewrite.java @@ -362,6 +362,13 @@ protected void service(final HttpServletRequest request, final HttpServletRespon } } } catch (final Throwable e) { + if (e instanceof IllegalArgumentException) { + LOG.warn("Invalid URI: {}", e.getMessage()); + response.getWriter().print(e.getMessage()); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + response.flushBuffer(); + return; + } LOG.error("Error while processing {}: {}", request.getRequestURI(), e.getMessage(), e); throw new ServletException("An error occurred while processing request to " + request.getRequestURI() + ": " + e.getMessage(), e); diff --git a/exist-core/src/main/java/org/exist/xquery/LetExpr.java b/exist-core/src/main/java/org/exist/xquery/LetExpr.java index 05dae3376c6..e9c9a91d168 100644 --- a/exist-core/src/main/java/org/exist/xquery/LetExpr.java +++ b/exist-core/src/main/java/org/exist/xquery/LetExpr.java @@ -1,234 +1,235 @@ -/* - * eXist-db Open Source Native XML Database - * Copyright (C) 2001 The eXist-db Authors - * - * info@exist-db.org - * http://www.exist-db.org - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.exist.xquery; - -import org.exist.dom.QName; -import org.exist.xquery.util.ExpressionDumper; -import org.exist.xquery.value.*; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -/** - * Implements an XQuery let-expression. - * - * @author Wolfgang Meier - */ -public class LetExpr extends BindingExpression { - - public LetExpr(XQueryContext context) { - super(context); - } - - @Override - public ClauseType getType() { - return ClauseType.LET; - } - - @Override - public void analyze(final AnalyzeContextInfo contextInfo) throws XPathException { - super.analyze(contextInfo); - //Save the local variable stack - final LocalVariable mark = context.markLocalVariables(false); - try { - contextInfo.setParent(this); - final AnalyzeContextInfo varContextInfo = new AnalyzeContextInfo(contextInfo); - inputSequence.analyze(varContextInfo); - //Declare the iteration variable - final LocalVariable inVar = new LocalVariable(QName.parse(context, varName, null)); - inVar.setSequenceType(sequenceType); - inVar.setStaticType(varContextInfo.getStaticReturnType()); - context.declareVariableBinding(inVar); - //Reset the context position - context.setContextSequencePosition(0, null); - - returnExpr.analyze(contextInfo); - } catch (final QName.IllegalQNameException e) { - throw new XPathException(this, ErrorCodes.XPST0081, "No namespace defined for prefix " + varName); - } finally { - // restore the local variable stack - context.popLocalVariables(mark); - } - } - - /* (non-Javadoc) - * @see org.exist.xquery.Expression#eval(org.exist.xquery.StaticContext, org.exist.dom.persistent.DocumentSet, org.exist.xquery.value.Sequence, org.exist.xquery.value.Item) - */ - public Sequence eval(Sequence contextSequence, Item contextItem) - throws XPathException { - if (context.getProfiler().isEnabled()){ - context.getProfiler().start(this); - context.getProfiler().message(this, Profiler.DEPENDENCIES, - "DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies())); - if (contextSequence != null) - {context.getProfiler().message(this, Profiler.START_SEQUENCES, - "CONTEXT SEQUENCE", contextSequence);} - if (contextItem != null) - {context.getProfiler().message(this, Profiler.START_SEQUENCES, - "CONTEXT ITEM", contextItem.toSequence());} - } - context.expressionStart(this); - context.pushDocumentContext(); - try { - //Save the local variable stack - LocalVariable mark = context.markLocalVariables(false); - Sequence in; - LocalVariable var; - Sequence resultSequence = null; - try { - // evaluate input sequence - in = inputSequence.eval(contextSequence, null); - clearContext(getExpressionId(), in); - // Declare the iteration variable - var = createVariable(varName); - var.setSequenceType(sequenceType); - context.declareVariableBinding(var); - var.setValue(in); - if (sequenceType == null) - {var.checkType();} //Just because it makes conversions ! - var.setContextDocs(inputSequence.getContextDocSet()); - registerUpdateListener(in); - - resultSequence = returnExpr.eval(contextSequence, null); - - if (sequenceType != null) { - Cardinality actualCardinality; - if (var.getValue().isEmpty()) {actualCardinality = Cardinality.EMPTY_SEQUENCE;} - else if (var.getValue().hasMany()) {actualCardinality = Cardinality._MANY;} - else {actualCardinality = Cardinality.EXACTLY_ONE;} - //Type.EMPTY is *not* a subtype of other types ; checking cardinality first - if (!sequenceType.getCardinality().isSuperCardinalityOrEqualOf(actualCardinality)) - {throw new XPathException(this, ErrorCodes.XPTY0004, - "Invalid cardinality for variable $" + varName + - ". Expected " + - sequenceType.getCardinality().getHumanDescription() + - ", got " + actualCardinality.getHumanDescription(), in);} - //TODO : ignore nodes right now ; they are returned as xs:untypedAtomicType - if (!Type.subTypeOf(sequenceType.getPrimaryType(), Type.NODE)) { - if (!var.getValue().isEmpty() && !Type.subTypeOf(var.getValue() - .getItemType(), sequenceType.getPrimaryType())) { - throw new XPathException(this, ErrorCodes.XPTY0004, - "Invalid type for variable $" + varName + - ". Expected " + Type.getTypeName(sequenceType.getPrimaryType()) + - ", got " +Type.getTypeName(var.getValue().getItemType()), in); - } - //Here is an attempt to process the nodes correctly - } else { - //Same as above : we probably may factorize - if (!var.getValue().isEmpty() && !sequenceType.checkType(var.getValue())) { - final Sequence value = var.getValue(); - final SequenceType valueType = new SequenceType(value.getItemType(), value.getCardinality()); - if ((!value.isEmpty()) && sequenceType.getPrimaryType() == Type.DOCUMENT && value.getItemType() == Type.DOCUMENT) { - // it's a document... we need to get the document element's name - final NodeValue nvItem = (NodeValue) value.itemAt(0); - final Document doc; - if (nvItem instanceof Document) { - doc = (Document) nvItem; - } else { - doc = nvItem.getOwnerDocument(); - } - if (doc != null) { - final Element elem = doc.getDocumentElement(); - if (elem != null) { - valueType.setNodeName(new QName(elem.getLocalName(), elem.getNamespaceURI())); - } - } - } - - if ((!value.isEmpty()) && sequenceType.getPrimaryType() == Type.ELEMENT && value.getItemType() == Type.ELEMENT) { - final NodeValue nvItem = (NodeValue) value.itemAt(0); - valueType.setNodeName(nvItem.getQName()); - } - - throw new XPathException( - this, - ErrorCodes.XPTY0004, - String.format("Invalid type for variable $%s. Expected %s, got %s", varName, sequenceType.toString(), valueType.toString()), in); - } - } - } - } finally { - // Restore the local variable stack - context.popLocalVariables(mark, resultSequence); - } - clearContext(getExpressionId(), in); - if (context.getProfiler().isEnabled()) - {context.getProfiler().end(this, "", resultSequence);} - if (resultSequence == null) - {return Sequence.EMPTY_SEQUENCE;} - if (!(resultSequence instanceof DeferredFunctionCall)) { - setActualReturnType(resultSequence.getItemType()); - } - if (getPreviousClause() == null) { - resultSequence = postEval(resultSequence); - } - return resultSequence; - } finally { - context.popDocumentContext(); - context.expressionEnd(this); - } - } - - /* (non-Javadoc) - * @see org.exist.xquery.Expression#dump(org.exist.xquery.util.ExpressionDumper) - */ - public void dump(ExpressionDumper dumper) { - dumper.display("let ", line); - dumper.startIndent(); - dumper.display("$").display(varName); - dumper.display(" := "); - inputSequence.dump(dumper); - dumper.endIndent(); - //TODO : QuantifiedExpr - if (returnExpr instanceof LetExpr) - {dumper.display(", ");} - else - {dumper.nl().display("return ");} - dumper.startIndent(); - returnExpr.dump(dumper); - dumper.endIndent(); - } - - public String toString() { - final StringBuilder result = new StringBuilder(); - result.append("let "); - result.append("$").append(varName); - result.append(" := "); - result.append(inputSequence.toString()); - result.append(" "); - //TODO : QuantifiedExpr - if (returnExpr instanceof LetExpr) - {result.append(", ");} - else - {result.append("return ");} - result.append(returnExpr.toString()); - return result.toString(); - } - - public void accept(ExpressionVisitor visitor) { - visitor.visitLetExpression(this); - } - - @Override - public boolean allowMixedNodesInReturn() { - return true; - } +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.xquery; + +import org.exist.dom.QName; +import org.exist.xquery.util.ExpressionDumper; +import org.exist.xquery.value.*; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Implements an XQuery let-expression. + * + * @author Wolfgang Meier + */ +public class LetExpr extends BindingExpression { + + public LetExpr(XQueryContext context) { + super(context); + } + + @Override + public ClauseType getType() { + return ClauseType.LET; + } + + @Override + public void analyze(final AnalyzeContextInfo contextInfo) throws XPathException { + super.analyze(contextInfo); + //Save the local variable stack + final LocalVariable mark = context.markLocalVariables(false); + try { + contextInfo.setParent(this); + final AnalyzeContextInfo varContextInfo = new AnalyzeContextInfo(contextInfo); + inputSequence.analyze(varContextInfo); + //Declare the iteration variable + final LocalVariable inVar = new LocalVariable(QName.parse(context, varName, null)); + inVar.setSequenceType(sequenceType); + inVar.setStaticType(varContextInfo.getStaticReturnType()); + context.declareVariableBinding(inVar); + //Reset the context position + context.setContextSequencePosition(0, null); + + returnExpr.analyze(contextInfo); + } catch (final QName.IllegalQNameException e) { + throw new XPathException(this, ErrorCodes.XPST0081, "No namespace defined for prefix " + varName); + } finally { + // restore the local variable stack + context.popLocalVariables(mark); + } + } + + /* (non-Javadoc) + * @see org.exist.xquery.Expression#eval(org.exist.xquery.StaticContext, org.exist.dom.persistent.DocumentSet, org.exist.xquery.value.Sequence, org.exist.xquery.value.Item) + */ + public Sequence eval(Sequence contextSequence, Item contextItem) + throws XPathException { + if (context.getProfiler().isEnabled()){ + context.getProfiler().start(this); + context.getProfiler().message(this, Profiler.DEPENDENCIES, + "DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies())); + if (contextSequence != null) + {context.getProfiler().message(this, Profiler.START_SEQUENCES, + "CONTEXT SEQUENCE", contextSequence);} + if (contextItem != null) + {context.getProfiler().message(this, Profiler.START_SEQUENCES, + "CONTEXT ITEM", contextItem.toSequence());} + } + context.expressionStart(this); + context.pushDocumentContext(); + try { + //Save the local variable stack + LocalVariable mark = context.markLocalVariables(false); + Sequence in; + LocalVariable var; + Sequence resultSequence = null; + try { + // evaluate input sequence + in = inputSequence.eval(contextSequence, null); + clearContext(getExpressionId(), in); + // Declare the iteration variable + var = createVariable(varName); + var.setSequenceType(sequenceType); + context.declareVariableBinding(var); + var.setValue(in); + if (sequenceType == null) + {var.checkType();} //Just because it makes conversions ! + var.setContextDocs(inputSequence.getContextDocSet()); + registerUpdateListener(in); + + if (sequenceType != null) { + Cardinality actualCardinality; + if (var.getValue().isEmpty()) {actualCardinality = Cardinality.EMPTY_SEQUENCE;} + else if (var.getValue().hasMany()) {actualCardinality = Cardinality._MANY;} + else {actualCardinality = Cardinality.EXACTLY_ONE;} + //Type.EMPTY is *not* a subtype of other types ; checking cardinality first + if (!sequenceType.getCardinality().isSuperCardinalityOrEqualOf(actualCardinality)) + {throw new XPathException(this, ErrorCodes.XPTY0004, + "Invalid cardinality for variable $" + varName + + ". Expected " + + sequenceType.getCardinality().getHumanDescription() + + ", got " + actualCardinality.getHumanDescription(), in);} + //TODO : ignore nodes right now ; they are returned as xs:untypedAtomicType + if (!Type.subTypeOf(sequenceType.getPrimaryType(), Type.NODE)) { + if (!var.getValue().isEmpty() && !Type.subTypeOf(var.getValue() + .getItemType(), sequenceType.getPrimaryType())) { + throw new XPathException(this, ErrorCodes.XPTY0004, + "Invalid type for variable $" + varName + + ". Expected " + Type.getTypeName(sequenceType.getPrimaryType()) + + ", got " +Type.getTypeName(var.getValue().getItemType()), in); + } + //Here is an attempt to process the nodes correctly + } else { + //Same as above : we probably may factorize + if (!var.getValue().isEmpty() && !sequenceType.checkType(var.getValue())) { + final Sequence value = var.getValue(); + final SequenceType valueType = new SequenceType(value.getItemType(), value.getCardinality()); + if ((!value.isEmpty()) && sequenceType.getPrimaryType() == Type.DOCUMENT && value.getItemType() == Type.DOCUMENT) { + // it's a document... we need to get the document element's name + final NodeValue nvItem = (NodeValue) value.itemAt(0); + final Document doc; + if (nvItem instanceof Document) { + doc = (Document) nvItem; + } else { + doc = nvItem.getOwnerDocument(); + } + if (doc != null) { + final Element elem = doc.getDocumentElement(); + if (elem != null) { + valueType.setNodeName(new QName(elem.getLocalName(), elem.getNamespaceURI())); + } + } + } + + if ((!value.isEmpty()) && sequenceType.getPrimaryType() == Type.ELEMENT && value.getItemType() == Type.ELEMENT) { + final NodeValue nvItem = (NodeValue) value.itemAt(0); + valueType.setNodeName(nvItem.getQName()); + } + + throw new XPathException( + this, + ErrorCodes.XPTY0004, + String.format("Invalid type for variable $%s. Expected %s, got %s", varName, sequenceType.toString(), valueType.toString()), in); + } + } + } + + resultSequence = returnExpr.eval(contextSequence, null); + + } finally { + // Restore the local variable stack + context.popLocalVariables(mark, resultSequence); + } + clearContext(getExpressionId(), in); + if (context.getProfiler().isEnabled()) + {context.getProfiler().end(this, "", resultSequence);} + if (resultSequence == null) + {return Sequence.EMPTY_SEQUENCE;} + if (!(resultSequence instanceof DeferredFunctionCall)) { + setActualReturnType(resultSequence.getItemType()); + } + if (getPreviousClause() == null) { + resultSequence = postEval(resultSequence); + } + return resultSequence; + } finally { + context.popDocumentContext(); + context.expressionEnd(this); + } + } + + /* (non-Javadoc) + * @see org.exist.xquery.Expression#dump(org.exist.xquery.util.ExpressionDumper) + */ + public void dump(ExpressionDumper dumper) { + dumper.display("let ", line); + dumper.startIndent(); + dumper.display("$").display(varName); + dumper.display(" := "); + inputSequence.dump(dumper); + dumper.endIndent(); + //TODO : QuantifiedExpr + if (returnExpr instanceof LetExpr) + {dumper.display(", ");} + else + {dumper.nl().display("return ");} + dumper.startIndent(); + returnExpr.dump(dumper); + dumper.endIndent(); + } + + public String toString() { + final StringBuilder result = new StringBuilder(); + result.append("let "); + result.append("$").append(varName); + result.append(" := "); + result.append(inputSequence.toString()); + result.append(" "); + //TODO : QuantifiedExpr + if (returnExpr instanceof LetExpr) + {result.append(", ");} + else + {result.append("return ");} + result.append(returnExpr.toString()); + return result.toString(); + } + + public void accept(ExpressionVisitor visitor) { + visitor.visitLetExpression(this); + } + + @Override + public boolean allowMixedNodesInReturn() { + return true; + } } \ No newline at end of file diff --git a/exist-core/src/main/java/org/exist/xquery/functions/fn/FnInvisibleXml.java b/exist-core/src/main/java/org/exist/xquery/functions/fn/FnInvisibleXml.java new file mode 100644 index 00000000000..a9f5a584bab --- /dev/null +++ b/exist-core/src/main/java/org/exist/xquery/functions/fn/FnInvisibleXml.java @@ -0,0 +1,157 @@ +package org.exist.xquery.functions.fn; + +import static org.exist.xquery.FunctionDSL.optParam; +import static org.exist.xquery.FunctionDSL.param; +import static org.exist.xquery.functions.fn.FnModule.functionSignature; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.exist.xquery.BasicFunction; +import org.exist.xquery.Cardinality; +import org.exist.xquery.ErrorCodes; +import org.exist.xquery.Expression; +import org.exist.xquery.Function; +import org.exist.xquery.FunctionCall; +import org.exist.xquery.FunctionFactory; +import org.exist.xquery.FunctionSignature; +import org.exist.xquery.XPathException; +import org.exist.xquery.XQueryContext; +import org.exist.xquery.functions.map.MapType; +import org.exist.xquery.value.BooleanValue; +import org.exist.xquery.value.FunctionReference; +import org.exist.xquery.value.FunctionReturnSequenceType; +import org.exist.xquery.value.Sequence; +import org.exist.xquery.value.StringValue; +import org.exist.xquery.value.Type; + +import de.bottlecaps.markup.Blitz; +import de.bottlecaps.markup.blitz.Parser; + +/** + * Implementation of + * fn:invisible-xml( + * $grammar as item()? := (), + * $options as map(*)? := {} + * ) as function(xs:string) as item() + * @see https://qt4cg.org/specifications/xpath-functions-40/Overview.html#ixml-functions + * + * To do: + * - Use the Markup Blitz serializer instead of parsing serialized XML. + */ +public class FnInvisibleXml extends BasicFunction +{ + + private static final String FS_INVISIBLE_XML_NAME = "invisible-xml"; + + static final FunctionSignature FS_INVISIBLE_XML = functionSignature( + FnInvisibleXml.FS_INVISIBLE_XML_NAME, + "Returns an ixml parser from a grammar. The parser returns an XML representation of the input string as parsed by the provided grammar.", + new FunctionReturnSequenceType(Type.FUNCTION_REFERENCE, Cardinality.EXACTLY_ONE, "A function that can be used to parse an input string."), + optParam("grammar", Type.ITEM, "The ixml grammar used to generate the parser"), + optParam("options", Type.MAP, "The options for the parser genarator and the parser itself") + ); + + private static final String IXML_GRAMMAR_RESOURCE = "org/exist/xquery/lib/ixml.ixml"; + + + public FnInvisibleXml(final XQueryContext context, final FunctionSignature signature) { + super(context, signature); + } + + @Override + public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException { + // Handle $grammar and $options parameters. + final String grammar; + if (args[0].isEmpty()) { + grammar = getIxmlGrammar(); + } else { + grammar = ((StringValue)args[0].itemAt(0)).getStringValue(); + } + final Blitz.Option[] options; + if (args[1].isEmpty()) { + options = new Blitz.Option[0]; + } else { + options = getOptions((MapType) args[1].itemAt(0)); + } + // Generate the Markup Blitz parser for the grammar. + final Parser parser = Blitz.generate(grammar, options); + // Make an IxmlParser function from the Markup Blitz parser. The signature is fn(xs:string) as item() + FunctionSignature parserSignature = functionSignature( + "generated-ixml-parser", + "Generated ixml parser, only used internally", + new FunctionReturnSequenceType(Type.ITEM, Cardinality.EXACTLY_ONE, "The result of parsing the input string"), + param("input", Type.STRING, "The input string") + ); + final IxmlParser ixmlParser = new IxmlParser(context, parserSignature, parser); + // Make a function reference that can be used as the result. + FunctionCall functionCall = FunctionFactory.wrap(context, ixmlParser); + return new FunctionReference(functionCall); + } + + // Read the ixml grammar from a resource. + private String getIxmlGrammar() throws XPathException { + try (final InputStream ixmlGrammarStream = FnInvisibleXml.class.getClassLoader().getResourceAsStream(IXML_GRAMMAR_RESOURCE)) { + if (ixmlGrammarStream == null) { + throw new XPathException(ErrorCodes.FODC0002, "The ixml grammar resource cannot be found at "+IXML_GRAMMAR_RESOURCE); + } + try ( InputStreamReader ixmlGrammarStreamReader = new InputStreamReader(ixmlGrammarStream); + BufferedReader reader = new BufferedReader(ixmlGrammarStreamReader)) { + return reader.lines().collect(Collectors.joining(System.lineSeparator())); + } + } + catch (IOException e) + { + throw new XPathException(ErrorCodes.FODC0002, "The ixml grammar resource cannot be opened at "+IXML_GRAMMAR_RESOURCE, e); + } + } + + // Get Markup Blitz options from the $options as map(*) parameter. + private Blitz.Option[] getOptions(final MapType options) throws XPathException { + List optionsList = new ArrayList(); + Sequence failOnError = options.get(new StringValue("fail-on-error")); + if (!failOnError.isEmpty() && ((BooleanValue)failOnError.itemAt(0).convertTo(Type.BOOLEAN)).getValue()) { + optionsList.add(Blitz.Option.FAIL_ON_ERROR); + } + return optionsList.stream().toArray(Blitz.Option[]::new); + } + + + /** + * A BasicFunction for the generated ixml parser. + */ + private static final class IxmlParser extends BasicFunction { + + private Parser parser; + + public IxmlParser(XQueryContext context, FunctionSignature signature, Parser parser) throws XPathException + { + super(context, signature); + this.parser = parser; + // We must set the arguments, which is not done automatically from the signature. + final List ixmlParserArgs = new ArrayList<>(1); + ixmlParserArgs.add(new Function.Placeholder(context)); + this.setArguments(ixmlParserArgs); + } + + @Override + public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException + { + final String input = ((StringValue)args[0].itemAt(0)).getStringValue(); + // Parse the input string. + final String output = parser.parse(input); + // The output is serialized XML, which we need to parse. + ParsingFunctions xmlParser = new ParsingFunctions(context, ParsingFunctions.signatures[0]); + final Sequence[] xmlParserArgs = new Sequence[1]; + xmlParserArgs[0] = new StringValue(output).toSequence(); + return xmlParser.eval(xmlParserArgs, contextSequence); + } + + } + +} diff --git a/exist-core/src/main/java/org/exist/xquery/functions/fn/FnModule.java b/exist-core/src/main/java/org/exist/xquery/functions/fn/FnModule.java index 61545789f63..4cec86f8abe 100644 --- a/exist-core/src/main/java/org/exist/xquery/functions/fn/FnModule.java +++ b/exist-core/src/main/java/org/exist/xquery/functions/fn/FnModule.java @@ -1,320 +1,321 @@ -/* - * eXist-db Open Source Native XML Database - * Copyright (C) 2001 The eXist-db Authors - * - * info@exist-db.org - * http://www.exist-db.org - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package org.exist.xquery.functions.fn; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.exist.dom.QName; -import org.exist.xquery.*; -import org.exist.xquery.value.FunctionParameterSequenceType; -import org.exist.xquery.value.FunctionReturnSequenceType; - -/** - * Module function definitions for xpath-functions module. - * - * @author Wolfgang Meier - * @author ljo - */ -public class FnModule extends AbstractInternalModule { - - public final static String PREFIX = ""; - public final static String INCLUSION_DATE = "2004-01-29"; - public final static String RELEASED_IN_VERSION = "pre eXist-1.0"; - - public final static FunctionDef[] functions = { - new FunctionDef(FunAbs.signature, FunAbs.class), - new FunctionDef(FunAvg.signature, FunAvg.class), - new FunctionDef(FunBaseURI.FS_BASE_URI_0, FunBaseURI.class), - new FunctionDef(FunBaseURI.FS_BASE_URI_1, FunBaseURI.class), - new FunctionDef(FunBaseURI.FS_STATIC_BASE_URI_0, FunBaseURI.class), - new FunctionDef(FunBoolean.signature, FunBoolean.class), - new FunctionDef(FunCeiling.signature, FunCeiling.class), - new FunctionDef(FunCodepointEqual.signature, FunCodepointEqual.class), - new FunctionDef(FunCodepointsToString.signature, FunCodepointsToString.class), - new FunctionDef(FunCollationKey.FS_COLLATION_KEY_SIGNATURES[0], FunCollationKey.class), - new FunctionDef(FunCollationKey.FS_COLLATION_KEY_SIGNATURES[1], FunCollationKey.class), - new FunctionDef(FunCompare.signatures[0], FunCompare.class), - new FunctionDef(FunCompare.signatures[1], FunCompare.class), - new FunctionDef(FunConcat.signature, FunConcat.class), - new FunctionDef(FunContains.signatures[0], FunContains.class), - new FunctionDef(FunContains.signatures[1], FunContains.class), - new FunctionDef(FunCount.signature, FunCount.class), - new FunctionDef(FunCurrentDateTime.fnCurrentDate, FunCurrentDateTime.class), - new FunctionDef(FunCurrentDateTime.fnCurrentDateTime, FunCurrentDateTime.class), - new FunctionDef(FunCurrentDateTime.fnCurrentTime, FunCurrentDateTime.class), - new FunctionDef(FunData.signatures[0], FunData.class), - new FunctionDef(FunData.signatures[1], FunData.class), - new FunctionDef(FunDateTime.signature, FunDateTime.class), - new FunctionDef(FunDeepEqual.signatures[0], FunDeepEqual.class), - new FunctionDef(FunDeepEqual.signatures[1], FunDeepEqual.class), - new FunctionDef(FunDefaultCollation.signature, FunDefaultCollation.class), - new FunctionDef(FnDefaultLanguage.FS_DEFAULT_LANGUAGE, FnDefaultLanguage.class), - new FunctionDef(FunDistinctValues.signatures[0], FunDistinctValues.class), - new FunctionDef(FunDistinctValues.signatures[1], FunDistinctValues.class), - new FunctionDef(FunDoc.signature, FunDoc.class), - new FunctionDef(FunDocAvailable.signature, FunDocAvailable.class), - new FunctionDef(FunDocumentURI.FS_DOCUMENT_URI_0, FunDocumentURI.class), - new FunctionDef(FunDocumentURI.FS_DOCUMENT_URI_1, FunDocumentURI.class), - new FunctionDef(FunElementWithId.FS_ELEMENT_WITH_ID_SIGNATURES[0], FunElementWithId.class), - new FunctionDef(FunElementWithId.FS_ELEMENT_WITH_ID_SIGNATURES[1], FunElementWithId.class), - new FunctionDef(FunEmpty.signature, FunEmpty.class), - new FunctionDef(FunEncodeForURI.signature, FunEncodeForURI.class), - new FunctionDef(FunEndsWith.signatures[0], FunEndsWith.class), - new FunctionDef(FunEndsWith.signatures[1], FunEndsWith.class), - new FunctionDef(FunError.signature[0], FunError.class), - new FunctionDef(FunError.signature[1], FunError.class), - new FunctionDef(FunError.signature[2], FunError.class), - new FunctionDef(FunError.signature[3], FunError.class), - new FunctionDef(FunEscapeHTMLURI.signature, FunEscapeHTMLURI.class), - new FunctionDef(FunEscapeURI.signature, FunEscapeURI.class), - new FunctionDef(FunExactlyOne.signature, FunExactlyOne.class), - new FunctionDef(FunExists.signature, FunExists.class), - new FunctionDef(FunFloor.signature, FunFloor.class), - new FunctionDef(FnFormatDates.FNS_FORMAT_DATETIME_2, FnFormatDates.class), - new FunctionDef(FnFormatDates.FNS_FORMAT_DATETIME_5, FnFormatDates.class), - new FunctionDef(FnFormatDates.FNS_FORMAT_DATE_2, FnFormatDates.class), - new FunctionDef(FnFormatDates.FNS_FORMAT_DATE_5, FnFormatDates.class), - new FunctionDef(FnFormatDates.FNS_FORMAT_TIME_2, FnFormatDates.class), - new FunctionDef(FnFormatDates.FNS_FORMAT_TIME_5, FnFormatDates.class), - new FunctionDef(FnFormatIntegers.FS_FORMAT_INTEGER[0], FnFormatIntegers.class), - new FunctionDef(FnFormatIntegers.FS_FORMAT_INTEGER[1], FnFormatIntegers.class), - new FunctionDef(FnFormatNumbers.FS_FORMAT_NUMBER[0], FnFormatNumbers.class), - new FunctionDef(FnFormatNumbers.FS_FORMAT_NUMBER[1], FnFormatNumbers.class), - new FunctionDef(FunGenerateId.signatures[0], FunGenerateId.class), - new FunctionDef(FunGenerateId.signatures[1], FunGenerateId.class), - new FunctionDef(FunGetDateComponent.fnDayFromDate, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnMonthFromDate, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnYearFromDate, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnTimezoneFromDate, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnHoursFromTime, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnMinutesFromTime, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnSecondsFromTime, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnTimezoneFromTime, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnDayFromDateTime, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnMonthFromDateTime, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnYearFromDateTime, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnHoursFromDateTime, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnMinutesFromDateTime, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnSecondsFromDateTime, FunGetDateComponent.class), - new FunctionDef(FunGetDateComponent.fnTimezoneFromDateTime, FunGetDateComponent.class), - new FunctionDef(FunGetDurationComponent.fnYearsFromDuration, FunGetDurationComponent.class), - new FunctionDef(FunGetDurationComponent.fnMonthsFromDuration, FunGetDurationComponent.class), - new FunctionDef(FunGetDurationComponent.fnDaysFromDuration, FunGetDurationComponent.class), - new FunctionDef(FunGetDurationComponent.fnHoursFromDuration, FunGetDurationComponent.class), - new FunctionDef(FunGetDurationComponent.fnMinutesFromDuration, FunGetDurationComponent.class), - new FunctionDef(FunGetDurationComponent.fnSecondsFromDuration, FunGetDurationComponent.class), - new FunctionDef(FunAdjustTimezone.fnAdjustDateToTimezone[0], FunAdjustTimezone.class), - new FunctionDef(FunAdjustTimezone.fnAdjustDateToTimezone[1], FunAdjustTimezone.class), - new FunctionDef(FunAdjustTimezone.fnAdjustTimeToTimezone[0], FunAdjustTimezone.class), - new FunctionDef(FunAdjustTimezone.fnAdjustTimeToTimezone[1], FunAdjustTimezone.class), - new FunctionDef(FunAdjustTimezone.fnAdjustDateTimeToTimezone[0], FunAdjustTimezone.class), - new FunctionDef(FunAdjustTimezone.fnAdjustDateTimeToTimezone[1], FunAdjustTimezone.class), - new FunctionDef(FunParseIetfDate.FNS_PARSE_IETF_DATE, FunParseIetfDate.class), - new FunctionDef(FnHasChildren.FNS_HAS_CHILDREN_0, FnHasChildren.class), - new FunctionDef(FnHasChildren.FNS_HAS_CHILDREN_1, FnHasChildren.class), - new FunctionDef(FunId.signature[0], FunId.class), - new FunctionDef(FunId.signature[1], FunId.class), - new FunctionDef(FunIdRef.signature[0], FunIdRef.class), - new FunctionDef(FunIdRef.signature[1], FunIdRef.class), - new FunctionDef(FunImplicitTimezone.signature, FunImplicitTimezone.class), - new FunctionDef(FunIndexOf.fnIndexOf[0], FunIndexOf.class), - new FunctionDef(FunIndexOf.fnIndexOf[1], FunIndexOf.class), - new FunctionDef(FnInnerMost.FNS_INNERMOST, FnInnerMost.class), - new FunctionDef(FunIRIToURI.signature, FunIRIToURI.class), - new FunctionDef(FunInScopePrefixes.signature, FunInScopePrefixes.class), - new FunctionDef(FunInsertBefore.signature, FunInsertBefore.class), - new FunctionDef(FunLang.signatures[0], FunLang.class), - new FunctionDef(FunLang.signatures[1], FunLang.class), - new FunctionDef(FunLast.signature, FunLast.class), - new FunctionDef(FunLocalName.signatures[0], FunLocalName.class), - new FunctionDef(FunLocalName.signatures[1], FunLocalName.class), - new FunctionDef(FunOnFunctions.signatures[0], FunOnFunctions.class), - new FunctionDef(FunOnFunctions.signatures[1], FunOnFunctions.class), - new FunctionDef(FunOnFunctions.signatures[2], FunOnFunctions.class), - new FunctionDef(FunMatches.signatures[0], FunMatches.class), - new FunctionDef(FunMatches.signatures[1], FunMatches.class), - new FunctionDef(FunMax.signatures[0], FunMax.class), - new FunctionDef(FunMax.signatures[1], FunMax.class), - new FunctionDef(FunMin.signatures[0], FunMin.class), - new FunctionDef(FunMin.signatures[1], FunMin.class), - new FunctionDef(FunNodeName.signatures[0], FunNodeName.class), - new FunctionDef(FunNodeName.signatures[1], FunNodeName.class), - new FunctionDef(FunName.signatures[0], FunName.class), - new FunctionDef(FunName.signatures[1], FunName.class), - new FunctionDef(FunNamespaceURI.signatures[0], FunNamespaceURI.class), - new FunctionDef(FunNamespaceURI.signatures[1], FunNamespaceURI.class), - new FunctionDef(FunNamespaceURIForPrefix.signature, FunNamespaceURIForPrefix.class), - new FunctionDef(FunNilled.FUNCTION_SIGNATURES_NILLED[0], FunNilled.class), - new FunctionDef(FunNilled.FUNCTION_SIGNATURES_NILLED[1], FunNilled.class), - new FunctionDef(FunNormalizeSpace.signatures[0], FunNormalizeSpace.class), - new FunctionDef(FunNormalizeSpace.signatures[1], FunNormalizeSpace.class), - new FunctionDef(FunNormalizeUnicode.signatures[0], FunNormalizeUnicode.class), - new FunctionDef(FunNormalizeUnicode.signatures[1], FunNormalizeUnicode.class), - new FunctionDef(FunNot.signature, FunNot.class), - new FunctionDef(FunNumber.signatures[0], FunNumber.class), - new FunctionDef(FunNumber.signatures[1], FunNumber.class), - new FunctionDef(FunOneOrMore.signature, FunOneOrMore.class), - new FunctionDef(FnOuterMost.FNS_OUTERMOST, FnOuterMost.class), - new FunctionDef(FunPath.FS_PATH_SIGNATURES[0], FunPath.class), - new FunctionDef(FunPath.FS_PATH_SIGNATURES[1], FunPath.class), - new FunctionDef(FunPosition.signature, FunPosition.class), - new FunctionDef(FunQName.signature, FunQName.class), - new FunctionDef(FunRemove.signature, FunRemove.class), - new FunctionDef(FunReplace.FS_REPLACE[0], FunReplace.class), - new FunctionDef(FunReplace.FS_REPLACE[1], FunReplace.class), - new FunctionDef(FunReverse.signature, FunReverse.class), - new FunctionDef(FunResolveURI.signatures[0], FunResolveURI.class), - new FunctionDef(FunResolveURI.signatures[1], FunResolveURI.class), - new FunctionDef(FunRoot.signatures[0], FunRoot.class), - new FunctionDef(FunRoot.signatures[1], FunRoot.class), - new FunctionDef(FunRound.FN_ROUND_SIGNATURES[0], FunRound.class), - new FunctionDef(FunRound.FN_ROUND_SIGNATURES[1], FunRound.class), - new FunctionDef(FunRoundHalfToEven.FN_ROUND_HALF_TO_EVEN_SIGNATURES[0], FunRoundHalfToEven.class), - new FunctionDef(FunRoundHalfToEven.FN_ROUND_HALF_TO_EVEN_SIGNATURES[1], FunRoundHalfToEven.class), - new FunctionDef(FunSerialize.signatures[0], FunSerialize.class), - new FunctionDef(FunSerialize.signatures[1], FunSerialize.class), - new FunctionDef(FunStartsWith.signatures[0], FunStartsWith.class), - new FunctionDef(FunStartsWith.signatures[1], FunStartsWith.class), - new FunctionDef(FunString.signatures[0], FunString.class), - new FunctionDef(FunString.signatures[1], FunString.class), - new FunctionDef(FunStringJoin.signatures[0], FunStringJoin.class), - new FunctionDef(FunStringJoin.signatures[1], FunStringJoin.class), - new FunctionDef(FunStringToCodepoints.signature, FunStringToCodepoints.class), - new FunctionDef(FunStrLength.signatures[0], FunStrLength.class), - new FunctionDef(FunStrLength.signatures[1], FunStrLength.class), - new FunctionDef(FunSubSequence.signatures[0], FunSubSequence.class), - new FunctionDef(FunSubSequence.signatures[1], FunSubSequence.class), - new FunctionDef(FunSubstring.signatures[0], FunSubstring.class), - new FunctionDef(FunSubstring.signatures[1], FunSubstring.class), - new FunctionDef(FunSubstringAfter.signatures[0], FunSubstringAfter.class), - new FunctionDef(FunSubstringAfter.signatures[1], FunSubstringAfter.class), - new FunctionDef(FunSubstringBefore.signatures[0], FunSubstringBefore.class), - new FunctionDef(FunSubstringBefore.signatures[1], FunSubstringBefore.class), - new FunctionDef(FunSum.signatures[0], FunSum.class), - new FunctionDef(FunSum.signatures[1], FunSum.class), - new FunctionDef(FunTokenize.FS_TOKENIZE[0], FunTokenize.class), - new FunctionDef(FunTokenize.FS_TOKENIZE[1], FunTokenize.class), - new FunctionDef(FunTokenize.FS_TOKENIZE[2], FunTokenize.class), - new FunctionDef(FunTrace.FS_TRACE1, FunTrace.class), - new FunctionDef(FunTrace.FS_TRACE2, FunTrace.class), - new FunctionDef(FnTransform.FS_TRANSFORM, FnTransform.class), - new FunctionDef(FunTranslate.signature, FunTranslate.class), - new FunctionDef(FunTrueOrFalse.fnTrue, FunTrueOrFalse.class), - new FunctionDef(FunTrueOrFalse.fnFalse, FunTrueOrFalse.class), - new FunctionDef(FunUpperOrLowerCase.fnLowerCase, FunUpperOrLowerCase.class), - new FunctionDef(FunUpperOrLowerCase.fnUpperCase, FunUpperOrLowerCase.class), - new FunctionDef(FunUriCollection.FS_URI_COLLECTION_SIGNATURES[0], FunUriCollection.class), - new FunctionDef(FunUriCollection.FS_URI_COLLECTION_SIGNATURES[1], FunUriCollection.class), - new FunctionDef(FunXmlToJson.FS_XML_TO_JSON[0], FunXmlToJson.class), - new FunctionDef(FunXmlToJson.FS_XML_TO_JSON[1], FunXmlToJson.class), - new FunctionDef(FunZeroOrOne.signature, FunZeroOrOne.class), - new FunctionDef(FunUnordered.signature, FunUnordered.class), - new FunctionDef(ExtCollection.signature, ExtCollection.class), - new FunctionDef(QNameFunctions.localNameFromQName, QNameFunctions.class), - new FunctionDef(QNameFunctions.prefixFromQName, QNameFunctions.class), - new FunctionDef(QNameFunctions.namespaceURIFromQName, QNameFunctions.class), - new FunctionDef(FunResolveQName.signature, FunResolveQName.class), - new FunctionDef(FunEquals.signatures[0], FunEquals.class), - new FunctionDef(FunEquals.signatures[1], FunEquals.class), - new FunctionDef(FunAnalyzeString.signatures[0], FunAnalyzeString.class), - new FunctionDef(FunAnalyzeString.signatures[1], FunAnalyzeString.class), - new FunctionDef(FunHeadTail.signatures[0], FunHeadTail.class), - new FunctionDef(FunHeadTail.signatures[1], FunHeadTail.class), - new FunctionDef(FunHigherOrderFun.FN_FOR_EACH, FunHigherOrderFun.class), - new FunctionDef(FunHigherOrderFun.FN_FOR_EACH_PAIR, FunHigherOrderFun.class), - new FunctionDef(FunHigherOrderFun.FN_FILTER, FunHigherOrderFun.class), - new FunctionDef(FunHigherOrderFun.FN_FOLD_LEFT, FunHigherOrderFun.class), - new FunctionDef(FunHigherOrderFun.FN_FOLD_RIGHT, FunHigherOrderFun.class), - new FunctionDef(FunHigherOrderFun.FN_APPLY, FunHigherOrderFun.class), - new FunctionDef(FunEnvironment.signature[0], FunEnvironment.class), - new FunctionDef(FunEnvironment.signature[1], FunEnvironment.class), - new FunctionDef(ParsingFunctions.signatures[0], ParsingFunctions.class), - new FunctionDef(ParsingFunctions.signatures[1], ParsingFunctions.class), - new FunctionDef(JSON.FS_PARSE_JSON[0], JSON.class), - new FunctionDef(JSON.FS_PARSE_JSON[1], JSON.class), - new FunctionDef(JSON.FS_JSON_DOC[0], JSON.class), - new FunctionDef(JSON.FS_JSON_DOC[1], JSON.class), - new FunctionDef(JSON.FS_JSON_TO_XML[0], JSON.class), - new FunctionDef(JSON.FS_JSON_TO_XML[1], JSON.class), - new FunctionDef(LoadXQueryModule.LOAD_XQUERY_MODULE_1, LoadXQueryModule.class), - new FunctionDef(LoadXQueryModule.LOAD_XQUERY_MODULE_2, LoadXQueryModule.class), - new FunctionDef(FunSort.signatures[0], FunSort.class), - new FunctionDef(FunSort.signatures[1], FunSort.class), - new FunctionDef(FunSort.signatures[2], FunSort.class), - new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT[0], FunUnparsedText.class), - new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT[1], FunUnparsedText.class), - new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT_LINES[0], FunUnparsedText.class), - new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT_LINES[1], FunUnparsedText.class), - new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT_AVAILABLE[0], FunUnparsedText.class), - new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT_AVAILABLE[1], FunUnparsedText.class), - new FunctionDef(FnRandomNumberGenerator.FS_RANDOM_NUMBER_GENERATOR[0], FnRandomNumberGenerator.class), - new FunctionDef(FnRandomNumberGenerator.FS_RANDOM_NUMBER_GENERATOR[1], FnRandomNumberGenerator.class), - new FunctionDef(FunContainsToken.FS_CONTAINS_TOKEN[0], FunContainsToken.class), - new FunctionDef(FunContainsToken.FS_CONTAINS_TOKEN[1], FunContainsToken.class) - }; - - static { - Arrays.sort(functions, new FunctionComparator()); - } - - public final static ErrorCodes.ErrorCode SENR0001 = new ErrorCodes.ErrorCode("SENR0001", "serialization error in fn:serialize"); - public final static ErrorCodes.ErrorCode SEPM0019 = new ErrorCodes.ErrorCode("SEPM0019", "It is an error if an instance of the data model " + - "used to specify the settings of serialization parameters specifies the value of the same parameter more than once."); - - public FnModule(Map> parameters) { - super(functions, parameters, true); - } - - @Override - public String getDescription() { - return "A module with the XQuery/XPath Core Library Functions"; - } - - @Override - public String getNamespaceURI() { - return Function.BUILTIN_FUNCTION_NS; - } - - @Override - public String getDefaultPrefix() { - return PREFIX; - } - - @Override - public String getReleaseVersion() { - return RELEASED_IN_VERSION; - } - - static FunctionSignature functionSignature(final String name, final String description, - final FunctionReturnSequenceType returnType, final FunctionParameterSequenceType... paramTypes) { - return FunctionDSL.functionSignature(new QName(name, Function.BUILTIN_FUNCTION_NS), description, - returnType, paramTypes); - } - - static FunctionSignature[] functionSignatures(final String name, final String description, - final FunctionReturnSequenceType returnType, final FunctionParameterSequenceType[][] variableParamTypes) { - return FunctionDSL.functionSignatures(new QName(name, Function.BUILTIN_FUNCTION_NS), description, - returnType, variableParamTypes); - } -} +/* + * eXist-db Open Source Native XML Database + * Copyright (C) 2001 The eXist-db Authors + * + * info@exist-db.org + * http://www.exist-db.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package org.exist.xquery.functions.fn; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.exist.dom.QName; +import org.exist.xquery.*; +import org.exist.xquery.value.FunctionParameterSequenceType; +import org.exist.xquery.value.FunctionReturnSequenceType; + +/** + * Module function definitions for xpath-functions module. + * + * @author Wolfgang Meier + * @author ljo + */ +public class FnModule extends AbstractInternalModule { + + public final static String PREFIX = ""; + public final static String INCLUSION_DATE = "2004-01-29"; + public final static String RELEASED_IN_VERSION = "pre eXist-1.0"; + + public final static FunctionDef[] functions = { + new FunctionDef(FunAbs.signature, FunAbs.class), + new FunctionDef(FunAvg.signature, FunAvg.class), + new FunctionDef(FunBaseURI.FS_BASE_URI_0, FunBaseURI.class), + new FunctionDef(FunBaseURI.FS_BASE_URI_1, FunBaseURI.class), + new FunctionDef(FunBaseURI.FS_STATIC_BASE_URI_0, FunBaseURI.class), + new FunctionDef(FunBoolean.signature, FunBoolean.class), + new FunctionDef(FunCeiling.signature, FunCeiling.class), + new FunctionDef(FunCodepointEqual.signature, FunCodepointEqual.class), + new FunctionDef(FunCodepointsToString.signature, FunCodepointsToString.class), + new FunctionDef(FunCollationKey.FS_COLLATION_KEY_SIGNATURES[0], FunCollationKey.class), + new FunctionDef(FunCollationKey.FS_COLLATION_KEY_SIGNATURES[1], FunCollationKey.class), + new FunctionDef(FunCompare.signatures[0], FunCompare.class), + new FunctionDef(FunCompare.signatures[1], FunCompare.class), + new FunctionDef(FunConcat.signature, FunConcat.class), + new FunctionDef(FunContains.signatures[0], FunContains.class), + new FunctionDef(FunContains.signatures[1], FunContains.class), + new FunctionDef(FunCount.signature, FunCount.class), + new FunctionDef(FunCurrentDateTime.fnCurrentDate, FunCurrentDateTime.class), + new FunctionDef(FunCurrentDateTime.fnCurrentDateTime, FunCurrentDateTime.class), + new FunctionDef(FunCurrentDateTime.fnCurrentTime, FunCurrentDateTime.class), + new FunctionDef(FunData.signatures[0], FunData.class), + new FunctionDef(FunData.signatures[1], FunData.class), + new FunctionDef(FunDateTime.signature, FunDateTime.class), + new FunctionDef(FunDeepEqual.signatures[0], FunDeepEqual.class), + new FunctionDef(FunDeepEqual.signatures[1], FunDeepEqual.class), + new FunctionDef(FunDefaultCollation.signature, FunDefaultCollation.class), + new FunctionDef(FnDefaultLanguage.FS_DEFAULT_LANGUAGE, FnDefaultLanguage.class), + new FunctionDef(FunDistinctValues.signatures[0], FunDistinctValues.class), + new FunctionDef(FunDistinctValues.signatures[1], FunDistinctValues.class), + new FunctionDef(FunDoc.signature, FunDoc.class), + new FunctionDef(FunDocAvailable.signature, FunDocAvailable.class), + new FunctionDef(FunDocumentURI.FS_DOCUMENT_URI_0, FunDocumentURI.class), + new FunctionDef(FunDocumentURI.FS_DOCUMENT_URI_1, FunDocumentURI.class), + new FunctionDef(FunElementWithId.FS_ELEMENT_WITH_ID_SIGNATURES[0], FunElementWithId.class), + new FunctionDef(FunElementWithId.FS_ELEMENT_WITH_ID_SIGNATURES[1], FunElementWithId.class), + new FunctionDef(FunEmpty.signature, FunEmpty.class), + new FunctionDef(FunEncodeForURI.signature, FunEncodeForURI.class), + new FunctionDef(FunEndsWith.signatures[0], FunEndsWith.class), + new FunctionDef(FunEndsWith.signatures[1], FunEndsWith.class), + new FunctionDef(FunError.signature[0], FunError.class), + new FunctionDef(FunError.signature[1], FunError.class), + new FunctionDef(FunError.signature[2], FunError.class), + new FunctionDef(FunError.signature[3], FunError.class), + new FunctionDef(FunEscapeHTMLURI.signature, FunEscapeHTMLURI.class), + new FunctionDef(FunEscapeURI.signature, FunEscapeURI.class), + new FunctionDef(FunExactlyOne.signature, FunExactlyOne.class), + new FunctionDef(FunExists.signature, FunExists.class), + new FunctionDef(FunFloor.signature, FunFloor.class), + new FunctionDef(FnFormatDates.FNS_FORMAT_DATETIME_2, FnFormatDates.class), + new FunctionDef(FnFormatDates.FNS_FORMAT_DATETIME_5, FnFormatDates.class), + new FunctionDef(FnFormatDates.FNS_FORMAT_DATE_2, FnFormatDates.class), + new FunctionDef(FnFormatDates.FNS_FORMAT_DATE_5, FnFormatDates.class), + new FunctionDef(FnFormatDates.FNS_FORMAT_TIME_2, FnFormatDates.class), + new FunctionDef(FnFormatDates.FNS_FORMAT_TIME_5, FnFormatDates.class), + new FunctionDef(FnFormatIntegers.FS_FORMAT_INTEGER[0], FnFormatIntegers.class), + new FunctionDef(FnFormatIntegers.FS_FORMAT_INTEGER[1], FnFormatIntegers.class), + new FunctionDef(FnFormatNumbers.FS_FORMAT_NUMBER[0], FnFormatNumbers.class), + new FunctionDef(FnFormatNumbers.FS_FORMAT_NUMBER[1], FnFormatNumbers.class), + new FunctionDef(FunGenerateId.signatures[0], FunGenerateId.class), + new FunctionDef(FunGenerateId.signatures[1], FunGenerateId.class), + new FunctionDef(FunGetDateComponent.fnDayFromDate, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnMonthFromDate, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnYearFromDate, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnTimezoneFromDate, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnHoursFromTime, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnMinutesFromTime, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnSecondsFromTime, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnTimezoneFromTime, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnDayFromDateTime, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnMonthFromDateTime, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnYearFromDateTime, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnHoursFromDateTime, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnMinutesFromDateTime, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnSecondsFromDateTime, FunGetDateComponent.class), + new FunctionDef(FunGetDateComponent.fnTimezoneFromDateTime, FunGetDateComponent.class), + new FunctionDef(FunGetDurationComponent.fnYearsFromDuration, FunGetDurationComponent.class), + new FunctionDef(FunGetDurationComponent.fnMonthsFromDuration, FunGetDurationComponent.class), + new FunctionDef(FunGetDurationComponent.fnDaysFromDuration, FunGetDurationComponent.class), + new FunctionDef(FunGetDurationComponent.fnHoursFromDuration, FunGetDurationComponent.class), + new FunctionDef(FunGetDurationComponent.fnMinutesFromDuration, FunGetDurationComponent.class), + new FunctionDef(FunGetDurationComponent.fnSecondsFromDuration, FunGetDurationComponent.class), + new FunctionDef(FunAdjustTimezone.fnAdjustDateToTimezone[0], FunAdjustTimezone.class), + new FunctionDef(FunAdjustTimezone.fnAdjustDateToTimezone[1], FunAdjustTimezone.class), + new FunctionDef(FunAdjustTimezone.fnAdjustTimeToTimezone[0], FunAdjustTimezone.class), + new FunctionDef(FunAdjustTimezone.fnAdjustTimeToTimezone[1], FunAdjustTimezone.class), + new FunctionDef(FunAdjustTimezone.fnAdjustDateTimeToTimezone[0], FunAdjustTimezone.class), + new FunctionDef(FunAdjustTimezone.fnAdjustDateTimeToTimezone[1], FunAdjustTimezone.class), + new FunctionDef(FunParseIetfDate.FNS_PARSE_IETF_DATE, FunParseIetfDate.class), + new FunctionDef(FnHasChildren.FNS_HAS_CHILDREN_0, FnHasChildren.class), + new FunctionDef(FnHasChildren.FNS_HAS_CHILDREN_1, FnHasChildren.class), + new FunctionDef(FunId.signature[0], FunId.class), + new FunctionDef(FunId.signature[1], FunId.class), + new FunctionDef(FunIdRef.signature[0], FunIdRef.class), + new FunctionDef(FunIdRef.signature[1], FunIdRef.class), + new FunctionDef(FunImplicitTimezone.signature, FunImplicitTimezone.class), + new FunctionDef(FunIndexOf.fnIndexOf[0], FunIndexOf.class), + new FunctionDef(FunIndexOf.fnIndexOf[1], FunIndexOf.class), + new FunctionDef(FnInnerMost.FNS_INNERMOST, FnInnerMost.class), + new FunctionDef(FunIRIToURI.signature, FunIRIToURI.class), + new FunctionDef(FunInScopePrefixes.signature, FunInScopePrefixes.class), + new FunctionDef(FunInsertBefore.signature, FunInsertBefore.class), + new FunctionDef(FunLang.signatures[0], FunLang.class), + new FunctionDef(FunLang.signatures[1], FunLang.class), + new FunctionDef(FunLast.signature, FunLast.class), + new FunctionDef(FunLocalName.signatures[0], FunLocalName.class), + new FunctionDef(FunLocalName.signatures[1], FunLocalName.class), + new FunctionDef(FunOnFunctions.signatures[0], FunOnFunctions.class), + new FunctionDef(FunOnFunctions.signatures[1], FunOnFunctions.class), + new FunctionDef(FunOnFunctions.signatures[2], FunOnFunctions.class), + new FunctionDef(FunMatches.signatures[0], FunMatches.class), + new FunctionDef(FunMatches.signatures[1], FunMatches.class), + new FunctionDef(FunMax.signatures[0], FunMax.class), + new FunctionDef(FunMax.signatures[1], FunMax.class), + new FunctionDef(FunMin.signatures[0], FunMin.class), + new FunctionDef(FunMin.signatures[1], FunMin.class), + new FunctionDef(FunNodeName.signatures[0], FunNodeName.class), + new FunctionDef(FunNodeName.signatures[1], FunNodeName.class), + new FunctionDef(FunName.signatures[0], FunName.class), + new FunctionDef(FunName.signatures[1], FunName.class), + new FunctionDef(FunNamespaceURI.signatures[0], FunNamespaceURI.class), + new FunctionDef(FunNamespaceURI.signatures[1], FunNamespaceURI.class), + new FunctionDef(FunNamespaceURIForPrefix.signature, FunNamespaceURIForPrefix.class), + new FunctionDef(FunNilled.FUNCTION_SIGNATURES_NILLED[0], FunNilled.class), + new FunctionDef(FunNilled.FUNCTION_SIGNATURES_NILLED[1], FunNilled.class), + new FunctionDef(FunNormalizeSpace.signatures[0], FunNormalizeSpace.class), + new FunctionDef(FunNormalizeSpace.signatures[1], FunNormalizeSpace.class), + new FunctionDef(FunNormalizeUnicode.signatures[0], FunNormalizeUnicode.class), + new FunctionDef(FunNormalizeUnicode.signatures[1], FunNormalizeUnicode.class), + new FunctionDef(FunNot.signature, FunNot.class), + new FunctionDef(FunNumber.signatures[0], FunNumber.class), + new FunctionDef(FunNumber.signatures[1], FunNumber.class), + new FunctionDef(FunOneOrMore.signature, FunOneOrMore.class), + new FunctionDef(FnOuterMost.FNS_OUTERMOST, FnOuterMost.class), + new FunctionDef(FunPath.FS_PATH_SIGNATURES[0], FunPath.class), + new FunctionDef(FunPath.FS_PATH_SIGNATURES[1], FunPath.class), + new FunctionDef(FunPosition.signature, FunPosition.class), + new FunctionDef(FunQName.signature, FunQName.class), + new FunctionDef(FunRemove.signature, FunRemove.class), + new FunctionDef(FunReplace.FS_REPLACE[0], FunReplace.class), + new FunctionDef(FunReplace.FS_REPLACE[1], FunReplace.class), + new FunctionDef(FunReverse.signature, FunReverse.class), + new FunctionDef(FunResolveURI.signatures[0], FunResolveURI.class), + new FunctionDef(FunResolveURI.signatures[1], FunResolveURI.class), + new FunctionDef(FunRoot.signatures[0], FunRoot.class), + new FunctionDef(FunRoot.signatures[1], FunRoot.class), + new FunctionDef(FunRound.FN_ROUND_SIGNATURES[0], FunRound.class), + new FunctionDef(FunRound.FN_ROUND_SIGNATURES[1], FunRound.class), + new FunctionDef(FunRoundHalfToEven.FN_ROUND_HALF_TO_EVEN_SIGNATURES[0], FunRoundHalfToEven.class), + new FunctionDef(FunRoundHalfToEven.FN_ROUND_HALF_TO_EVEN_SIGNATURES[1], FunRoundHalfToEven.class), + new FunctionDef(FunSerialize.signatures[0], FunSerialize.class), + new FunctionDef(FunSerialize.signatures[1], FunSerialize.class), + new FunctionDef(FunStartsWith.signatures[0], FunStartsWith.class), + new FunctionDef(FunStartsWith.signatures[1], FunStartsWith.class), + new FunctionDef(FunString.signatures[0], FunString.class), + new FunctionDef(FunString.signatures[1], FunString.class), + new FunctionDef(FunStringJoin.signatures[0], FunStringJoin.class), + new FunctionDef(FunStringJoin.signatures[1], FunStringJoin.class), + new FunctionDef(FunStringToCodepoints.signature, FunStringToCodepoints.class), + new FunctionDef(FunStrLength.signatures[0], FunStrLength.class), + new FunctionDef(FunStrLength.signatures[1], FunStrLength.class), + new FunctionDef(FunSubSequence.signatures[0], FunSubSequence.class), + new FunctionDef(FunSubSequence.signatures[1], FunSubSequence.class), + new FunctionDef(FunSubstring.signatures[0], FunSubstring.class), + new FunctionDef(FunSubstring.signatures[1], FunSubstring.class), + new FunctionDef(FunSubstringAfter.signatures[0], FunSubstringAfter.class), + new FunctionDef(FunSubstringAfter.signatures[1], FunSubstringAfter.class), + new FunctionDef(FunSubstringBefore.signatures[0], FunSubstringBefore.class), + new FunctionDef(FunSubstringBefore.signatures[1], FunSubstringBefore.class), + new FunctionDef(FunSum.signatures[0], FunSum.class), + new FunctionDef(FunSum.signatures[1], FunSum.class), + new FunctionDef(FunTokenize.FS_TOKENIZE[0], FunTokenize.class), + new FunctionDef(FunTokenize.FS_TOKENIZE[1], FunTokenize.class), + new FunctionDef(FunTokenize.FS_TOKENIZE[2], FunTokenize.class), + new FunctionDef(FunTrace.FS_TRACE1, FunTrace.class), + new FunctionDef(FunTrace.FS_TRACE2, FunTrace.class), + new FunctionDef(FnTransform.FS_TRANSFORM, FnTransform.class), + new FunctionDef(FunTranslate.signature, FunTranslate.class), + new FunctionDef(FunTrueOrFalse.fnTrue, FunTrueOrFalse.class), + new FunctionDef(FunTrueOrFalse.fnFalse, FunTrueOrFalse.class), + new FunctionDef(FunUpperOrLowerCase.fnLowerCase, FunUpperOrLowerCase.class), + new FunctionDef(FunUpperOrLowerCase.fnUpperCase, FunUpperOrLowerCase.class), + new FunctionDef(FunUriCollection.FS_URI_COLLECTION_SIGNATURES[0], FunUriCollection.class), + new FunctionDef(FunUriCollection.FS_URI_COLLECTION_SIGNATURES[1], FunUriCollection.class), + new FunctionDef(FunXmlToJson.FS_XML_TO_JSON[0], FunXmlToJson.class), + new FunctionDef(FunXmlToJson.FS_XML_TO_JSON[1], FunXmlToJson.class), + new FunctionDef(FunZeroOrOne.signature, FunZeroOrOne.class), + new FunctionDef(FunUnordered.signature, FunUnordered.class), + new FunctionDef(ExtCollection.signature, ExtCollection.class), + new FunctionDef(QNameFunctions.localNameFromQName, QNameFunctions.class), + new FunctionDef(QNameFunctions.prefixFromQName, QNameFunctions.class), + new FunctionDef(QNameFunctions.namespaceURIFromQName, QNameFunctions.class), + new FunctionDef(FunResolveQName.signature, FunResolveQName.class), + new FunctionDef(FunEquals.signatures[0], FunEquals.class), + new FunctionDef(FunEquals.signatures[1], FunEquals.class), + new FunctionDef(FunAnalyzeString.signatures[0], FunAnalyzeString.class), + new FunctionDef(FunAnalyzeString.signatures[1], FunAnalyzeString.class), + new FunctionDef(FunHeadTail.signatures[0], FunHeadTail.class), + new FunctionDef(FunHeadTail.signatures[1], FunHeadTail.class), + new FunctionDef(FunHigherOrderFun.FN_FOR_EACH, FunHigherOrderFun.class), + new FunctionDef(FunHigherOrderFun.FN_FOR_EACH_PAIR, FunHigherOrderFun.class), + new FunctionDef(FunHigherOrderFun.FN_FILTER, FunHigherOrderFun.class), + new FunctionDef(FunHigherOrderFun.FN_FOLD_LEFT, FunHigherOrderFun.class), + new FunctionDef(FunHigherOrderFun.FN_FOLD_RIGHT, FunHigherOrderFun.class), + new FunctionDef(FunHigherOrderFun.FN_APPLY, FunHigherOrderFun.class), + new FunctionDef(FunEnvironment.signature[0], FunEnvironment.class), + new FunctionDef(FunEnvironment.signature[1], FunEnvironment.class), + new FunctionDef(ParsingFunctions.signatures[0], ParsingFunctions.class), + new FunctionDef(ParsingFunctions.signatures[1], ParsingFunctions.class), + new FunctionDef(JSON.FS_PARSE_JSON[0], JSON.class), + new FunctionDef(JSON.FS_PARSE_JSON[1], JSON.class), + new FunctionDef(JSON.FS_JSON_DOC[0], JSON.class), + new FunctionDef(JSON.FS_JSON_DOC[1], JSON.class), + new FunctionDef(JSON.FS_JSON_TO_XML[0], JSON.class), + new FunctionDef(JSON.FS_JSON_TO_XML[1], JSON.class), + new FunctionDef(LoadXQueryModule.LOAD_XQUERY_MODULE_1, LoadXQueryModule.class), + new FunctionDef(LoadXQueryModule.LOAD_XQUERY_MODULE_2, LoadXQueryModule.class), + new FunctionDef(FunSort.signatures[0], FunSort.class), + new FunctionDef(FunSort.signatures[1], FunSort.class), + new FunctionDef(FunSort.signatures[2], FunSort.class), + new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT[0], FunUnparsedText.class), + new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT[1], FunUnparsedText.class), + new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT_LINES[0], FunUnparsedText.class), + new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT_LINES[1], FunUnparsedText.class), + new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT_AVAILABLE[0], FunUnparsedText.class), + new FunctionDef(FunUnparsedText.FS_UNPARSED_TEXT_AVAILABLE[1], FunUnparsedText.class), + new FunctionDef(FnRandomNumberGenerator.FS_RANDOM_NUMBER_GENERATOR[0], FnRandomNumberGenerator.class), + new FunctionDef(FnRandomNumberGenerator.FS_RANDOM_NUMBER_GENERATOR[1], FnRandomNumberGenerator.class), + new FunctionDef(FunContainsToken.FS_CONTAINS_TOKEN[0], FunContainsToken.class), + new FunctionDef(FunContainsToken.FS_CONTAINS_TOKEN[1], FunContainsToken.class), + new FunctionDef(FnInvisibleXml.FS_INVISIBLE_XML, FnInvisibleXml.class) + }; + + static { + Arrays.sort(functions, new FunctionComparator()); + } + + public final static ErrorCodes.ErrorCode SENR0001 = new ErrorCodes.ErrorCode("SENR0001", "serialization error in fn:serialize"); + public final static ErrorCodes.ErrorCode SEPM0019 = new ErrorCodes.ErrorCode("SEPM0019", "It is an error if an instance of the data model " + + "used to specify the settings of serialization parameters specifies the value of the same parameter more than once."); + + public FnModule(Map> parameters) { + super(functions, parameters, true); + } + + @Override + public String getDescription() { + return "A module with the XQuery/XPath Core Library Functions"; + } + + @Override + public String getNamespaceURI() { + return Function.BUILTIN_FUNCTION_NS; + } + + @Override + public String getDefaultPrefix() { + return PREFIX; + } + + @Override + public String getReleaseVersion() { + return RELEASED_IN_VERSION; + } + + static FunctionSignature functionSignature(final String name, final String description, + final FunctionReturnSequenceType returnType, final FunctionParameterSequenceType... paramTypes) { + return FunctionDSL.functionSignature(new QName(name, Function.BUILTIN_FUNCTION_NS), description, + returnType, paramTypes); + } + + static FunctionSignature[] functionSignatures(final String name, final String description, + final FunctionReturnSequenceType returnType, final FunctionParameterSequenceType[][] variableParamTypes) { + return FunctionDSL.functionSignatures(new QName(name, Function.BUILTIN_FUNCTION_NS), description, + returnType, variableParamTypes); + } +} diff --git a/exist-core/src/main/java/org/exist/xquery/functions/fn/FunHigherOrderFun.java b/exist-core/src/main/java/org/exist/xquery/functions/fn/FunHigherOrderFun.java index 599f1c9ac14..1656fe65773 100644 --- a/exist-core/src/main/java/org/exist/xquery/functions/fn/FunHigherOrderFun.java +++ b/exist-core/src/main/java/org/exist/xquery/functions/fn/FunHigherOrderFun.java @@ -224,6 +224,9 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) } } else if (isCalledAs("apply")) { try (final FunctionReference ref = (FunctionReference) args[0].itemAt(0)) { + // Sometimes the function reference has no realUser in its context. In that case we set it using prepareForExecution(). + if (ref.getCall().getContext().getRealUser() == null) + ref.getCall().getContext().prepareForExecution(); ref.analyze(cachedContextInfo); final ArrayType array = (ArrayType) args[1].itemAt(0); if (funcRefHasDifferentArity(ref, array.getSize())) { diff --git a/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Convert.java b/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Convert.java index 35e5b310763..0fcbf32e5d2 100644 --- a/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Convert.java +++ b/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Convert.java @@ -26,17 +26,23 @@ import net.sf.saxon.type.BuiltInAtomicType; import org.exist.dom.QName; import org.exist.dom.memtree.DocumentImpl; +import org.exist.dom.persistent.NodeProxy; import org.exist.xquery.ErrorCodes; import org.exist.xquery.XPathException; import org.exist.xquery.functions.array.ArrayType; import org.exist.xquery.functions.fn.FnTransform; +import org.exist.xquery.functions.map.AbstractMapType; import org.exist.xquery.value.*; import org.w3c.dom.Document; import org.w3c.dom.Node; +import io.lacuna.bifurcan.IEntry; + import javax.xml.transform.dom.DOMSource; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Type conversion to and from Saxon @@ -119,11 +125,18 @@ static net.sf.saxon.s9api.QName of(final QNameValue qName) { } XdmValue of(final Item item) throws XPathException { + if (item instanceof NodeProxy) { + return ofNode(((NodeProxy) item).getNode()); + } final int itemType = item.getType(); if (Type.subTypeOf(itemType, Type.ATOMIC)) { return ofAtomic((AtomicValue) item); } else if (Type.subTypeOf(itemType, Type.NODE)) { return ofNode((Node) item); + } else if (Type.subTypeOf(itemType, Type.MAP)) { + return ofMap((AbstractMapType) item); + } else if (Type.subTypeOf(itemType, Type.ARRAY)) { + return ofArray((ArrayType) item); } throw new XPathException(ErrorCodes.XPTY0004, "Item " + item + " of type " + Type.getTypeName(itemType) + COULD_NOT_BE_CONVERTED + "XdmValue"); @@ -140,14 +153,12 @@ static private XdmValue ofAtomic(final AtomicValue atomicValue) throws XPathExce } else if (Type.subTypeOf(itemType, Type.STRING)) { return XdmValue.makeValue(((StringValue) atomicValue).getStringValue()); } - throw new XPathException(ErrorCodes.XPTY0004, "Atomic value " + atomicValue + " of type " + Type.getTypeName(itemType) + COULD_NOT_BE_CONVERTED + "XdmValue"); } private XdmValue ofNode(final Node node) throws XPathException { - final DocumentBuilder sourceBuilder = newDocumentBuilder(); try { if (node instanceof DocumentImpl) { @@ -167,6 +178,25 @@ private XdmValue ofNode(final Node node) throws XPathException { } } + private XdmValue ofMap(final AbstractMapType map) throws XPathException { + Map xdmMap = new HashMap(); + for (IEntry entry : map) { + XdmAtomicValue key = (XdmAtomicValue) ofAtomic(entry.key()); + XdmValue value = of(entry.value()); + xdmMap.put(key, value); + } + return new XdmMap(xdmMap); + } + + private XdmValue ofArray(final ArrayType array) throws XPathException { + int size = array.getSize(); + XdmValue[] members = new XdmValue[size]; + for (int i = 0; i < size; ++i) { + members[i] = of(array.get(i)); + } + return new XdmArray(members); + } + XdmValue[] of(final ArrayType values) throws XPathException { final int size = values.getSize(); final XdmValue[] result = new XdmValue[size]; diff --git a/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Transform.java b/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Transform.java index 34e2b5e33f1..62aa04ebbde 100644 --- a/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Transform.java +++ b/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/Transform.java @@ -192,8 +192,10 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro final Transform.TemplateInvocation invocation = new Transform.TemplateInvocation( options, sourceNode, delivery, xslt30Transformer, resultDocuments); return invocation.invoke(); - } catch (final SaxonApiException | UncheckedXPathException e) { - throw originalXPathException("Could not transform input: ", e, ErrorCodes.FOXT0003); + } catch (final SaxonApiException e) { + throw originalXPathException("Could not transform with "+options.xsltSource._1+" line "+e.getLineNumber()+": ", e, ErrorCodes.FOXT0003); + } catch (final UncheckedXPathException e) { + throw originalXPathException("Could not transform with "+options.xsltSource._1+" line "+e.getXPathException().getLocationAsString()+": ", e, ErrorCodes.FOXT0003); } } else { diff --git a/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/TreeUtils.java b/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/TreeUtils.java index dae639ea3bf..e710d6ec60d 100644 --- a/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/TreeUtils.java +++ b/exist-core/src/main/java/org/exist/xquery/functions/fn/transform/TreeUtils.java @@ -24,6 +24,7 @@ import net.sf.saxon.s9api.XdmNode; import org.exist.xquery.value.NodeValue; +import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -64,20 +65,41 @@ static StringBuilder pathTo(final Node node) { static List treeIndex(final Node node) { final Node parent = node.getParentNode(); if (parent == null) { - return new ArrayList<>(); + final List index = new ArrayList<>(); + // The root element always index 0 within the document node. + // Some node implementations (e.g., org.exist.dom.memtree.NodeImpl) do not always have an associated document. + // In this case, the nodeIndex must get an extra 0 index to be valid for xdmDocument. + if (! (node instanceof Document)) { + index.add(0); + } + return index; } final List index = treeIndex(parent); - Node sibling = node.getPreviousSibling(); + Node sibling = previousSiblingNotAttribute(node); int position = 0; while (sibling != null) { position += 1; - sibling = sibling.getPreviousSibling(); + sibling = previousSiblingNotAttribute(sibling); } index.add(position); return index; } + /** + * A org.exist.dom.persistent.StoredNode returns attributes of an element as previous siblings of the element's children. + * This is not compatible with the way xdmNodeAtIndex works, so we need to compensate for this. + * @param node + * @return the previous sibling of `node` that is not an attribute. + */ + private static Node previousSiblingNotAttribute(Node node) { + Node sibling = node.getPreviousSibling(); + if (sibling instanceof Attr) { + return null; + } + return sibling; + } + static XdmNode xdmNodeAtIndex(final XdmNode xdmNode, final List index) { if (index.isEmpty()) { return xdmNode; diff --git a/exist-core/src/main/resources/org/exist/xquery/lib/ixml.ixml b/exist-core/src/main/resources/org/exist/xquery/lib/ixml.ixml new file mode 100644 index 00000000000..70fcb4f58a5 --- /dev/null +++ b/exist-core/src/main/resources/org/exist/xquery/lib/ixml.ixml @@ -0,0 +1,72 @@ +{version 2024-03-19} + ixml: s, (prolog, RS)?, rule++RS, s. + -s: (whitespace; comment)*. {Optional spacing} + -RS: (whitespace; comment)+. {Required spacing} + -whitespace: -[Zs]; + tab; + lf; + cr. + -tab: -#9. + -lf: -#a. + -cr: -#d. + comment: -'{', (cchar; comment)*, -'}'. + -cchar: ~['{}']. + prolog: version. + version: -'ixml', RS, -'version', RS, string, s, -'.'. + rule: naming, -['=:'], s, -alts, -'.'. + -naming: (mark, s)?, name, s, ('>', s, alias, s)?. + @name: namestart, namefollower*. + -namestart: ['_'; L]. +-namefollower: namestart; + ['-.·‿⁀'; Nd; Mn]. + @alias: name. + alts: alt++(-[';|'], s). + alt: term**(-',', s). + -term: factor; + option; + repeat0; + repeat1. + -factor: terminal; + nonterminal; + insertion; + -'(', s, alts, -')', s. + repeat0: factor, (-'*', s; -'**', s, sep). + repeat1: factor, (-'+', s; -'++', s, sep). + option: factor, -'?', s. + @mark: ['@^-']. + sep: factor. + nonterminal: naming. + -terminal: literal; + charset. + literal: quoted; + encoded. + -quoted: (tmark, s)?, string, s. + @tmark: ['^-']. + @string: -'"', dchar+, -'"'; + -'''', schar+, -''''. + dchar: ~['"'; Cc]; + '"', -'"'. {all characters except controls; quotes must be doubled} + schar: ~[''''; Cc]; + '''', -''''. {all characters except controls; quotes must be doubled} + -encoded: (tmark, s)?, -'#', hex, s. + @hex: ['0'-'9'; 'a'-'f'; 'A'-'F']+. + -charset: inclusion; + exclusion. + inclusion: (tmark, s)?, set. + exclusion: (tmark, s)?, -'~', s, set. + -set: -'[', s, (member, s)**(-[';|'], s), -']', s. + member: string; + -'#', hex; + range; + class. + -range: from, s, -'-', s, to. + @from: character. + @to: character. + -character: -'"', dchar, -'"'; + -'''', schar, -''''; + '#', hex. + -class: code. + @code: capital, letter?. + -capital: ['A'-'Z']. + -letter: ['A'-'Z'; 'a'-'z']. + insertion: -'+', s, (string; -'#', hex), s. \ No newline at end of file diff --git a/exist-docker/pom.xml b/exist-docker/pom.xml index 78ad8637666..9f76e18f92d 100644 --- a/exist-docker/pom.xml +++ b/exist-docker/pom.xml @@ -22,7 +22,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA --> - + 4.0.0 @@ -187,14 +189,18 @@ - - + + false - + META-INF/mailcap - + META-INF/mailcap.default diff --git a/extensions/modules/sql/src/main/java/org/exist/xquery/modules/sql/GetConnectionFunction.java b/extensions/modules/sql/src/main/java/org/exist/xquery/modules/sql/GetConnectionFunction.java index 5230ffa8179..01698e7bd8f 100644 --- a/extensions/modules/sql/src/main/java/org/exist/xquery/modules/sql/GetConnectionFunction.java +++ b/extensions/modules/sql/src/main/java/org/exist/xquery/modules/sql/GetConnectionFunction.java @@ -33,6 +33,7 @@ package org.exist.xquery.modules.sql; import com.zaxxer.hikari.HikariDataSource; +import com.zaxxer.hikari.HikariPoolMXBean; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -173,18 +174,27 @@ private Connection getConnectionFromPool(final Sequence[] args) throws XPathExce throw new XPathException(this, "There is no configured connection pool named: " + poolName); } + Connection connection = null; try { if (args.length == 3) { final String username = args[1].getStringValue(); final String password = args[2].getStringValue(); - return pool.getConnection(username, password); + connection = pool.getConnection(username, password); } else { - return pool.getConnection(); + connection = pool.getConnection(); } } catch (final SQLException sqle) { LOGGER.error("sql:get-connection-from-pool() Cannot retrieve connection from pool: " + poolName, sqle); throw new XPathException(this, "sql:get-connection-from-pool() Cannot retrieve connection from pool: " + poolName, sqle); } + + HikariPoolMXBean poolBean = pool.getHikariPoolMXBean(); + + if (poolBean.getThreadsAwaitingConnection() > 0) { + LOGGER.info("getConnectionFromPool("+poolName+"), "+poolBean.getActiveConnections()+" active, "+poolBean.getIdleConnections()+" available, "+poolBean.getThreadsAwaitingConnection()+" waiting, "+poolBean.getTotalConnections()+" total connections."); + } + + return connection; } private static FunctionSignature[] functionSignatures(final String name, final String description, final FunctionReturnSequenceType returnType, final FunctionParameterSequenceType[][] variableParamTypes) { diff --git a/extensions/modules/sql/src/main/java/org/exist/xquery/modules/sql/SQLModule.java b/extensions/modules/sql/src/main/java/org/exist/xquery/modules/sql/SQLModule.java index cbff3e400ff..f1af6a053de 100644 --- a/extensions/modules/sql/src/main/java/org/exist/xquery/modules/sql/SQLModule.java +++ b/extensions/modules/sql/src/main/java/org/exist/xquery/modules/sql/SQLModule.java @@ -37,25 +37,35 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.exist.dom.QName; import org.exist.xquery.*; +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; import java.sql.SQLException; - +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.exist.xquery.modules.ModuleUtils; import org.exist.xquery.modules.ModuleUtils.ContextMapEntryModifier; -import org.exist.xquery.value.FunctionParameterSequenceType; -import org.exist.xquery.value.FunctionReturnSequenceType; - import javax.annotation.Nullable; import static org.exist.xquery.FunctionDSL.functionDefs; @@ -86,6 +96,7 @@ public class SQLModule extends AbstractInternalModule { public final static String PREPARED_STATEMENTS_CONTEXTVAR = "_eXist_sql_prepared_statements"; private static final Map CONNECTION_POOLS = new ConcurrentHashMap<>(); + private static final Map NO_CONNECTION_POOLS = new ConcurrentHashMap<>(); private static final Pattern POOL_NAME_PATTERN = Pattern.compile("(pool\\.[0-9]+)\\.name"); public SQLModule(final Map> parameters) { @@ -105,7 +116,8 @@ public SQLModule(final Map> parameters) { final String poolId = poolNameMatcher.group(1); final String poolName = parameter.getValue().get(0).toString(); if (poolName != null && !poolName.isEmpty()) { - if (!CONNECTION_POOLS.containsKey(poolName)) { + boolean connectionFailure = NO_CONNECTION_POOLS.size() > 0; + if (!CONNECTION_POOLS.containsKey(poolName) && !connectionFailure) { final Properties poolProperties = new Properties();; poolProperties.setProperty("poolName", poolName); @@ -121,9 +133,18 @@ public SQLModule(final Map> parameters) { } } - final HikariConfig hikariConfig = new HikariConfig(poolProperties); - final HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig); - CONNECTION_POOLS.put(poolName, hikariDataSource); + try { + final HikariConfig hikariConfig = new HikariConfig(poolProperties); + final HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig); + CONNECTION_POOLS.put(poolName, hikariDataSource); + } catch (Exception ex) { + connectionFailure = true; + } + } + if (connectionFailure && !NO_CONNECTION_POOLS.containsKey(poolName)) { + LOG.warn("No database connection for "+poolName+" could be made. Also not for "+String.join(", ", NO_CONNECTION_POOLS.keySet())); + CONNECTION_POOLS.put(poolName, new NoConnectionHikariDataSource()); + NO_CONNECTION_POOLS.put(poolName, new Date()); } } } @@ -291,4 +312,185 @@ public void modifyEntry(final Entry entry) { } }); } + + class NoConnectionHikariDataSource extends HikariDataSource { + @Override + public Connection getConnection() throws SQLException + { + return new Connection() { + + @Override + public T unwrap(Class iface) throws SQLException { return null; } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { return false; } + + @Override + public Statement createStatement() throws SQLException { return null; } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { return null; } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { return null; } + + @Override + public String nativeSQL(String sql) throws SQLException { return null; } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException {} + + @Override + public boolean getAutoCommit() throws SQLException { return false; } + + @Override + public void commit() throws SQLException {} + + @Override + public void rollback() throws SQLException {} + + @Override + public void close() throws SQLException {} + + @Override + public boolean isClosed() throws SQLException { return false; } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { return null; } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException {} + + @Override + public boolean isReadOnly() throws SQLException { return false; } + + @Override + public void setCatalog(String catalog) throws SQLException {} + + @Override + public String getCatalog() throws SQLException { return null; } + + @Override + public void setTransactionIsolation(int level) throws SQLException {} + + @Override + public int getTransactionIsolation() throws SQLException { return 0;} + + @Override + public SQLWarning getWarnings() throws SQLException { return null; } + + @Override + public void clearWarnings() throws SQLException {} + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) + throws SQLException { return null; } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, + int resultSetConcurrency) throws SQLException { return null; } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, + int resultSetConcurrency) throws SQLException { return null; } + + @Override + public Map> getTypeMap() throws SQLException { return null; } + + @Override + public void setTypeMap(Map> map) throws SQLException {} + + @Override + public void setHoldability(int holdability) throws SQLException {} + + @Override + public int getHoldability() throws SQLException { return 0; } + + @Override + public Savepoint setSavepoint() throws SQLException { return null; } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { return null; } + + @Override + public void rollback(Savepoint savepoint) throws SQLException {} + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException {} + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, + int resultSetHoldability) throws SQLException { return null; } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, + int resultSetConcurrency, int resultSetHoldability) throws SQLException { return null; } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, + int resultSetConcurrency, int resultSetHoldability) throws SQLException { return null; } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) + throws SQLException { return null; } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) + throws SQLException { return null; } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) + throws SQLException { return null; } + + @Override + public Clob createClob() throws SQLException { return null; } + + @Override + public Blob createBlob() throws SQLException { return null; } + + @Override + public NClob createNClob() throws SQLException { return null; } + + @Override + public SQLXML createSQLXML() throws SQLException { return null; } + + @Override + public boolean isValid(int timeout) throws SQLException { return false; } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException {} + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException {} + + @Override + public String getClientInfo(String name) throws SQLException { return null; } + + @Override + public Properties getClientInfo() throws SQLException { return null; } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { return null; } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { return null; } + + @Override + public void setSchema(String schema) throws SQLException {} + + @Override + public String getSchema() throws SQLException { return null; } + + @Override + public void abort(Executor executor) throws SQLException {} + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) + throws SQLException {} + + @Override + public int getNetworkTimeout() throws SQLException { return 0; } + }; + } + } }