• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

evolvedbinary / elemental / 982

29 Apr 2025 08:34PM UTC coverage: 56.409% (+0.007%) from 56.402%
982

push

circleci

adamretter
[feature] Improve README.md badges

28451 of 55847 branches covered (50.94%)

Branch coverage included in aggregate %.

77468 of 131924 relevant lines covered (58.72%)

0.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.49
/exist-core/src/main/java/org/exist/dom/persistent/VirtualNodeSet.java
1
/*
2
 * Elemental
3
 * Copyright (C) 2024, Evolved Binary Ltd
4
 *
5
 * admin@evolvedbinary.com
6
 * https://www.evolvedbinary.com | https://www.elemental.xyz
7
 *
8
 * Use of this software is governed by the Business Source License 1.1
9
 * included in the LICENSE file and at www.mariadb.com/bsl11.
10
 *
11
 * Change Date: 2028-04-27
12
 *
13
 * On the date above, in accordance with the Business Source License, use
14
 * of this software will be governed by the Apache License, Version 2.0.
15
 *
16
 * Additional Use Grant: Production use of the Licensed Work for a permitted
17
 * purpose. A Permitted Purpose is any purpose other than a Competing Use.
18
 * A Competing Use means making the Software available to others in a commercial
19
 * product or service that: substitutes for the Software; substitutes for any
20
 * other product or service we offer using the Software that exists as of the
21
 * date we make the Software available; or offers the same or substantially
22
 * similar functionality as the Software.
23
 *
24
 * NOTE: Parts of this file contain code from 'The eXist-db Authors'.
25
 *       The original license header is included below.
26
 *
27
 * =====================================================================
28
 *
29
 * eXist-db Open Source Native XML Database
30
 * Copyright (C) 2001 The eXist-db Authors
31
 *
32
 * info@exist-db.org
33
 * http://www.exist-db.org
34
 *
35
 * This library is free software; you can redistribute it and/or
36
 * modify it under the terms of the GNU Lesser General Public
37
 * License as published by the Free Software Foundation; either
38
 * version 2.1 of the License, or (at your option) any later version.
39
 *
40
 * This library is distributed in the hope that it will be useful,
41
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
42
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
43
 * Lesser General Public License for more details.
44
 *
45
 * You should have received a copy of the GNU Lesser General Public
46
 * License along with this library; if not, write to the Free Software
47
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
48
 */
49
package org.exist.dom.persistent;
50

51
import org.exist.collections.Collection;
52
import org.exist.indexing.StructuralIndex;
53
import org.exist.numbering.NodeId;
54
import org.exist.stax.EmbeddedXMLStreamReader;
55
import org.exist.stax.ExtendedXMLStreamReader;
56
import org.exist.storage.DBBroker;
57
import org.exist.storage.ElementValue;
58
import org.exist.storage.dom.INodeIterator;
59
import org.exist.xquery.Constants;
60
import org.exist.xquery.Expression;
61
import org.exist.xquery.NodeTest;
62
import org.exist.xquery.XPathException;
63
import org.exist.xquery.value.Item;
64
import org.exist.xquery.value.SequenceIterator;
65
import org.exist.xquery.value.Type;
66
import org.w3c.dom.Node;
67
import org.w3c.dom.NodeList;
68

69
import javax.xml.stream.XMLStreamConstants;
70
import javax.xml.stream.XMLStreamException;
71
import java.io.IOException;
72
import java.util.Iterator;
73

74
/**
75
 * This node set is called virtual because it is just a placeholder for
76
 * the set of relevant nodes. For XPath expressions like //* or //node(),
77
 * it would be totally inefficient to actually retrieve all descendant nodes.
78
 * In many cases, the expression can be resolved at a later point in time
79
 * without retrieving the whole node set.
80
 *
81
 * VirtualNodeSet basically provides method getFirstParent to retrieve the first
82
 * matching descendant of its context according to the primary type axis.
83
 *
84
 * Class LocationStep will always return an instance of VirtualNodeSet
85
 * if it finds something like descendant::* etc..
86
 *
87
 * @author Wolfgang Meier
88
 * @author Timo Boehme
89
 */
