From 3f4028a9f6839798ed9aff49a6ca80532872622b Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sat, 21 May 2022 14:02:28 +0200 Subject: [PATCH] [bugfix] Fix the Node Test for document-node(element(name)) Closes https://github.com/eXist-db/exist/issues/3463 --- .../org/exist/xquery/DynamicNameCheck.java | 73 +++++++------ .../main/java/org/exist/xquery/NameTest.java | 13 +++ exist-core/src/test/xquery/node-tests.xqm | 102 ++++++++++++++++++ 3 files changed, 151 insertions(+), 37 deletions(-) create mode 100644 exist-core/src/test/xquery/node-tests.xqm diff --git a/exist-core/src/main/java/org/exist/xquery/DynamicNameCheck.java b/exist-core/src/main/java/org/exist/xquery/DynamicNameCheck.java index b60a65ff469..ceb32d84450 100644 --- a/exist-core/src/main/java/org/exist/xquery/DynamicNameCheck.java +++ b/exist-core/src/main/java/org/exist/xquery/DynamicNameCheck.java @@ -21,11 +21,14 @@ */ package org.exist.xquery; +import org.exist.dom.INode; import org.exist.dom.persistent.DocumentSet; import org.exist.dom.persistent.NodeProxy; 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; import org.w3c.dom.Node; /** @@ -77,56 +80,52 @@ public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathExc } } if (!Type.subTypeOf(itemType, test.getType())) { - throw new XPathException(expression, "Type error in expression" + + throw new XPathException(expression, ErrorCodes.XPTY0004, "Type error in expression" + ": required type is " + Type.getTypeName(test.getType()) + "; got: " + Type.getTypeName(item.getType()) + ": " + item.getStringValue()); } final Node node = ((NodeValue) item).getNode(); - if (!test.matchesName(node)) - {throw new XPathException(expression, "Type error in expression: " + - "required node name is " + getPrefixedNodeName(test.getName()) + - "; got: " + getPrefixedNodeName(node));} + if (!test.matchesName(node)) { + throw new XPathException(expression, ErrorCodes.XPTY0004, "Type error in expression: " + + "required node name is " + getPrefixedNodeName(test) + + "; got: " + getPrefixedNodeName((INode) node)); } - if (context.getProfiler().isEnabled()) { - context.getProfiler().end(this, "", seq); - } - return seq; - } catch(final IllegalArgumentException iae) { - throw new XPathException(expression, iae); } + if (context.getProfiler().isEnabled()) { + context.getProfiler().end(this, "", seq); + } + return seq; + } catch(final IllegalArgumentException iae) { + throw new XPathException(expression, iae); + } } - private String getPrefixedNodeName(Node node) { - final String prefix = node.getPrefix(); - if (prefix == null) { - final String nameSpace = node.getNamespaceURI(); - if (nameSpace == null) { - return node.getNodeName(); - } else { - return "{'" + nameSpace + "'}:" + node.getNodeName(); + private String getPrefixedNodeName(final INode iNode) { + if (iNode instanceof Document) { + final Element documentElement = ((Document) iNode).getDocumentElement(); + if (documentElement != null) { + return getPrefixedNodeName(true, ((INode) documentElement).getQName()); } - } else if (prefix.isEmpty()) { - return "{''}:" + node.getNodeName(); - } else { - return prefix + ":" + node.getLocalName(); } + return getPrefixedNodeName(false, iNode.getQName()); } - // TODO should be moved to QName - private String getPrefixedNodeName(QName name) { - final String prefix = name.getPrefix(); - final String localName = name.getLocalPart(); - if (prefix == null) { - final String namespaceURI = name.getNamespaceURI(); - if (namespaceURI == null) { - return localName; - } else { - return "{'" + namespaceURI + "'}:" + localName; - } - } else if (prefix.isEmpty()) { - return "{''}:" + localName; + private String getPrefixedNodeName(final NameTest nameTest) { + return getPrefixedNodeName(nameTest.isOfType(Node.DOCUMENT_NODE), nameTest.getName()); + } + + private String getPrefixedNodeName(final boolean wasDocumentNodeWithNamedElementTest, final QName name) { + final String prefixedName; + if (name.getPrefix() == null && name.hasNamespace()) { + prefixedName = name.toURIQualifiedName(); + } else { + prefixedName = name.getStringValue(); + } + + if (wasDocumentNodeWithNamedElementTest) { + return "document-node(" + prefixedName + ")"; } else { - return prefix + ":" + localName; + return prefixedName; } } diff --git a/exist-core/src/main/java/org/exist/xquery/NameTest.java b/exist-core/src/main/java/org/exist/xquery/NameTest.java index 54c71eedb4f..3ad88f24dce 100644 --- a/exist-core/src/main/java/org/exist/xquery/NameTest.java +++ b/exist-core/src/main/java/org/exist/xquery/NameTest.java @@ -21,12 +21,15 @@ */ package org.exist.xquery; +import org.exist.dom.INode; import org.exist.dom.persistent.NodeProxy; import org.exist.dom.QName; import org.exist.dom.memtree.NodeImpl; import org.exist.dom.memtree.ReferenceNode; import org.exist.xquery.util.ExpressionDumper; import org.exist.xquery.value.Type; +import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.ProcessingInstruction; @@ -96,6 +99,16 @@ public boolean matchesName(final Node other) { return true; } + if (nodeType == Type.DOCUMENT && other.getNodeType() == Node.DOCUMENT_NODE) { + final Element otherElement = ((Document) other).getDocumentElement(); + if (otherElement != null) { + final QName otherQName = ((INode) otherElement).getQName(); + if (nodeName.equals(otherQName)) { + return true; + } + } + } + if (!(nodeName instanceof QName.WildcardNamespaceURIQName)) { String otherNs = other.getNamespaceURI(); if (otherNs == null) { diff --git a/exist-core/src/test/xquery/node-tests.xqm b/exist-core/src/test/xquery/node-tests.xqm new file mode 100644 index 00000000000..b36d0dd5d99 --- /dev/null +++ b/exist-core/src/test/xquery/node-tests.xqm @@ -0,0 +1,102 @@ +(: + : 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 + :) +xquery version "3.1"; + +module namespace nt = "http://exist-db.org/xquery/test/node-tests"; + +import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql"; + + +declare %private function nt:f-document-node($a as document-node()) { + $a//text() +}; + +declare %private function nt:f-document-node-with-document-element($a as document-node(element())) { + $a//text() +}; + +declare %private function nt:f-document-node-with-named-document-element($a as document-node(element(a))) { + $a//text() +}; + + +declare + %test:assertEmpty +function nt:test-document-node-valid-1() { + nt:f-document-node(document { () }) +}; + +declare + %test:assertEquals("is not b") +function nt:test-document-node-valid-2() { + nt:f-document-node(document {is not b}) +}; + +declare + %test:assertError("XPTY0004") +function nt:test-document-node-invalid() { + nt:f-document-node(is not b) +}; + +declare + %test:assertEquals("is not b") +function nt:test-document-node-with-document-element-valid() { + nt:f-document-node-with-document-element(document {is not b}) +}; + +declare + %test:pending("BaseX returns an empty-sequence, but Saxon raises the error XPTY0004... which should it be?") + %test:assertError("XPTY0004") +function nt:test-document-node-with-document-element-invalid-1() { + nt:f-document-node-with-document-element(document { () }) +}; + +declare + %test:pending("BaseX returns an empty-sequence, but Saxon raises the error XPTY0004... which should it be?") + %test:assertError("XPTY0004") +function nt:test-document-node-with-document-element-invalid-2() { + nt:f-document-node-with-document-element(document {}) +}; + +declare + %test:assertEquals("is not b") +function nt:test-document-node-with-named-document-element-valid() { + nt:f-document-node-with-named-document-element(document {is not b}) +}; + +declare + %test:assertError("XPTY0004") +function nt:test-document-node-with-named-document-element-invalid-1() { + nt:f-document-node-with-named-document-element(document { () }) +}; + +declare + %test:assertError("XPTY0004") +function nt:test-document-node-with-named-document-element-invalid-2() { + nt:f-document-node-with-named-document-element(document {}) +}; + +declare + %test:assertError("XPTY0004") +function nt:test-document-node-with-named-document-element-invalid-3() { + nt:f-document-node-with-named-document-element(document {is b}) +};