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 )