90
public class VirtualNodeSet extends AbstractNodeSet {
91

92
    private static final int MAX_CHILD_COUNT_FOR_OPTIMIZE = 5;
93

94
    protected int axis = Constants.UNKNOWN_AXIS;
1✔
95
    protected NodeTest test;
96
    protected NodeSet context;
97
    protected NodeSet realSet = null;
1✔
98
    protected boolean realSetIsComplete = false;
1✔
99
    protected boolean inPredicate = false;
1✔
100
    protected boolean useSelfAsContext = false;
1✔
101
    protected int contextId = Expression.NO_CONTEXT_ID;
1✔
102

103
    private DocumentSet realDocumentSet = null;
1✔
104

105
    private boolean knownIsEmptyCardinality = false;
1✔
106
    private boolean knownHasOneCardinality = false;
1✔
107
    private boolean knownHasManyCardinality = false;
1✔
108

109
    protected boolean hasMany = false;
1✔
110

111
    private DBBroker broker;
112

113
    /**
114
     * Creates a new <code>VirtualNodeSet</code> instance.
115
     *
116
     * @param broker the DBBroker
117
     * @param axis      an <code>int</code> value
118
     * @param test      a <code>NodeTest</code> value
119
     * @param contextId an <code>int</code> value
120
     * @param context   a <code>NodeSet</code> value
121
     */
122
    public VirtualNodeSet(final DBBroker broker, final int axis, final NodeTest test, final int contextId, final NodeSet context) {
1✔
123
        this.isEmpty = true;
1✔
124
        this.hasOne = false;
1✔
125
        this.axis = axis;
1✔
126
        this.test = test;
1✔
127
        this.context = context;
1✔
128
        this.contextId = contextId;
1✔
129
        this.broker = broker;
1✔
130
    }
1✔
131

132
    @Override
133
    public boolean contains(final NodeProxy p) {
134
        final NodeProxy firstParent = getFirstParent(p, null, axis == Constants.SELF_AXIS, 0);
×
135
        // Timo Boehme: getFirstParent returns now only real parents
136
        // therefore test if node is child of context
137
        if(firstParent != null) {
×
138
            return true;
×
139
        }
140
        return false;
×
141
    }
142

143
    @Override
144
    public boolean containsReference(final Item item) {
145
        if (!(item instanceof NodeProxy)) {
×
146
            return false;
×
147
        }
148

149
        final NodeProxy firstParent = getFirstParent((NodeProxy) item, null, axis == Constants.SELF_AXIS, 0);
×
150
        return firstParent == item;
×
151
    }
152

153
    @Override
154
    public boolean contains(final Item item) {
155
        if (!(item instanceof NodeProxy)) {
×
156
            return false;
×
157
        }
158

159
        final NodeProxy firstParent = getFirstParent((NodeProxy) item, null, axis == Constants.SELF_AXIS, 0);
×
160
        return firstParent.equals(item);
×
161
    }
162

163
    public void setInPredicate(final boolean predicate) {
164
        inPredicate = predicate;
1✔
165
    }
1✔
166

167
    @Override
168
    public DocumentSet getDocumentSet() {
169
        //If we know what are our documents, return them...
170
        if(realDocumentSet != null) {
1✔
171
            return realDocumentSet;
1✔
172
        }
173
        //... otherwise, we default to every *potentially* concerned document
174
        return context.getDocumentSet();
1✔
175
    }
176

177

178
    @Override
179
    public Iterator<Collection> getCollectionIterator() {
180
        return context.getCollectionIterator();
1✔
181
    }
182

183
    private NodeProxy getFirstParent(final NodeProxy self, final NodeProxy firstParent,
184
            final boolean includeSelf, final int recursions) {
185
        return getFirstParent(self, firstParent, includeSelf, true, recursions);
×
186
    }
187

188
    private NodeProxy getFirstParent(NodeProxy self, NodeProxy candidateFirstParent,
189
            final boolean includeSelf, final boolean restrictToDirectParent, final int recursions) {
190
        /* if the node is a document node we still need to
191
         * complete this method to check if we have found a potential parent
192
         * in one of the iterations before.
193
         */
194
        final NodeId parentOfSelfId = self.getNodeId().getParentId();
1✔
195
        // check if the start-node should be included, e.g. to process an
196
        // expression like *[. = 'xxx']
197
        //TODO : investigate on expression like *[.//* = 'xxx']
198
        if(recursions == 0 && includeSelf && test.matches(self)) {
1!
199
            // if we're on the child axis, test if
200
            // the node is a direct child of the context node
201
            if(axis == Constants.CHILD_AXIS) {
×
202
                //WARNING : get() realizes virtual node sets
203
                //TODO : investigate more efficient solutions
204
                final NodeProxy parent = context.get(self.getOwnerDocument(), parentOfSelfId);
×
205
                if(parent != null) {
×
206
                    self.copyContext(parent);
×
207
                    if(useSelfAsContext && inPredicate) {
×
208
                        self.addContextNode(contextId, self);
×
209
                    } else if(inPredicate) {
×
210
                        self.addContextNode(contextId, parent);
×
211
                    }
212
                    return self;
×
213
                }
214
            } else {
215
                // descendant axis: remember the node and continue 
216
                candidateFirstParent = self;
×
217
            }
218
        }
219
        // if this is the first call to this method, remember the first 
220
        // parent node and continue to evaluate the method. We can't just return 
221
        // the first parent as we need a parent that is *actually* contained 
222
        // in the context set. We will thus call the method again to complete.
223
        if(candidateFirstParent == null) {
1✔
224
            //given node was already document element -> no parent
225
            if(parentOfSelfId == NodeId.DOCUMENT_NODE) {
1!
226
                return null;
×
227
            }
228
            candidateFirstParent = new NodeProxy(null, self.getOwnerDocument(), parentOfSelfId, Node.ELEMENT_NODE);
1✔
229
            // if we are on the self axis, check if the first parent can be selected
230
            if(axis == Constants.DESCENDANT_SELF_AXIS) {
1!
231
                //WARNING : get() realizes virtual node sets
232
                //TODO : investigate more efficient solutions
233
                final NodeProxy parent = context.get(candidateFirstParent.getOwnerDocument(), parentOfSelfId);
×
234
                if(parent != null && test.matches(parent)) {
×
235
                    candidateFirstParent.copyContext(parent);
×
236
                    if(useSelfAsContext && inPredicate) {
×
237
                        candidateFirstParent.addContextNode(contextId, candidateFirstParent);
×
238
                    } else if(inPredicate) {
×
239
                        candidateFirstParent.addContextNode(contextId, parent);
×
240
                    }
241
                    return candidateFirstParent;
×
242
                }
243
            }
244
            // We need a real parent : keep the candidate and continue to iterate from this one
245
            return getFirstParent(candidateFirstParent, candidateFirstParent, false,
1✔
246
                restrictToDirectParent, recursions + 1);
1✔
247
        }
248
        // is the node's parent in the context set?
249
        //WARNING : get() realizes virtual node sets
250
        //TODO : investigate more efficient solutions
251
        NodeProxy parentOfSelf = context.get(self.getOwnerDocument(), parentOfSelfId);
1✔
252
        if(parentOfSelf != null && test.matches(self)) {
1!
253
            if(axis != Constants.CHILD_AXIS) {
1✔
254
                // if we are on the descendant axis, we return the first node 
255
                // we found while walking bottom-up.
256
                // Otherwise, we return the last one (which is the node itself)
257
                self = candidateFirstParent;
1✔
258
            }
259
            self.copyContext(parentOfSelf);
1✔
260
            if(useSelfAsContext && inPredicate) {
1!
261
                self.addContextNode(contextId, self);
×
262
            } else if(inPredicate) {
1!
263
                self.addContextNode(contextId, parentOfSelf);
×
264
            }
265
            // Timo Boehme: we return the ancestor which is child of context
266
            return self;
1✔
267
        } else if(parentOfSelfId == NodeId.DOCUMENT_NODE) {
1!
268
            // no matching node has been found in the context
269
            return null;
×
270
        } else if(restrictToDirectParent && axis == Constants.CHILD_AXIS && recursions == 1) {
1!
271
            // break here if the expression is like /*/n
272
            return null;
1✔
273
        } else {
274
            // continue for expressions like //*/n or /*//n
275
            parentOfSelf = new NodeProxy(parentOfSelf != null ? parentOfSelf.getExpression() : null, self.getOwnerDocument(), parentOfSelfId, Node.ELEMENT_NODE);
×
276
            return getFirstParent(parentOfSelf, candidateFirstParent, false, false, recursions + 1);
×
277
        }
278
    }
279

280
    private void addInternal(final NodeProxy p) {
281
        if(realSet == null) {
1✔
282
            realSet = new NewArrayNodeSet();
1✔
283
        }
284
        realSet.add(p);
1✔
285
        knownIsEmptyCardinality = true;
1✔
286
        knownHasOneCardinality = true;
1✔
287
        knownHasManyCardinality = true;
1✔
288
        isEmpty = realSet.isEmpty();
1✔
289
        hasOne = realSet.hasOne();
1✔
290
        hasMany = !(isEmpty || hasOne);
1!
291
        //Reset the real document set
292
        //TODO : use realDocumentSet.add(p.getOwnerDocument()) ?
293
        realDocumentSet = null;
1✔
294
        realSetIsComplete = false;
1✔
295
    }
1✔
296

297
    @Override
298
    public NodeProxy parentWithChild(final NodeProxy proxy, final boolean restrictToDirectParent,
299
            final boolean includeSelf, final int level) {
300
        if(realSet != null && realSetIsComplete) {
×
301
            return realSet.parentWithChild(proxy, restrictToDirectParent, includeSelf, level);
×
302
        } else {
303
            final NodeProxy first = getFirstParent(proxy, null, includeSelf, restrictToDirectParent, 0);
×
304
            if(first != null) {
×
305
                //TODO : should we set an empty cardinality here ?
306
                addInternal(first);
×
307
            }
308
            return first;
×
309
        }
310
    }
311

312
    @Override
313
    public NodeProxy parentWithChild(final DocumentImpl doc, final NodeId nodeId,
314
            final boolean restrictToDirectParent, final boolean includeSelf) {
315
        if(realSet != null && realSetIsComplete) {
1✔
316
            return realSet.parentWithChild(doc, nodeId, restrictToDirectParent, includeSelf);
1✔
317
        } else {
318
            final NodeProxy first = getFirstParent(new NodeProxy(null, doc, nodeId), null,
1✔
319
                includeSelf, restrictToDirectParent, 0);
1✔
320
            if(first != null) {
1✔
321
                //TODO : should we set an empty cardinality here ?
322
                addInternal(first);
1✔
323
            }
324
            return first;
1✔
325
        }
326
    }
327

328
    /**
329
     * Realize the node set by recursively scanning the
330
     * DOM.
331
     */
332
    private NodeSet getNodes() {
333
        final NewArrayNodeSet result = new NewArrayNodeSet();
1✔
334
        for (final NodeProxy proxy : context) {
1✔
335
            if (proxy.getNodeId() == NodeId.DOCUMENT_NODE) {
1✔
336
                if (proxy.getOwnerDocument().getResourceType() == DocumentImpl.BINARY_FILE) {
1!
337
                    // skip binary resources
338
                    continue;
×
339
                }
340
                // Add root node if axis is either self, ancestor-self or descendant-self /ljo
341
                if ((axis == Constants.SELF_AXIS ||
1!
342
                        axis == Constants.ANCESTOR_SELF_AXIS ||
1!
343
                        axis == Constants.DESCENDANT_SELF_AXIS) &&
1!
344
                        test.matches(proxy)) {
×
345
                    result.add(proxy);
×
346
                }
347
                if ((axis == Constants.CHILD_AXIS || axis == Constants.ATTRIBUTE_AXIS) &&
1!
348
                        proxy.getOwnerDocument().getChildCount() == 1) {
1✔
349
                    // Optimization: if the document has just 1 child node, we know that
350
                    // it has to be an element. Instead of calling Document.getChildNodes(),
351
                    // we just create a NodeProxy for the first child and return it if the
352
                    // test matches
353
                    final NodeProxy p = proxy.getOwnerDocument().getFirstChildProxy();
1✔
354
                    if (test.matches(p)) {
1✔
355
                        if (useSelfAsContext && inPredicate) {
1!
356
                            p.addContextNode(contextId, p);
1✔
357
                        }
358
                        p.addMatches(proxy);
1✔
359
                        result.add(p);
1✔
360
                    }
361
                } else {
1✔
362
                    final NodeList cl = proxy.getOwnerDocument().getChildNodes();
1✔
363
                    for (int j = 0; j < cl.getLength(); j++) {
1✔
364
                        final IStoredNode<?> node = (IStoredNode<?>) cl.item(j);
1✔
365
                        final NodeProxy p = new NodeProxy(null, node);
1✔
366
                        if (test.matches(p)) {
1✔
367
                            // fixme! check for unwanted
368
                            // side effects. /ljo
369
                            //p.deepCopyContext(proxy);
370
                            if (useSelfAsContext && inPredicate) {
1!
371
                                p.addContextNode(contextId, p);
1✔
372
                            }
373
                            result.add(p);
1✔
374
                        }
375
                        if (node.getNodeType() == Node.ELEMENT_NODE &&
1✔
376
                                (axis == Constants.DESCENDANT_AXIS ||
1✔
377
                                        axis == Constants.DESCENDANT_SELF_AXIS ||
1!
378
                                        axis == Constants.DESCENDANT_ATTRIBUTE_AXIS)) {
1✔
379
                            // note: we create a copy of the docElemProxy here to
380
                            // be used as context when traversing the tree.
381
                            final NodeProxy contextNode = new NodeProxy(null, p);
1✔
382
                            contextNode.deepCopyContext(proxy);
1✔
383
                            //TODO : is this StoredNode construction necessary ?
384
                            try (final INodeIterator domIter = broker.getNodeIterator(contextNode.asStoredNode())) {
1✔
385
                                domIter.next();
1✔
386
                                contextNode.setMatches(proxy.getMatches());
1✔
387
                                addChildren(contextNode, result, node, domIter, 0);
1✔
388
                            } catch (final IOException ioe) {
×
389
                                LOG.warn("Unable to close iterator", ioe);
×
390
                            }
391
                        }
392
                        if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE
1✔
393
                                && (axis == Constants.CHILD_AXIS ||
1✔
394
                                axis == Constants.DESCENDANT_AXIS ||
1!
395
                                axis == Constants.DESCENDANT_SELF_AXIS ||
×
396
                                // fixme! self axis probably not needed /ljo
397
                                axis == Constants.SELF_AXIS ||
×
398
                                axis == Constants.PRECEDING_AXIS ||
×
399
                                axis == Constants.FOLLOWING_AXIS)
×
400
                                && test.matches(node)) {
1✔
401

402
                            result.add(p);
1✔
403
                        }
404
                    }
405
                }
406
                continue;
1✔
407
            }
408
            if ((axis == Constants.SELF_AXIS ||
1!
409
                    axis == Constants.ANCESTOR_SELF_AXIS ||
1!
410
                    axis == Constants.DESCENDANT_SELF_AXIS) &&
1✔
411
                    test.matches(proxy)) {
1!
412
                if (useSelfAsContext && inPredicate) {
1!
413
                    proxy.addContextNode(contextId, proxy);
×
414
                }
415
                result.add(proxy);
1✔
416
            }
417
            if (test.getType() == Type.PROCESSING_INSTRUCTION ||
1!
418
                    test.getType() == Type.COMMENT ||
1✔
419
                    test.getType() == Type.CDATA_SECTION) {
1!
420
                final DocumentImpl doc = proxy.getOwnerDocument();
1✔
421
                if (axis == Constants.PRECEDING_AXIS) {
1!
422
                    IStoredNode<?> ps = (IStoredNode<?>) doc.getFirstChild();
×
423
                    final IStoredNode<?> pe = (IStoredNode<?>) doc.getDocumentElement();
×
424
                    while (ps != null && !ps.equals(pe)) {
×
425
                        if (test.matches(ps)) {
×
426
                            result.add(new NodeProxy(null, ps));
×
427
                        }
428
                        ps = (IStoredNode<?>) doc.getFollowingSibling(ps);
×
429
                    }
430
                }
431
                if (axis == Constants.FOLLOWING_AXIS) {
1!
432
                    final IStoredNode<?> pe = (IStoredNode<?>) doc.getDocumentElement();
×
433
                    IStoredNode<?> pf = (IStoredNode<?>) doc.getFollowingSibling(pe);
×
434
                    while (pf != null) {
×
435
                        if (test.matches(pf)) {
×
436
                            result.add(new NodeProxy(null, pf));
×
437
                        }
438
                        pf = (IStoredNode<?>) doc.getFollowingSibling(pf);
×
439
                    }
440
                }
441
                if (axis == Constants.SELF_AXIS ||
1!
442
                        axis == Constants.ANCESTOR_SELF_AXIS ||
1!
443
                        axis == Constants.DESCENDANT_SELF_AXIS) {
1!
444
                    result.add(proxy);
×
445
                }
446
            }
447
            if (axis != Constants.SELF_AXIS) {
1!
448
                addChildren(proxy, result);
1✔
449
            }
450
        }
451
        realDocumentSet = result.getDocumentSet();
1✔
452
        return result;
1✔
453
    }
