Pull processing

The internal mechanisms and API for pull processing have been substantially rewritten in this release.

There is a new method iterateEvents() on the Expression class, which evaluates the expression and returns its result as a sequence of PullEvent objects. A PullEvent can be an Item (that is, a NodeInfo or AtomicValue). It can also be a StartElement, EndElement, StartDocument, or EndDocument event. Within the content of a document or element, the child nodes can be represented either as complete nodes (including element nodes, and potentially even document nodes, in which case the document wrapper should be ignored) or as nested event sequences using further StartElement and EndElement events.

An EventIterator is also a PullEvent, so a stream of events can contain nested streams. If you want to process the events without having to handle this nesting, you can flatten the sequence by calling the static method EventStackIterator.flatten().

To serialize the results of a query, there is a static method EventIteratorToReceiver.copy() which reads the events from a pull pipeline (an EventIterator), and pushes them to a push pipeline (a Receiver). However, if you are serializing the results then it probably makes sense in most cases to evaluate the query in push mode to start with.

A sequence in which all the PullEvents are Item objects is called a composed sequence. A sequence in which all document and element nodes are split into their constituent events is called a decomposed sequence. You can turn any sequence of PullEvents into a fully composed sequence by wrapping it in a SequenceComposer. This will physically construct any document or element nodes that were represented in the sequence in decomposed form. Similarly, you can turn any sequence into a fully decomposed sequence by wrapping it in a Decomposer.