IFClsXDef:
@IFQName: SerialWalker @ClsQName: ManakaiDOMSerialWalker
@enDesc: {ISSUE:: Better name? }
A <IF::SerialWalker> object can be used to traverse the subtree rooted by a node in document order. Unlike <IF::TreeWalker> and <IF::NodeIterator>, the <IF::SerialWalker> object cannot go back toward nodes that had already been seen. The <IF::SerialWalker>, however, revisits a node after its children are visited. This might be useful when an application wants to serialize a subtree in a format where delimiter and / or terminator should be inserted between and / or after nodes.
How the <IF::SerialWalker> works is defined by the following reference algorithm. An implementation does not have to implement this exact algorithm, but it <kwd:MUST> implement its method so that it has the same effect as the reference algorithm when the underlying subtree has not been modified.
At the time of creation of the <IF::SerialWalker>, the implementation <kwd:MUST> prepare a queue and <kwd:MUST> initialize it by pushing the <A::SerialWalker.root> node.
{P:: When the <M::SerialWalker.nextNode> method is invoked,
- If the queue is empty, the method <kwd:MUST> return <DOM::null>. It <kwd:MUST-NOT> change <A::SerialWalker.currentNode>, <A::SerialWalker.currentPhase>, and <A::SerialWalker.currentIndex> attributes.
{LI:: Otherwise,
- Shift a node from the queue. If the node has the phase value, the method <kwd:MUST> return the node. In addition, it <kwd:MUST> set the <A::SerialWalker.currentNode> attribute to the node, the <A::SerialWalker.currentPhase> attribute to the associated phase value, and the <A::SerialWalker.currentIndex> attribute to the integer value that is large by one than the most recent <A::SerialWalker.currentIndex> value when the <A::SerialWalker.currentNode> was same as the shiftted node. If it is the first time the node is set to the <A::SerialWalker.currentNode> attribute, the <A::SerialWalker.currentIndex> attribute <kwd:MUST> set to zero.
{LI:: Otherwise, determine whether the shifted node should be accepted or not, using <A::SerialWalker.expandEntityReferences>, <A::SerialWalker.whatToShow>, and <A::SerialWalker.filter> attributes, as defined for the <IF::TreeWalker> interface.
{LI:: If the node is <C::NodeFilter.FILTER_ACCEPT>ed,
= Prepend the node to the queue, with phase value set to <C::SerialWalker.POST_PHASE>. = Prepend the child nodes of the node, if any, to the queue keeping the document order, without phase value. That is, the first child <kwd:MUST> be located at the top (first position) of the queue.
= Prepend the node to the queue, with phase value set to <C::SerialWalker.PRE_PHASE>. = If the node has a previous sibling in the logical view, preprend the parent node of the node in the logical view, with phase value set to <C::SerialWalker.IN_PHASE>.
= Invoke the algorithm recursively.
}
{LI:: If the node is <C::NodeFilter.FILTER_SKIP>ped,
= Prepend the child nodes of the node, if any,
to the queue keeping the document order,
without phase value. That is, the first child
<kwd:MUST> be located at the top (first
position) of the queue.
= Invoke the algorithm recursively.
}
{LI:: If the node is <C::NodeFilter.MANAKAI_FILTER_OPAQUE>, = Prepend the node to the queue, with phase value set to <C::SerialWalker.POST_PHASE>.
= Prepend the node to the queue, with phase value set to <C::SerialWalker.PRE_PHASE>. = If the node has a previous sibling in the logical view, preprend the parent node of the node in the logical view, with phase value set to <C::SerialWalker.IN_PHASE>.
= Invoke the algorithm recursively.
}
- Otherwise, invoke the algorithm recursively.
} }
}
{NOTE::
This algorithm will stop unless nodes are simulataneously
populated to the underlying tree by application via
e.g. <IF::NodeFilter>.
}
Implementation <kwd:MAY> choose to implement the <IF::SerialWalker> using another algorithm. In particular, when the <IF::NodeFilter> is invoked might vary across implementations. Implementations should try to return the same effect as the reference algorithm even if the underlying subtree is modified, but applications <kwd:MUST-NOT> rely on the particular implementation's behavior.
As long as the underlying subtree is not modified, a node will never set to the <A::SerialWalker.currentNode> attribute with the <A::SerialWalker.currentPhase> of <C::SerialWalker.PRE_PHASE>. If the underlying subtree is modified, depending on how the method is implemented, it might be in case. For the purpose of description below, such duplication was considered as if they were different nodes.
{P:: Even if the underlying subtree has been modified during
the traversal, the implementation <kwd:MUST> ensure
the following conditions are met:
- A node <kwd:MUST-NOT> be set to the <A::SerialWalker.currentNode> attribute with the <A::SerialWalker.currentPhase> of <C::SerialWalker.PRE_PHASE> or <C::SerialWalker.POST_PHASE> more than once respectively.
- A node <kwd:MUST-NOT> be set to the <A::SerialWalker.currentNode> attribute with the <A::SerialWalker.currentPhase> of <C::SerialWalker.IN_PHASE> before it is once set to the same node with the <A::SerialWalker.currentPhase> of <C::SerialWalker.PRE_PHASE>, or after it is once set to the same node with the <A::SerialWalker.currentPhase> of <C::SerialWalker.POST_PHASE>.
- A node <kwd:MUST-NOT> be set to the <A::SerialWalker.currentNode> attribute with the <A::SerialWalker.currentPhase> of <C::SerialWalker.POST_PHASE> before it is once set to the same node with the <A::SerialWalker.currentPhase> of <C::SerialWalker.PRE_PHASE>.
- A node <kwd:MUST> be set to the <A::SerialWalker.currentNode> attribute with the <A::SerialWalker.currentPhase> of <C::SerialWalker.POST_PHASE> later if it is once set to the same node with the <A::SerialWalker.currentPhase> of <C::SerialWalker.PRE_PHASE>.
- The <A::SerialWalker.currentPhase> <kwd:MUST-NOT> set to <C::SerialWalker.PRE_PHASE> unless its value is <C::SerialWalker.PRE_PHASE> or <C::SerialWalker.IN_PHASE>.
- The <A::SerialWalker.currentPhase> <kwd:MUST-NOT> set to <C::SerialWalker.IN_PHASE> unless its value is <C::SerialWalker.POST_PHASE>.
- The <A::SerialWalker.currentPhase> <kwd:MUST-NOT> set to <C::SerialWalker.POST_PHASE> unless its value is <C::SerialWalker.PRE_PHASE> or <C::SerialWalker.POST_PHASE>.
- If a node <VAR::N> is set to the <A::SerialWalker.currentNode> attribute and then another node <VAR::M> is set to the attribute with both <A::SerialWalker.currentPhase> of <C::SerialWalker.PRE_PHASE>, the <A::SerialWalker.currentNode> attribute <kwd:MUST-NOT> be set to <VAR::N> with <A::SerialWalker.currentPhase> of <C::SerialWalker.POST_PHASE> before once the <A::SerialWalker.currentNode> attribute is set to <VAR::M> with <A::SerialWalker.currentPhase> of <C::SerialWalker.POST_PHASE>.
- For each time the <A::SerialWalker.currentNode> attribute is set to a node, the <A::SerialWalker.currentIndex> <kwd:MUST> be increased by one. If the node is set to the attribute for the first time, the <A::SerialWalker.currentIndex> <kwd:MUST> be set to zero. That is, with and only with <C::SerialWalker.PRE_PHASE> the <A::SerialWalker.currentIndex> can be zero. }
@CODE: @@QName: createSWForTest @@PerlDef: __CODE{tc|createEmptyDocumentForTest:: $doc => $doc}__;
my $docel = $doc-><M::Document.createElement> ('docel');
$doc-><M::Node.appendChild> ($docel);
my $doctrv = $doc-><M::Node.getFeature> ('Traversal', '2.0'); $sw = $doctrv-><M::DocumentTraversal.manakaiCreateSerialWalker> ($docel);
@LXAttr: @@Name: root @@enDesc: The root node of the <IF::SerialWalker>. @@Type: Node @@Get: @@@enDesc: The root node set when the <IF::SerialWalker> is created. @@@PerlDef: $r = $self->{root};
@LXAttr:
@@Name: currentNode
@@enDesc:
The current node of the <IF::SerialWalker>.
{NOTE:: Unlike <IF::TreeWalker>, the <A::SerialWalker.currentNode> attribute is read-only. } @@Type: Node @@Get: @@@enDesc: The current node. @@@PerlDef: $r = $self->{current_node};
@mShortConstGroup:
@@IFQName: SerialWalkerPhase
@@enDesc:
An integer to indicate a phase of <IF::SerialWalker>.
@@mConst: @@@Name: PRE_PHASE @@@intValue: 1 @@@enDesc: The <A::SerialWalker.currentNode> is visited by the <IF::SerialWalker> for the first time. It is expected that the same node will be revisited with <A::SerialWalker.currentPhase> of <C::SerialWalker.IN_PHASE> (optionally) and <C::SerialWalker.POST_PHASE> later. @@mConst: @@@Name: IN_PHASE @@@intValue: 2 @@@enDesc: The <A::SerialWalker.currentNode> is visited by the <IF::SerialWalker>, not for the first or last time, between visitings to its two child nodes. @@mConst: @@@Name: POST_PHASE @@@intValue: 3 @@@enDesc: The <A::SerialWalker.currentNode> is visited by the <IF::SerialWalker> for the last time.
@LXAttr:
@@Name: currentPhase
@@enDesc:
The current phase of the <IF::SerialWalker>.
@@Type: unsignedShort
@@actualType: SerialWalkerPhase
@@Get:
@@@enDesc:
An integer to indicate the current phase.
@@@PerlDef:
$r = $self->{current_phase};
@LXAttr: @@Name: currentIndex @@enDesc: The current index of the <IF::SerialWalker>. The <DFN::current index> represents how many times the <A::SerialWalker.currentNode> is exposed through the <IF::SerialWalker>. @@Type: unsignedLong @@Get: @@@enDesc: An integer to indicate the current index. @@@PerlDef: $r = $self->{current_index};
@LXAttr: @@Name: expandEntityReferences @@enDesc: The value of the flag that determines whether the children of <IF::tx|EntityReference> nodes are visible to the <IF::SerialWalker>.
{NOTE:: To produce a view of the document that has entity references expanded and does not expose the <IF::tx|EntityReference> nodes themselves, use the <A::SerialWalker.whatToShow> flags to hide the <IF::tx|EntityReference> nodes and set the <A::SerialWalker.expandEntityReferences> flag to <DOM::true> when creating the <IF::SerialWalker>.
To produce a view of the document that has <IF::tx|EntityReference> nodes but no entity expansion, use the <A::SerialWalker.whatToShow> flags to show the <IF::tx|EntityReference> nodes and set the <A::SerialWalker.expandEntityReferences> flag to <DOM::false> when creating the <IF::SerialWalker>. } @@Type: boolean @@Get: @@@enDesc: The <CODE::entityReferenceExpansion> value that was specified when the <IF::SerialWalker> was created. @@@TrueCase: @@@@enDesc: The children and their descendants of <IF::tx|EntityReference> nodes will <EM::not> be rejected. Note that the <A::SerialWalker.whatToShow> flags and the <A::SerialWalker.filter> set to the <IF::SerialWalker> might reject them as usual way. @@@FalseCase: @@@@enDesc: The children and their descendants of entity reference nodes will be rejected. This rejection takes precedence over the <A::SerialWalker.whatToShow> flags and the <A::SerialWalker.filter> set to the <IF::SerialWalker>, if any. @@@PerlDef: $r = $self->{expand_entity_references};
@LXAttr: @@Name: filter @@enDesc: The filter used to screen nodes. @@Type: NodeFilter @@Get: @@@enDesc: The <IF::NodeFilter> that was specified when the <IF::SerialWalker> was created. @@@nullCase: @@@@enDesc: If no <IF::NodeFilter> was specified when the <IF::SerialWalker> was created. @@@PerlDef: $r = $self->{filter};
@LXAttr: @@Name: whatToShow @@enDesc: The flags that determines which node types are presented via the <IF::SerialWalker>. The available set of constants is defined in the <IF::NodeFilter> interface.
Nodes not accepted by the <A::SerialWalker.whatToShow> flags will be skipped, but their children may still be considered. This skip takes precedence over the <A::SerialWalker.filter> set to the <IF::SerialWalker>, if any. @@Type: unsignedShort @@actualType: WhatToShow @@Get: @@@enDesc: The <CODE::whatToShow> value that was specified when the <IF::SerialWalker> was created. @@@PerlDef: $r = $self->{what_to_show};
@LXMethod: @@Name: nextNode @@enDesc: Returns the node next to the <A::SerialWalker.currentNode> of the <IF::SerialWalker>. @@Return: @@@Type: Node @@@enDesc: The next node. @@@nullCase: @@@@enDesc: If no next node.