454

455
    /**
456
     * Realize the node set by scanning the structural index.
457
     * This is usually cheaper than calling {@link #getNodes()}.
458
     *
459
     * Not used right now because the method seems to dramatically slow down
460
     * some expressions instead of improving performance. To be checked.
461
     */
462
    private NodeSet getNodesFromIndex() {
463
        final StructuralIndex index = broker.getStructuralIndex();
×
464
        final byte type = test.getType() == Type.ELEMENT ? ElementValue.ELEMENT : ElementValue.ATTRIBUTE;
×
465
        final NodeSet result = index.scanByType(type, axis, test, useSelfAsContext && inPredicate,
×
466
            context.getDocumentSet(), context, contextId);
×
467
        realDocumentSet = result.getDocumentSet();
×
468
        return result;
×
469
    }
470

471
    /**
472
     * recursively adds child nodes
473
     *
474
     * @param contextNode a <code>NodeProxy</code> value
475
     * @param result      a <code>NodeSet</code> value
476
     * @param node        a <code>IStoredNode</code> value
477
     * @param iter        an <code>Iterator</code> value
478
     * @param recursions  an <code>int</code> value
479
     */
480
    private void addChildren(final NodeProxy contextNode, final NodeSet result,
481
            final IStoredNode node, final INodeIterator iter, final int recursions) {
482
        if(node.hasChildNodes() || node.hasAttributes()) {
1✔
483
            for(int i = 0; i < node.getChildCount(); i++) {
1✔
484
                final IStoredNode child = iter.next();
1✔
485
                if(child == null) {
1!
486
                    LOG.debug("CHILD == NULL; doc = {}", ((DocumentImpl) node.getOwnerDocument()).getURI());
×
487
                    //TODO : throw exception ? -pb
488
                    return;
×
489
                }
490
                if(node.getOwnerDocument() == null) {
1!
491
                    LOG.debug("DOC == NULL");
×
492
                    //TODO : throw exception ? -pb
493
                    return;
×
494
                }
495
                child.setOwnerDocument((DocumentImpl) node.getOwnerDocument());
1✔
496
                final NodeProxy p = new NodeProxy(null, child);
1✔
497
                p.setMatches(contextNode.getMatches());
1✔
498
                if(test.matches(child) && (
1✔
499
                    ((axis == Constants.CHILD_AXIS || axis == Constants.ATTRIBUTE_AXIS) && recursions == 0)
1!
500
                    || (axis == Constants.DESCENDANT_AXIS || axis == Constants.DESCENDANT_SELF_AXIS || axis == Constants.DESCENDANT_ATTRIBUTE_AXIS))
1!
501
                ) {
502

503
                    p.deepCopyContext(contextNode);
1✔
504

505
                    if(useSelfAsContext && inPredicate) {
1!
506
                        p.addContextNode(contextId, p);
1✔
507
                    } else if(inPredicate) {
1!
508
                        p.addContextNode(contextId, contextNode);
×
509
                    }
510
                    result.add(p);
1✔
511
                }
512
                addChildren(contextNode, result, child, iter, recursions + 1);
1✔
513
            }
514
        }
515
    }
