package Message::DOM::SerialWalker; use strict; our $VERSION=do{my @r=(q$Revision: 1.1 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r}; push our @ISA, 'Message::IF::SerialWalker'; sub AUTOLOAD { my $method_name = our $AUTOLOAD; $method_name =~ s/.*:://; return if $method_name eq 'DESTROY'; if ({ current_index => 1, current_node => 1, current_phase => 1, expand_entity_references => 1, filter => 1, root => 1, what_to_show => 1, }->{$method_name}) { no strict 'refs'; eval qq{ sub $method_name (\$) { \$_[0]->{$method_name} } }; goto &{ $AUTOLOAD }; } else { require Carp; Carp::croak (qq); } } # AUTOLOAD ## |SerialWalker| constants ## |SerialWalkerPhase| sub PRE_PHASE () { 1 } sub IN_PHASE () { 2 } sub POST_PHASE () { 3 } ## |SerialWalker| attributes sub current_index ($); sub current_node ($); sub current_phase ($); sub expand_entity_references ($); sub filter ($); sub root ($); sub what_to_show ($); ## |SerialWalker| methods sub next_node ($) { my $self = $_[0]; A: { unless (@{$self->{next}}) { # } elsif ($self->{next}->[0]->[1] != 0) { my $v = shift @{$self->{next}}; $self->{current_phase} = $v->[1]; $self->{current_index} = $self->{_index}->{$v->[2]}++; return ($self->{current_node} = $v->[0]); } else { my $v = shift @{$self->{next}}; my $vresult = $self->_test_node ($v->[0]); my @pnext; if ($vresult == 1) { # FILTER_ACCEPT my $key = $v->[2].'.'.$self->{_index}->{$v->[2]}; push @pnext, [$v->[3], IN_PHASE, $v->[4]] if defined $v->[3] and $self->{_index}->{$v->[4].'.1'}; $self->{_index}->{$key} = 0; push @pnext, [$v->[0], PRE_PHASE, $key]; push @pnext, [$_, 0, $key, $v->[0], $key] for @{$v->[0]->child_nodes}; push @pnext, [$v->[0], POST_PHASE, $key]; } elsif ($vresult == 3) { # FILTER_SKIP push @pnext, [$_, 0, $v->[2], $v->[3], $v->[4]] for @{$v->[0]->child_nodes}; } elsif ($vresult == 12101) { # MANAKAI_FILTER_OPAQUE my $key = $v->[2].'.'.$self->{_index}->{$v->[2]}; push @pnext, [$v->[3], IN_PHASE, $v->[4]] if defined $v->[3] and $self->{_index}->{$v->[4].'.1'}; $self->{_index}->{$key} = 0; push @pnext, [$v->[0], PRE_PHASE, $key]; push @pnext, [$v->[0], POST_PHASE, $key]; } unshift @{$self->{next}}, @pnext; redo A; } } # A return undef; } # next_node sub _test_node ($$) { ## NOTE: There is a code clone in |TreeWalker.pm|. unless ($_[0]->{expand_entity_references}) { my $parent = $_[1]->parent_node; if (defined $parent and $parent->node_type == 5) { # ENTITY_REFERENCE_NODE return 2; # FILTER_REJECT ## NOTE: Even if |NodeIterator|. } } if ($_[0]->{what_to_show} != 0xFFFFFFFF) { # SHOW_ALL my $nt = $_[1]->node_type; if ($nt < 33 and ($_[0]->{what_to_show} & (1 << ($nt-1)))) { # } else { return 3; # FILTER_SKIP } } if (defined $_[0]->{filter}) { local $Error::Depth = $Error::Depth + 1; return $_[0]->{filter}->($_[1]); } else { return 1; # FILTER_ACCEPT } } # _test_node package Message::IF::SerialWalker; package Message::DOM::Document; sub manakai_create_serial_walker ($$;$$$) { unless (defined $_[1]) { require Message::DOM::DOMException; report Message::DOM::DOMException -object => $_[0], -type => 'NOT_SUPPORTED_ERR', -subtype => 'NULLPO_ERR'; } ## TODO: Initials for current_index and current_phase return bless { root => $_[1], what_to_show => 0+($_[2] or 0), filter => $_[3], expand_entity_references => $_[4] ? 1 : 0, current_node => $_[1], next => [[$_[1], 0, '']], _index => {'' => 0}, }, 'Message::DOM::SerialWalker'; } # manakai_create_serial_walker =pod ## TODO: Documentation @LXMethod: @@Name: manakaiCreateSerialWalker @@enDesc: Creates a new object. @@Param: @@@Name: root @@@Type: Node @@@enDesc: The node that will serve as the root for the . The flags and the are not considered when setting this value; any node type will be accepted as the root. The of the created is initialized to the node, whether or not it is visible. The functions as a stopping point for traversal methods that look upwards in the document structure, i.e. . The be . If it is, the implementation throw an exception. @@Param: @@@Name: whatToShow @@@Type: unsignedShort @@@actualType: WhatToShow @@@enDesc: The flags that specifies which node types may appear in the logical view of the tree presented by the . The set of the flags are defined in the interface. They can be combined using the binary operation. @@Param: @@@Name: filter @@@Type: NodeFilter @@@enDesc: The to be used with the created . @@@nullCase: @@@@enDesc: Indicates that no filter is used. @@Param: @@@Name: entityReferenceExpansion @@@Type: boolean @@@enDesc: The flag of the created . @@@TrueCase: @@@@enDesc: The contents of the nodes presented in the logical view. @@@FalseCase: @@@@enDesc: The contents of the nodes are presented in the logical view. @@Return: @@@Type: SerialWalker @@@enDesc: {P:: The newly created . It have attributes set as: - ::: . - ::: . - ::: . - ::: . - ::: . } @@@dx:raises: @@@@@: c|SET_TO_NULL_ERR @@@@enDesc: If the specified is . {ISSUE:: Better name? } A object can be used to traverse the subtree rooted by a node in document order. Unlike and , the object cannot go back toward nodes that had already been seen. The , 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 works is defined by the following reference algorithm. An implementation does not have to implement this exact algorithm, but it 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 , the implementation prepare a queue and initialize it by pushing the node. {P:: When the method is invoked, - If the queue is empty, the method return . It change , , and attributes. {LI:: Otherwise, - Shift a node from the queue. If the node has the phase value, the method return the node. In addition, it set the attribute to the node, the attribute to the associated phase value, and the attribute to the integer value that is large by one than the most recent value when the was same as the shiftted node. If it is the first time the node is set to the attribute, the attribute set to zero. {LI:: Otherwise, determine whether the shifted node should be accepted or not, using , , and attributes, as defined for the interface. {LI:: If the node is ed, = Prepend the node to the queue, with phase value set to . = Prepend the child nodes of the node, if any, to the queue keeping the document order, without phase value. That is, the first child be located at the top (first position) of the queue. = Prepend the node to the queue, with phase value set to . = 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 . = Invoke the algorithm recursively. } {LI:: If the node is 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 be located at the top (first position) of the queue. = Invoke the algorithm recursively. } {LI:: If the node is , = Prepend the node to the queue, with phase value set to . = Prepend the node to the queue, with phase value set to . = 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 . = 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. . } Implementation choose to implement the using another algorithm. In particular, when the 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 rely on the particular implementation's behavior. As long as the underlying subtree is not modified, a node will never set to the attribute with the of . 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 ensure the following conditions are met: - A node be set to the attribute with the of or more than once respectively. - A node be set to the attribute with the of before it is once set to the same node with the of , or after it is once set to the same node with the of . - A node be set to the attribute with the of before it is once set to the same node with the of . - A node be set to the attribute with the of later if it is once set to the same node with the of . - The set to unless its value is or . - The set to unless its value is . - The set to unless its value is or . - If a node is set to the attribute and then another node is set to the attribute with both of , the attribute be set to with of before once the attribute is set to with of . - For each time the attribute is set to a node, the be increased by one. If the node is set to the attribute for the first time, the be set to zero. That is, with and only with the can be zero. } @LXAttr: @@Name: root @@enDesc: The root node of the . @@Type: Node @@Get: @@@enDesc: The root node set when the is created. @LXAttr: @@Name: currentNode @@enDesc: The current node of the . {NOTE:: Unlike , the attribute is read-only. } @@Type: Node @@Get: @@@enDesc: The current node. @mShortConstGroup: @@IFQName: SerialWalkerPhase @@enDesc: An integer to indicate a phase of . @@mConst: @@@Name: PRE_PHASE @@@intValue: 1 @@@enDesc: The is visited by the for the first time. It is expected that the same node will be revisited with of (optionally) and later. @@mConst: @@@Name: IN_PHASE @@@intValue: 2 @@@enDesc: The is visited by the , not for the first or last time, between visitings to its two child nodes. @@mConst: @@@Name: POST_PHASE @@@intValue: 3 @@@enDesc: The is visited by the for the last time. @LXAttr: @@Name: currentPhase @@enDesc: The current phase of the . @@Type: unsignedShort @@actualType: SerialWalkerPhase @@Get: @@@enDesc: An integer to indicate the current phase. @LXAttr: @@Name: currentIndex @@enDesc: The current index of the . The represents how many times the is exposed through the . @@Type: unsignedLong @@Get: @@@enDesc: An integer to indicate the current index. @LXAttr: @@Name: expandEntityReferences @@enDesc: The value of the flag that determines whether the children of nodes are visible to the . {NOTE:: To produce a view of the document that has entity references expanded and does not expose the nodes themselves, use the flags to hide the nodes and set the flag to when creating the . To produce a view of the document that has nodes but no entity expansion, use the flags to show the nodes and set the flag to when creating the . } @@Type: boolean @@Get: @@@enDesc: The value that was specified when the was created. @@@TrueCase: @@@@enDesc: The children and their descendants of nodes will be rejected. Note that the flags and the set to the 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 flags and the set to the , if any. @LXAttr: @@Name: filter @@enDesc: The filter used to screen nodes. @@Type: NodeFilter @@Get: @@@enDesc: The that was specified when the was created. @@@nullCase: @@@@enDesc: If no was specified when the was created. @LXAttr: @@Name: whatToShow @@enDesc: The flags that determines which node types are presented via the . The available set of constants is defined in the interface. Nodes not accepted by the flags will be skipped, but their children may still be considered. This skip takes precedence over the set to the , if any. @@Type: unsignedShort @@actualType: WhatToShow @@Get: @@@enDesc: The value that was specified when the was created. @LXMethod: @@Name: nextNode @@enDesc: Returns the node next to the of the . @@Return: @@@Type: Node @@@enDesc: The next node. @@@nullCase: @@@@enDesc: If no next node. =cut =head1 LICENSE Copyright 2007 Wakaba This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; ## $Date: 2007/07/14 16:32:28 $