HTML-Object
view release on metacpan or search on metacpan
lib/HTML/Object/XPath/Step.pm view on Meta::CPAN
}
}
sub axis_preceding_sibling
{
my $self = shift( @_ );
my( $context, $results ) = @_;
while( $context = $context->getPreviousSibling )
{
if( $self->node_test( $context ) )
{
$results->push( $context );
}
}
}
sub axis_self
{
my $self = shift( @_ );
my( $context, $results ) = @_;
if( $self->node_test( $context ) )
{
$results->push( $context );
}
}
sub evaluate
{
my $self = shift( @_ );
# context nodeset
my $from = shift( @_ );
if( $from && !$from->isa( 'HTML::Object::XPath::NodeSet' ) )
{
my $from_nodeset = $self->new_nodeset();
$from_nodeset->push( $from );
$from = $from_nodeset;
}
# warn "Step::evaluate called with ", $from->size, " length nodeset\n";
my $saved_context = $self->{pp}->_get_context_set;
my $saved_pos = $self->{pp}->_get_context_pos;
$self->{pp}->_set_context_set( $from );
my $initial_nodeset = $self->new_nodeset();
# See spec section 2.1, paragraphs 3,4,5:
# The node-set selected by the location step is the node-set
# that results from generating an initial node set from the
# axis and node-test, and then filtering that node-set by
# each of the predicates in turn.
# Make each node in the nodeset be the context node, one by one
for( my $i = 1; $i <= $from->size; $i++ )
{
$self->{pp}->_set_context_pos( $i );
# Capture the intermediate NodeSet in a lexical variable and release
# it immediately after appending its nodes to $initial_nodeset.
# Without this, the temporary NodeSet returned by evaluate_node()
# would stay alive until the end of the for() scope, causing
# accumulated memory pressure when $from is large (e.g. after a
# descendant-or-self step that returns every node in the document).
{
my $tmp = $self->evaluate_node( $from->get_node( $i ) );
$initial_nodeset->append( $tmp );
}
}
# warn "Step::evaluate initial nodeset size: ", $initial_nodeset->size, "\n";
$self->{pp}->_set_context_set( $saved_context );
$self->{pp}->_set_context_pos( $saved_pos );
return( $initial_nodeset );
}
# Evaluate the step against a particular node
sub evaluate_node
{
my $self = shift( @_ );
my $context = shift( @_ );
# warn "Evaluate node: $self->{axis}\n";
# warn "Node: ", $context->[node_name], "\n";
my $method = $self->{axis_method};
my $results = $self->new_nodeset();
no strict 'refs';
eval{ $self->$method( $context, $results ); };
if( $@ )
{
die( "axis $method not implemented [$@]\n" );
}
# warn("results: ", join('><', map {$_->string_value} @$results), "\n");
# filter initial nodeset by each predicate
foreach my $predicate ( @{$self->{predicates}} )
{
$results = $self->filter_by_predicate( $results, $predicate );
}
return( $results );
}
sub filter_by_predicate
{
my $self = shift( @_ );
my( $nodeset, $predicate ) = @_;
# See spec section 2.4, paragraphs 2 & 3:
# For each node in the node-set to be filtered, the predicate Expr
# is evaluated with that node as the context node, with the number
# of nodes in the node set as the context size, and with the
# proximity position of the node in the node set with respect to
# the axis as the context position.
# use ref because nodeset has a bool context
if( !ref( $nodeset ) )
{
die( "No nodeset!!!" );
}
# warn "Filter by predicate: $predicate\n";
( run in 0.694 second using v1.01-cache-2.11-cpan-d7a12ab2c7f )