1✔
516

517
    private void addChildren(final NodeProxy contextNode, final NodeSet result) {
518
        try {
519
            final EmbeddedXMLStreamReader reader = (EmbeddedXMLStreamReader)broker.getXMLStreamReader(contextNode, true);
1✔
520
            int status = reader.next();
1✔
521
            if (status != XMLStreamConstants.START_ELEMENT) {
1!
522
                return;
×
523
            }
524

525
            int level = 0;
1✔
526
            while(reader.hasNext()) {
1!
527
                status = reader.next();
1✔
528
                if(axis == Constants.ATTRIBUTE_AXIS && status != XMLStreamConstants.ATTRIBUTE) {
1✔
529
                    break;
1✔
530
                }
531
                switch(status) {
1✔
532
                    case XMLStreamConstants.END_ELEMENT:
533
                        if(--level < 0) {
1✔
534
                            return;
1✔
535
                        }
536
                        break;
537
                    case XMLStreamConstants.ATTRIBUTE:
538
                        if((axis == Constants.ATTRIBUTE_AXIS && level == 0) ||
1!
539
                            axis == Constants.DESCENDANT_ATTRIBUTE_AXIS) {
1!
540
                            final AttrImpl attr = (AttrImpl) reader.getNode();
1✔
541
                            if(test.matches(attr)) {
1!
542
                                final NodeProxy p = new NodeProxy(null, attr);
1✔
543
                                p.deepCopyContext(contextNode);
1✔
544
                                if(useSelfAsContext && inPredicate) {
1!
545
                                    p.addContextNode(contextId, p);
×
546
                                } else if(inPredicate) {
1!
547
                                    p.addContextNode(contextId, contextNode);
×
548
                                }
549
                                result.add(p);
1✔
550
                            }
551
                        }
552
                        break;
1✔
553
                    default:
554
                        if(((axis == Constants.CHILD_AXIS && level == 0) ||
1✔
555
                            axis == Constants.DESCENDANT_AXIS ||
1✔
556
                            axis == Constants.DESCENDANT_SELF_AXIS) &&
1✔
557
                            test.matches(reader)) {
1✔
558
                            final NodeId nodeId = (NodeId) reader.getProperty(ExtendedXMLStreamReader.PROPERTY_NODE_ID);
1✔
559
                            final NodeProxy p = new NodeProxy(null, contextNode.getOwnerDocument(), nodeId,
1✔
560
                                reader.getNodeType(), reader.getCurrentPosition());
1✔
561
                            p.deepCopyContext(contextNode);
1✔
562
                            if(useSelfAsContext && inPredicate) {
1!
563
                                p.addContextNode(contextId, p);
1✔
564
                            } else if(inPredicate) {
1✔
565
                                p.addContextNode(contextId, contextNode);
1✔
566
                            }
567
                            result.add(p);
1✔
568
                        }
569
                        break;
570
                }
571
                if(status == XMLStreamConstants.START_ELEMENT) {
1✔
572
                    ++level;
1✔
573
                }
574
            }
575
        } catch(final IOException | XMLStreamException e) {
×
576
            LOG.error(e);
×
577
            //TODO : throw exception ,
578
        } //TODO : throw exception ? -pb
579

580
    }
