Skip to content

Commit 4ce606a

Browse files
committed
[bugfix] fn:root should not return the Document Node for a MemTree Node unless it was explicily created
Closes #1463
1 parent 37f2139 commit 4ce606a

File tree

4 files changed

+271
-13
lines changed

4 files changed

+271
-13
lines changed

exist-core/src/main/java/org/exist/dom/memtree/DocumentImpl.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,9 @@ protected void copyTo(NodeImpl node, final DocumentBuilderReceiver receiver, fin
10601060
nextNode = (NodeImpl) node.getFirstChild();
10611061
}
10621062
while(nextNode == null) {
1063-
copyEndNode(node, receiver);
1063+
if (node != null) {
1064+
copyEndNode(node, receiver);
1065+
}
10641066
if((top != null) && (top.nodeNumber == node.nodeNumber)) {
10651067
break;
10661068
}
@@ -1069,7 +1071,9 @@ protected void copyTo(NodeImpl node, final DocumentBuilderReceiver receiver, fin
10691071
if(nextNode == null) {
10701072
node = (NodeImpl) node.getParentNode();
10711073
if((node == null) || ((top != null) && (top.nodeNumber == node.nodeNumber))) {
1072-
copyEndNode(node, receiver);
1074+
if (node != null) {
1075+
copyEndNode(node, receiver);
1076+
}
10731077
break;
10741078
}
10751079
}
@@ -1308,7 +1312,9 @@ public void streamTo(final Serializer serializer, NodeImpl node, final Receiver
13081312
if(nextNode == null) {
13091313
node = (NodeImpl) node.getParentNode();
13101314
if((node == null) || ((top != null) && (top.nodeNumber == node.nodeNumber))) {
1311-
endNode(node, receiver);
1315+
if (node != null) {
1316+
endNode(node, receiver);
1317+
}
13121318
break;
13131319
}
13141320
}

exist-core/src/main/java/org/exist/dom/memtree/NodeImpl.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,13 +270,23 @@ public short getNodeType() {
270270
@Override
271271
public Node getParentNode() {
272272
int next = document.next[nodeNumber];
273-
while(next > nodeNumber) {
273+
while (next > nodeNumber) {
274274
next = document.next[next];
275275
}
276-
if(next < 0) {
276+
if (next < 0) {
277277
return null;
278278
}
279-
return document.getNode(next);
279+
final NodeImpl parent = document.getNode(next);
280+
if (parent.getNodeType() == DOCUMENT_NODE && !((DocumentImpl) parent).isExplicitlyCreated()) {
281+
/*
282+
All nodes in the MemTree will return an Owner document due to how the MemTree is implemented,
283+
however the explicitlyCreated flag tells us whether there "really" was a Document Node or not.
284+
See https://github.com/eXist-db/exist/issues/1463
285+
*/
286+
return null;
287+
} else {
288+
return parent;
289+
}
280290
}
281291

282292
public Node selectParentNode() {

exist-core/src/main/java/org/exist/xquery/functions/fn/FunRoot.java

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222
package org.exist.xquery.functions.fn;
2323

24+
import org.exist.dom.memtree.DocumentImpl;
2425
import org.exist.dom.persistent.ExtArrayNodeSet;
2526
import org.exist.dom.persistent.NodeProxy;
2627
import org.exist.dom.QName;
@@ -41,8 +42,11 @@
4142
import org.exist.xquery.value.SequenceType;
4243
import org.exist.xquery.value.Type;
4344
import org.exist.xquery.value.ValueSequence;
45+
import org.w3c.dom.Attr;
46+
import org.w3c.dom.Node;
4447

4548
/**
49+
* @author <a href="mailto:[email protected]">Adam Retter</a>
4650
* @author <a href="mailto:[email protected]">Wolfgang Meier</a>
4751
*/
4852
public class FunRoot extends Function {
@@ -125,13 +129,8 @@ public Sequence eval(Sequence contextSequence, final Item contextItem) throws XP
125129
final NodeProxy p = s.toNodeSet().get(0);
126130
result.add(new NodeProxy(p.getOwnerDocument()));
127131
} else {
128-
if (seq.hasOne() && item.getType() == Type.ATTRIBUTE) {
129-
result.add(item);
130-
} else if (item.getType() == Type.DOCUMENT) {
131-
result.add(item);
132-
} else {
133-
result.add(((NodeImpl) item).getOwnerDocument());
134-
}
132+
final NodeImpl ancestor = findAncestorOrSelf((NodeImpl) item);
133+
result.add(ancestor);
135134
}
136135
}
137136

@@ -141,4 +140,37 @@ public Sequence eval(Sequence contextSequence, final Item contextItem) throws XP
141140

142141
return result;
143142
}
143+
144+
private NodeImpl findAncestorOrSelf(final NodeImpl self) {
145+
if (self.getNodeType() == Node.DOCUMENT_NODE) {
146+
return self;
147+
148+
} else {
149+
final DocumentImpl ownerDocument = self.getOwnerDocument();
150+
if (ownerDocument != null && ownerDocument.isExplicitlyCreated()) {
151+
/*
152+
All nodes in the MemTree will return an Owner document due to how the MemTree is implemented,
153+
however the explicitlyCreated flag tells us whether there "really" was a Document Node or not.
154+
See https://github.com/eXist-db/exist/issues/1463
155+
*/
156+
return ownerDocument;
157+
}
158+
159+
NodeImpl ancestor = self;
160+
161+
for (NodeImpl current = ancestor; current != null; ) {
162+
if (current.getNodeType() == Node.ATTRIBUTE_NODE) {
163+
current = (NodeImpl) ((Attr) current).getOwnerElement();
164+
} else {
165+
current = (NodeImpl) current.getParentNode();
166+
}
167+
168+
if (current != null) {
169+
ancestor = current;
170+
}
171+
}
172+
173+
return ancestor;
174+
}
175+
}
144176
}

exist-core/src/test/xquery/root.xq

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
(:
2+
: eXist-db Open Source Native XML Database
3+
: Copyright (C) 2001 The eXist-db Authors
4+
:
5+
6+
: http://www.exist-db.org
7+
:
8+
: This library is free software; you can redistribute it and/or
9+
: modify it under the terms of the GNU Lesser General Public
10+
: License as published by the Free Software Foundation; either
11+
: version 2.1 of the License, or (at your option) any later version.
12+
:
13+
: This library is distributed in the hope that it will be useful,
14+
: but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
: Lesser General Public License for more details.
17+
:
18+
: You should have received a copy of the GNU Lesser General Public
19+
: License along with this library; if not, write to the Free Software
20+
: Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+
:)
22+
xquery version "3.1";
23+
24+
module namespace rt = "http://exist-db.org/xquery/test/fn-root";
25+
26+
declare namespace test = "http://exist-db.org/xquery/xqsuite";
27+
28+
declare
29+
%test:assertEquals("true", "false", "false")
30+
function rt:memtree-document() {
31+
let $x := document{()}
32+
return
33+
(
34+
$x/root() instance of document-node(),
35+
$x/ancestor::node() instance of document-node(),
36+
$x/parent::node() instance of document-node()
37+
)
38+
};
39+
40+
declare
41+
%test:assertEquals("false", "true", "false", "false", "false", "false")
42+
function rt:memtree-element() {
43+
let $x := element e1{}
44+
return
45+
(
46+
$x/root() instance of document-node(),
47+
$x/root() instance of element(),
48+
$x/ancestor::node() instance of document-node(),
49+
$x/ancestor::node() instance of element(),
50+
$x/parent::node() instance of document-node(),
51+
$x/parent::node() instance of element()
52+
)
53+
};
54+
55+
declare
56+
%test:assertEquals("false", "true", "false", "false", "false", "false")
57+
function rt:memtree-attribute() {
58+
let $x := attribute a1{ "a1" }
59+
return
60+
(
61+
$x/root() instance of document-node(),
62+
$x/root() instance of attribute(),
63+
$x/ancestor::node() instance of document-node(),
64+
$x/ancestor::node() instance of attribute(),
65+
$x/parent::node() instance of document-node(),
66+
$x/parent::node() instance of attribute()
67+
)
68+
};
69+
70+
declare
71+
%test:assertEquals("false", "true", "false", "false", "false", "false")
72+
function rt:memtree-comment() {
73+
let $x := comment { "c1" }
74+
return
75+
(
76+
$x/root() instance of document-node(),
77+
$x/root() instance of comment(),
78+
$x/ancestor::node() instance of document-node(),
79+
$x/ancestor::node() instance of comment(),
80+
$x/parent::node() instance of document-node(),
81+
$x/parent::node() instance of comment()
82+
)
83+
};
84+
85+
declare
86+
%test:assertEquals("false", "true", "false", "false", "false", "false")
87+
function rt:memtree-processing-instruction() {
88+
let $x := processing-instruction p1 { "p1" }
89+
return
90+
(
91+
$x/root() instance of document-node(),
92+
$x/root() instance of processing-instruction(),
93+
$x/ancestor::node() instance of document-node(),
94+
$x/ancestor::node() instance of processing-instruction(),
95+
$x/parent::node() instance of document-node(),
96+
$x/parent::node() instance of processing-instruction()
97+
)
98+
};
99+
100+
declare
101+
%test:assertEquals("false", "true", "false", "false", "false", "false")
102+
function rt:memtree-text() {
103+
let $x := text { "t1" }
104+
return
105+
(
106+
$x/root() instance of document-node(),
107+
$x/root() instance of text(),
108+
$x/ancestor::node() instance of document-node(),
109+
$x/ancestor::node() instance of text(),
110+
$x/parent::node() instance of document-node(),
111+
$x/parent::node() instance of text()
112+
)
113+
};
114+
115+
declare
116+
%test:assertEquals("false", "true", "false", "true", "false", "true")
117+
function rt:memtree-element-in-element() {
118+
let $x := element e1 {
119+
element e2{}
120+
}
121+
return
122+
(
123+
root($x/e2) instance of document-node(),
124+
root($x/e2) instance of element(),
125+
$x/e2/ancestor::node() instance of document-node(),
126+
$x/e2/ancestor::node() instance of element(),
127+
$x/e2/parent::node() instance of document-node(),
128+
$x/e2/parent::node() instance of element()
129+
)
130+
};
131+
132+
declare
133+
%test:assertEquals("false", "true", "false", "false", "true", "false", "false", "true", "false")
134+
function rt:memtree-attribute-in-element() {
135+
let $x := element e1 {
136+
attribute lang { "en" }
137+
}
138+
return
139+
(
140+
root($x/@lang) instance of document-node(),
141+
root($x/@lang) instance of element(),
142+
root($x/@lang) instance of attribute(),
143+
$x/@lang/ancestor::node() instance of document-node(),
144+
$x/@lang/ancestor::node() instance of element(),
145+
$x/@lang/ancestor::node() instance of attribute(),
146+
$x/@lang/parent::node() instance of document-node(),
147+
$x/@lang/parent::node() instance of element(),
148+
$x/@lang/parent::node() instance of attribute()
149+
)
150+
};
151+
152+
declare
153+
%test:assertEquals("false", "true", "false", "false", "true", "false", "false", "true", "false")
154+
function rt:memtree-comment-in-element() {
155+
let $x := element e1 {
156+
comment { "c1" }
157+
}
158+
return
159+
(
160+
root($x/comment()) instance of document-node(),
161+
root($x/comment()) instance of element(),
162+
root($x/comment()) instance of comment(),
163+
$x/comment()/ancestor::node() instance of document-node(),
164+
$x/comment()/ancestor::node() instance of element(),
165+
$x/comment()/ancestor::node() instance of comment(),
166+
$x/comment()/parent::node() instance of document-node(),
167+
$x/comment()/parent::node() instance of element(),
168+
$x/comment()/parent::node() instance of comment()
169+
)
170+
};
171+
172+
declare
173+
%test:assertEquals("false", "true", "false", "false", "true", "false", "false", "true", "false")
174+
function rt:memtree-processing-instruction-in-element() {
175+
let $x := element e1 {
176+
processing-instruction p1 { "p1" }
177+
}
178+
return
179+
(
180+
root($x/processing-instruction()) instance of document-node(),
181+
root($x/processing-instruction()) instance of element(),
182+
root($x/processing-instruction()) instance of processing-instruction(),
183+
$x/processing-instruction()/ancestor::node() instance of document-node(),
184+
$x/processing-instruction()/ancestor::node() instance of element(),
185+
$x/processing-instruction()/ancestor::node() instance of processing-instruction(),
186+
$x/processing-instruction()/parent::node() instance of document-node(),
187+
$x/processing-instruction()/parent::node() instance of element(),
188+
$x/processing-instruction()/parent::node() instance of processing-instruction()
189+
)
190+
};
191+
192+
declare
193+
%test:assertEquals("false", "true", "false", "false", "true", "false", "false", "true", "false")
194+
function rt:memtree-text-in-element() {
195+
let $x := element e1 {
196+
text { "t1" }
197+
}
198+
return
199+
(
200+
root($x/text()) instance of document-node(),
201+
root($x/text()) instance of element(),
202+
root($x/text()) instance of text(),
203+
$x/text()/ancestor::node() instance of document-node(),
204+
$x/text()/ancestor::node() instance of element(),
205+
$x/text()/ancestor::node() instance of text(),
206+
$x/text()/parent::node() instance of document-node(),
207+
$x/text()/parent::node() instance of element(),
208+
$x/text()/parent::node() instance of text()
209+
)
210+
};

0 commit comments

Comments
 (0)