404

[ Avaa Bypassed ]




Upload:

Command:

botdev@18.190.157.16: ~ $
# $Id: Step.pm,v 1.35 2001/04/01 16:56:40 matt Exp $

package XML::XPathEngine::Step;
use XML::XPathEngine;
use strict;

# the beginnings of using XS for this file...
# require DynaLoader;
# use vars qw/$VERSION @ISA/;
# $VERSION = '1.0';
# @ISA = qw(DynaLoader);
# 
# bootstrap XML::XPathEngine::Step $VERSION;

sub test_qname () { 0; } # Full name
sub test_ncwild () { 1; } # NCName:*
sub test_any () { 2; } # *

sub test_attr_qname () { 3; } # @ns:attrib
sub test_attr_ncwild () { 4; } # @nc:*
sub test_attr_any () { 5; } # @*

sub test_nt_comment () { 6; } # comment()
sub test_nt_text () { 7; } # text()
sub test_nt_pi () { 8; } # processing-instruction()
sub test_nt_node () { 9; } # node()

sub new {
    my $class = shift;
    my ($pp, $axis, $test, $literal) = @_;
    my $axis_method = "axis_$axis";
    $axis_method =~ tr/-/_/;
    my $self = {
        pp => $pp, # the XML::XPathEngine class
        axis => $axis,
        axis_method => $axis_method,
        test => $test,
        literal => $literal,
        predicates => [],
        };
    bless $self, $class;
}

sub as_string {
    my $self = shift;
    my $string = $self->{axis} . "::";

    my $test = $self->{test};
        
    if ($test == test_nt_pi) {
        $string .= 'processing-instruction(';
        if ($self->{literal}->value) {
            $string .= $self->{literal}->as_string;
        }
        $string .= ")";
    }
    elsif ($test == test_nt_comment) {
        $string .= 'comment()';
    }
    elsif ($test == test_nt_text) {
        $string .= 'text()';
    }
    elsif ($test == test_nt_node) {
        $string .= 'node()';
    }
    elsif ($test == test_ncwild || $test == test_attr_ncwild) {
        $string .= $self->{literal} . ':*';
    }
    else {
        $string .= $self->{literal};
    }
    
    foreach (@{$self->{predicates}}) {
        next unless defined $_;
        $string .= "[" . $_->as_string . "]";
    }
    return $string;
}

sub as_xml {
    my $self = shift;
    my $string = "<Step>\n";
    $string .= "<Axis>" . $self->{axis} . "</Axis>\n";
    my $test = $self->{test};
    
    $string .= "<Test>";
    
    if ($test == test_nt_pi) {
        $string .= '<processing-instruction';
        if ($self->{literal}->value) {
            $string .= '>';
            $string .= $self->{literal}->as_string;
            $string .= '</processing-instruction>';
        }
        else {
            $string .= '/>';
        }
    }
    elsif ($test == test_nt_comment) {
        $string .= '<comment/>';
    }
    elsif ($test == test_nt_text) {
        $string .= '<text/>';
    }
    elsif ($test == test_nt_node) {
        $string .= '<node/>';
    }
    elsif ($test == test_ncwild || $test == test_attr_ncwild) {
        $string .= '<namespace-prefix>' . $self->{literal} . '</namespace-prefix>';
    }
    else {
        $string .= '<nametest>' . $self->{literal} . '</nametest>';
    }
    
    $string .= "</Test>\n";
    
    foreach (@{$self->{predicates}}) {
        next unless defined $_;
        $string .= "<Predicate>\n" . $_->as_xml() . "</Predicate>\n";
    }
    
    $string .= "</Step>\n";
    
    return $string;
}