1✔
581

582
    /**
583
     * Realize the node set. This should only be done if the
584
     * wildcard step is the last step in a path expression.
585
     */
586
    private final void realize() {
587
        if(realSet != null && realSetIsComplete) {
1!
588
            return;
1✔
589
        }
590
        realSet = getNodes();
1✔
591
        knownIsEmptyCardinality = true;
1✔
592
        knownHasOneCardinality = true;
1✔
593
        knownHasManyCardinality = true;
1✔
594
        isEmpty = realSet.isEmpty();
1✔
595
        hasOne = realSet.hasOne();
1✔
596
        hasMany = realSet.hasMany();
1✔
597
        realSetIsComplete = true;
1✔
598
    }
1✔
599

600
    public void setSelfIsContext() {
601
        useSelfAsContext = true;
1✔
602
        if(realSet != null && realSetIsComplete) {
1!
603
            for (final NodeProxy p : realSet) {
×
604
                p.addContextNode(contextId, p);
×
605
            }
606
        }
607
    }
1✔
608

609
    public void setContextId(final int contextId) {
610
        this.contextId = contextId;
1✔
611
    }
1✔
612

613
    /* the following methods are normally never called in this context,
614
     * we just provide them because they are declared abstract
615
     * in the super class
616
     */
617

618
    @Override
619
    public boolean isEmpty() {
620
        if(knownIsEmptyCardinality) {
1✔
621
            return isEmpty;
1✔
622
        }
623
        return getLength() == 0;
1✔
624
    }
625

626
    @Override
627
    public boolean hasOne() {
628
        if(knownHasOneCardinality) {
1!
629
            return hasOne;
1✔
630
        }
631
        return getLength() == 1;
×
632
    }
633

634
    @Override
635
    public boolean hasMany() {
636
        if(knownHasManyCardinality) {
1!
637
            return hasMany;
1✔
638
        }
639
        return getLength() > 1;
×
640
    }
641

642
    @Override
643
    public void add(final NodeProxy proxy) {
644
        //Nothing to do
645
    }
×
646

647
    @Override
648
    public void addAll(final NodeSet other) {
649
        //Nothing to do
650
    }
×
651

652
    @Override
653
    public int getLength() {
654
        realize();
1✔
655
        return realSet.getLength();
1✔
656
    }
657

658
    @Override
659
    public int getItemType() {
660
        if(realSet != null && realSetIsComplete) {
1!
661
            return realSet.getItemType();
1✔
662
        }
663
        return Type.NODE;
1✔
664
    }
665

666
    @Override
667
    public long getItemCountLong() {
668
        //TODO : evaluate both semantics
669
        realize();
1✔
670
        return realSet.getItemCountLong();
1✔
671
    }