sub evaluate {
    my $self = shift;
    my $from = shift; # context nodeset

    if( $from && !$from->isa( 'XML::XPathEngine::NodeSet'))
      { 
        my $from_nodeset= XML::XPathEngine::NodeSet->new();
        $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 = XML::XPathEngine::NodeSet->new();
    
    # 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);
        $initial_nodeset->append($self->evaluate_node($from->get_node($i)));
    }
    
#    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 = XML::XPathEngine::NodeSet->new();
    no strict 'refs';
    eval {
        $method->($self, $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 axis_ancestor {
    my $self = shift;
    my ($context, $results) = @_;
    
    my $parent = $context->getParentNode;
        
    START:
    return $results unless $parent;
    if (node_test($self, $parent)) {
        $results->push($parent);
    }
    $parent = $parent->getParentNode;
    goto START;
}

sub axis_ancestor_or_self {
    my $self = shift;
    my ($context, $results) = @_;
    
    START:
    return $results unless $context;
    if (node_test($self, $context)) {
        $results->push($context);
    }
    $context = $context->getParentNode;
    goto START;
}

sub axis_attribute {
    my $self = shift;
    my ($context, $results) = @_;
    
    foreach my $attrib (@{$context->getAttributes}) {
        if ($self->test_attribute($attrib)) {
            $results->push($attrib);
        }
    }
}

sub axis_child {
    my $self = shift;
    my ($context, $results) = @_;
    
    foreach my $node (@{$context->getChildNodes}) {
        if (node_test($self, $node)) {
            $results->push($node);
        }
    }
}

sub axis_descendant {
    my $self = shift;
    my ($context, $results) = @_;

    my @stack = $context->getChildNodes;

    while (@stack) {
        my $node = shift @stack;
        if (node_test($self, $node)) {
            $results->push($node);
        }
        unshift @stack, $node->getChildNodes;
    }
}

sub axis_descendant_or_self {
    my $self = shift;
    my ($context, $results) = @_;
    
    my @stack = ($context);

     while (@stack) {
        my $node = shift @stack;
         if (node_test($self, $node)) {
            $results->push($node);
         }
        #warn "node is a ", ref( $node);
        unshift @stack, $node->getChildNodes;
     }
}

sub axis_following 
  { my $self = shift;
    my ($context, $results) = @_;

    my $elt= $context->getNextSibling || _next_sibling_of_an_ancestor_of( $context);
    while( $elt)
      { if (node_test($self, $elt)) { $results->push( $elt); }
        $elt= $elt->getFirstChild || $elt->getNextSibling || _next_sibling_of_an_ancestor_of( $elt);
      }
  }

sub _next_sibling_of_an_ancestor_of
  { my $elt= shift;
    $elt= $elt->getParentNode || return;
    my $next_elt;
    while( !($next_elt= $elt->getNextSibling))
      { $elt= $elt->getParentNode;  
        return unless( $elt && $elt->can( 'getNextSibling')); 
      }
    return $next_elt;
  }


sub axis_following_sibling {
    my $self = shift;
    my ($context, $results) = @_;

    #warn "in axis_following_sibling";
    while ($context = $context->getNextSibling) {
        if (node_test($self, $context)) {
            $results->push($context);
        }
    }
}

sub axis_namespace {
    my $self = shift;
    my ($context, $results) = @_;
    
    return $results unless $context->isElementNode;
    foreach my $ns (@{$context->getNamespaces}) {
        if ($self->test_namespace($ns)) {
            $results->push($ns);
        }
    }
}

sub axis_parent {
    my $self = shift;
    my ($context, $results) = @_;
    
    my $parent = $context->getParentNode;
    return $results unless $parent;
    if (node_test($self, $parent)) {
        $results->push($parent);
    }
}

sub axis_preceding 
  { my $self = shift;
    my ($context, $results) = @_;

    my $elt= $context->getPreviousSibling || _previous_sibling_of_an_ancestor_of( $context);
    while( $elt)
      { if (node_test($self, $elt)) { $results->push( $elt); }
        $elt= $elt->getLastChild || $elt->getPreviousSibling || _previous_sibling_of_an_ancestor_of( $elt);
      }
  }

sub _previous_sibling_of_an_ancestor_of
  { my $elt= shift;
    $elt= $elt->getParentNode || return;
    my $next_elt;
    while( !($next_elt= $elt->getPreviousSibling))
      { $elt= $elt->getParentNode;  
        return unless $elt->getParentNode; # so we don't have to write a getPreviousSibling 
      }
    return $next_elt;
  }


sub axis_preceding_sibling {
    my $self = shift;
    my ($context, $results) = @_;
    
    while ($context = $context->getPreviousSibling) {
        if (node_test($self, $context)) {
            $results->push($context);
        }
    }
}

sub axis_self {
    my $self = shift;
    my ($context, $results) = @_;
    
    if (node_test($self, $context)) {
        $results->push($context);
    }
}
    
sub node_test {
    my $self = shift;
    my $node = shift;
    
    # if node passes test, return true
    
    my $test = $self->{test};

    return 1 if $test == test_nt_node;
        
    if ($test == test_any) {
        return 1 if $node->isElementNode && defined $node->getName;
    }
        
    local $^W;

    if ($test == test_ncwild) {
        return unless $node->isElementNode;
        return _match_ns( $self, $node);
    }
    elsif ($test == test_qname) {
        return unless $node->isElementNode;
        if ($self->{literal} =~ /:/ || $self->{pp}->{strict_namespaces}) {
            my ($prefix, $name) = _name2prefix_and_local_name( $self->{literal});
            return 1 if( ($name eq $node->getLocalName) && _match_ns( $self, $node));
            }
        else {
            return 1 if $node->getName eq $self->{literal};
        }
    }
    elsif ($test == test_nt_text) {
        return 1 if $node->isTextNode;
    }
    elsif ($test == test_nt_comment) {
        return 1 if $node->isCommentNode;
    }
     elsif ($test == test_nt_pi && !$self->{literal}) {
         return 1 if $node->isPINode;
     }
    elsif ($test == test_nt_pi) {
        return unless $node->isPINode;
        if (my $val = $self->{literal}->value) {
            return 1 if $node->getTarget eq $val;
        }
        else {
            return 1;
        }
    }
    
    return; # fallthrough returns false
}

sub _name2prefix_and_local_name
  { my $name= shift; 
    return $name =~ /:/ ? split(':', $name, 2) : ( '', $name);
  }
sub _name2prefix
  { my $name= shift;
    if( $name=~ m{^(.*?):}) { return $1; } else { return ''; } 
  }

sub _match_ns
  { my( $self, $node)= @_;
    my $pp= $self->{pp};
    my $prefix= _name2prefix( $self->{literal});
    my( $match_ns, $node_ns);
    if( $pp->{uses_namespaces} || $pp->{strict_namespaces})
      { $match_ns = $pp->get_namespace($prefix);
        if( $match_ns || $pp->{strict_namespaces})
          { $node_ns= $node->getNamespace->getValue; }
        else
          { # non-standard behaviour: if the query prefix is not declared
            # compare the 2 prefixes
            $match_ns = $prefix;
            $node_ns  = _name2prefix( $node->getName);
          }
      }
    else
      { $match_ns = $prefix;
        $node_ns  = _name2prefix( $node->getName);
      }

    return $match_ns eq $node_ns;
  }


sub test_attribute {
    my $self = shift;
    my $node = shift;
    
    my $test = $self->{test};
    
    return 1 if ($test == test_attr_any) || ($test == test_nt_node);
        
    if ($test == test_attr_ncwild) {
        return 1 if _match_ns( $self, $node);
    }
    elsif ($test == test_attr_qname) {
        if ($self->{literal} =~ /:/) {
            my ($prefix, $name) = _name2prefix_and_local_name( $self->{literal});

            return 1 if ( ($name eq $node->getLocalName) && ( _match_ns( $self, $node)) );
            }
        else {
            return 1 if $node->getName eq $self->{literal};
        }
    }
    
    return; # fallthrough returns false
}

sub test_namespace {
    my $self = shift;
    my $node = shift;
    
    # Not sure if this is correct. The spec seems very unclear on what
    # constitutes a namespace test... bah!
    
    my $test = $self->{test};
    
    return 1 if $test == test_any; # True for all nodes of principal type
    
    if ($test == test_any) {
        return 1;
    }
    elsif ($self->{literal} eq $node->getExpanded) {
        return 1;
    }
    
    return;
}

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.
    
    if (!ref($nodeset)) { # use ref because nodeset has a bool context
        die "No nodeset!!!";
    }
    
#    warn "Filter by predicate: $predicate\n";
    
    my $newset = XML::XPathEngine::NodeSet->new();

    for(my $i = 1; $i <= $nodeset->size; $i++) {
        # set context set each time 'cos a loc-path in the expr could change it
        $self->{pp}->_set_context_set($nodeset);
        $self->{pp}->_set_context_pos($i);
        my $result = $predicate->evaluate($nodeset->get_node($i));
        if ($result->isa('XML::XPathEngine::Boolean')) {
            if ($result->value) {
                $newset->push($nodeset->get_node($i));
            }
        }
        elsif ($result->isa('XML::XPathEngine::Number')) {
            if ($result->value == $i) {
                $newset->push($nodeset->get_node($i)); last;
            }
        }
        else {
            if ($result->to_boolean->value) {
                $newset->push($nodeset->get_node($i));
            }
        }
    }
    
    return $newset;
}

1;

Filemanager

Name Type Size Permission Actions
Boolean.pm File 1.31 KB 0644
Expr.pm File 19.61 KB 0644
Function.pm File 10.8 KB 0644
Literal.pm File 2.17 KB 0644
LocationPath.pm File 1.1 KB 0644
NodeSet.pm File 4.06 KB 0644
Number.pm File 1.76 KB 0644
Root.pm File 634 B 0644
Step.pm File 14.17 KB 0644
Variable.pm File 850 B 0644