672

673
    @Override
674
    public Node item(final int pos) {
675
        realize();
1✔
676
        return realSet.item(pos);
1✔
677
    }
678

679
    @Override
680
    public NodeProxy get(final int pos) {
681
        realize();
×
682
        return realSet.get(pos);
×
683
    }
684

685
    @Override
686
    public Item itemAt(final int pos) {
687
        realize();
1✔
688
        return realSet.itemAt(pos);
1✔
689
    }
690

691
    @Override
692
    public NodeProxy get(final DocumentImpl doc, final NodeId nodeId) {
693
        realize();
1✔
694
        return realSet.get(doc, nodeId);
1✔
695
    }
696

697
    @Override
698
    public NodeProxy get(final NodeProxy proxy) {
699
        realize();
×
700
        return realSet.get(proxy);
×
701
    }
702

703
    @Override
704
    public NodeSetIterator iterator() {
705
        realize();
1✔
706
        return realSet.iterator();
1✔
707
    }
708

709
    @Override
710
    public SequenceIterator iterate() throws XPathException {
711
        realize();
1✔
712
        return realSet.iterate();
1✔
713
    }
714

715
    @Override
716
    public SequenceIterator unorderedIterator() throws XPathException {
717
        realize();
×
718
        return realSet.unorderedIterator();
×
719
    }
720

721
    @Override
722
    public NodeSet intersection(final NodeSet other) {
723
        realize();
×
724
        return realSet.intersection(other);
×
725
    }
726

727
    @Override
728
    public NodeSet union(final NodeSet other) {
729
        realize();
×
730
        return realSet.union(other);
×
731
    }
732

733
    @Override
734
    public NodeSet filterDocuments(final NodeSet otherSet) {
735
        return this;
×
736
    }
737

738
    @Override
739
    public String toString() {
740
        if(realSet == null) {
×
741
            return "Virtual#unknown";
×
742
        }
743
        return "";
×
744
    }
745
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc