home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / perl5 / XML / XPath / Step.pm < prev    next >
Encoding:
Perl POD Document  |  2001-04-01  |  13.1 KB  |  520 lines

  1. # $Id: Step.pm,v 1.35 2001/04/01 16:56:40 matt Exp $
  2.  
  3. package XML::XPath::Step;
  4. use XML::XPath::Parser;
  5. use XML::XPath::Node;
  6. use strict;
  7.  
  8. # the beginnings of using XS for this file...
  9. # require DynaLoader;
  10. # use vars qw/$VERSION @ISA/;
  11. # $VERSION = '1.0';
  12. # @ISA = qw(DynaLoader);
  13. # bootstrap XML::XPath::Step $VERSION;
  14.  
  15. sub test_qname () { 0; } # Full name
  16. sub test_ncwild () { 1; } # NCName:*
  17. sub test_any () { 2; } # *
  18.  
  19. sub test_attr_qname () { 3; } # @ns:attrib
  20. sub test_attr_ncwild () { 4; } # @nc:*
  21. sub test_attr_any () { 5; } # @*
  22.  
  23. sub test_nt_comment () { 6; } # comment()
  24. sub test_nt_text () { 7; } # text()
  25. sub test_nt_pi () { 8; } # processing-instruction()
  26. sub test_nt_node () { 9; } # node()
  27.  
  28. sub new {
  29.     my $class = shift;
  30.     my ($pp, $axis, $test, $literal) = @_;
  31.     my $axis_method = "axis_$axis";
  32.     $axis_method =~ tr/-/_/;
  33.     my $self = {
  34.         pp => $pp, # the XML::XPath::Parser class
  35.         axis => $axis,
  36.         axis_method => $axis_method,
  37.         test => $test,
  38.         literal => $literal,
  39.         predicates => [],
  40.         };
  41.     bless $self, $class;
  42. }
  43.  
  44. sub as_string {
  45.     my $self = shift;
  46.     my $string = $self->{axis} . "::";
  47.  
  48.     my $test = $self->{test};
  49.         
  50.     if ($test == test_nt_pi) {
  51.         $string .= 'processing-instruction(';
  52.         if ($self->{literal}->value) {
  53.             $string .= $self->{literal}->as_string;
  54.         }
  55.         $string .= ")";
  56.     }
  57.     elsif ($test == test_nt_comment) {
  58.         $string .= 'comment()';
  59.     }
  60.     elsif ($test == test_nt_text) {
  61.         $string .= 'text()';
  62.     }
  63.     elsif ($test == test_nt_node) {
  64.         $string .= 'node()';
  65.     }
  66.     elsif ($test == test_ncwild || $test == test_attr_ncwild) {
  67.         $string .= $self->{literal} . ':*';
  68.     }
  69.     else {
  70.         $string .= $self->{literal};
  71.     }
  72.     
  73.     foreach (@{$self->{predicates}}) {
  74.         next unless defined $_;
  75.         $string .= "[" . $_->as_string . "]";
  76.     }
  77.     return $string;
  78. }
  79.  
  80. sub as_xml {
  81.     my $self = shift;
  82.     my $string = "<Step>\n";
  83.     $string .= "<Axis>" . $self->{axis} . "</Axis>\n";
  84.     my $test = $self->{test};
  85.     
  86.     $string .= "<Test>";
  87.     
  88.     if ($test == test_nt_pi) {
  89.         $string .= '<processing-instruction';
  90.         if ($self->{literal}->value) {
  91.             $string .= '>';
  92.             $string .= $self->{literal}->as_string;
  93.             $string .= '</processing-instruction>';
  94.         }
  95.         else {
  96.             $string .= '/>';
  97.         }
  98.     }
  99.     elsif ($test == test_nt_comment) {
  100.         $string .= '<comment/>';
  101.     }
  102.     elsif ($test == test_nt_text) {
  103.         $string .= '<text/>';
  104.     }
  105.     elsif ($test == test_nt_node) {
  106.         $string .= '<node/>';
  107.     }
  108.     elsif ($test == test_ncwild || $test == test_attr_ncwild) {
  109.         $string .= '<namespace-prefix>' . $self->{literal} . '</namespace-prefix>';
  110.     }
  111.     else {
  112.         $string .= '<nametest>' . $self->{literal} . '</nametest>';
  113.     }
  114.     
  115.     $string .= "</Test>\n";
  116.     
  117.     foreach (@{$self->{predicates}}) {
  118.         next unless defined $_;
  119.         $string .= "<Predicate>\n" . $_->as_xml() . "</Predicate>\n";
  120.     }
  121.     
  122.     $string .= "</Step>\n";
  123.     
  124.     return $string;
  125. }
  126.  
  127. sub evaluate {
  128.     my $self = shift;
  129.     my $from = shift; # context nodeset
  130.     
  131. #    warn "Step::evaluate called with ", $from->size, " length nodeset\n";
  132.     
  133.     $self->{pp}->set_context_set($from);
  134.     
  135.     my $initial_nodeset = XML::XPath::NodeSet->new();
  136.     
  137.     # See spec section 2.1, paragraphs 3,4,5:
  138.     # The node-set selected by the location step is the node-set
  139.     # that results from generating an initial node set from the
  140.     # axis and node-test, and then filtering that node-set by
  141.     # each of the predicates in turn.
  142.     
  143.     # Make each node in the nodeset be the context node, one by one
  144.     for(my $i = 1; $i <= $from->size; $i++) {
  145.         $self->{pp}->set_context_pos($i);
  146.         $initial_nodeset->append($self->evaluate_node($from->get_node($i)));
  147.     }
  148.     
  149. #    warn "Step::evaluate initial nodeset size: ", $initial_nodeset->size, "\n";
  150.     
  151.     $self->{pp}->set_context_set(undef);
  152.  
  153.     $initial_nodeset->sort;
  154.         
  155.     return $initial_nodeset;
  156. }
  157.  
  158. # Evaluate the step against a particular node
  159. sub evaluate_node {
  160.     my $self = shift;
  161.     my $context = shift;
  162.     
  163. #    warn "Evaluate node: $self->{axis}\n";
  164.     
  165. #    warn "Node: ", $context->[node_name], "\n";
  166.     
  167.     my $method = $self->{axis_method};
  168.     
  169.     my $results = XML::XPath::NodeSet->new();
  170.     no strict 'refs';
  171.     eval {
  172.         $method->($self, $context, $results);
  173.     };
  174.     if ($@) {
  175.         die "axis $method not implemented [$@]\n";
  176.     }
  177.     
  178. #    warn("results: ", join('><', map {$_->string_value} @$results), "\n");
  179.     # filter initial nodeset by each predicate
  180.     foreach my $predicate (@{$self->{predicates}}) {
  181.         $results = $self->filter_by_predicate($results, $predicate);
  182.     }
  183.     
  184.     return $results;
  185. }
  186.  
  187. sub axis_ancestor {
  188.     my $self = shift;
  189.     my ($context, $results) = @_;
  190.     
  191.     my $parent = $context->getParentNode;
  192.         
  193.     START:
  194.     return $results unless $parent;
  195.     if (node_test($self, $parent)) {
  196.         $results->push($parent);
  197.     }
  198.     $parent = $parent->getParentNode;
  199.     goto START;
  200. }
  201.  
  202. sub axis_ancestor_or_self {
  203.     my $self = shift;
  204.     my ($context, $results) = @_;
  205.     
  206.     START:
  207.     return $results unless $context;
  208.     if (node_test($self, $context)) {
  209.         $results->push($context);
  210.     }
  211.     $context = $context->getParentNode;
  212.     goto START;
  213. }
  214.  
  215. sub axis_attribute {
  216.     my $self = shift;
  217.     my ($context, $results) = @_;
  218.     
  219.     foreach my $attrib (@{$context->getAttributes}) {
  220.         if ($self->test_attribute($attrib)) {
  221.             $results->push($attrib);
  222.         }
  223.     }
  224. }
  225.  
  226. sub axis_child {
  227.     my $self = shift;
  228.     my ($context, $results) = @_;
  229.     
  230.     foreach my $node (@{$context->getChildNodes}) {
  231.         if (node_test($self, $node)) {
  232.             $results->push($node);
  233.         }
  234.     }
  235. }
  236.  
  237. sub axis_descendant {
  238.     my $self = shift;
  239.     my ($context, $results) = @_;
  240.  
  241.     my @stack = $context->getChildNodes;
  242.  
  243.     while (@stack) {
  244.         my $node = pop @stack;
  245.         if (node_test($self, $node)) {
  246.             $results->unshift($node);
  247.         }
  248.         push @stack, $node->getChildNodes;
  249.     }
  250. }
  251.  
  252. sub axis_descendant_or_self {
  253.     my $self = shift;
  254.     my ($context, $results) = @_;
  255.     
  256.     my @stack = ($context);
  257.     
  258.     while (@stack) {
  259.         my $node = pop @stack;
  260.         if (node_test($self, $node)) {
  261.             $results->unshift($node);
  262.         }
  263.         push @stack, $node->getChildNodes;
  264.     }
  265. }
  266.  
  267. sub axis_following {
  268.     my $self = shift;
  269.     my ($context, $results) = @_;
  270.     
  271.     START:
  272.  
  273.     my $parent = $context->getParentNode;
  274.     return $results unless $parent;
  275.         
  276.     while ($context = $context->getNextSibling) {
  277.         axis_descendant_or_self($self, $context, $results);
  278.     }
  279.  
  280.     $context = $parent;
  281.     goto START;
  282. }
  283.  
  284. sub axis_following_sibling {
  285.     my $self = shift;
  286.     my ($context, $results) = @_;
  287.  
  288.     while ($context = $context->getNextSibling) {
  289.         if (node_test($self, $context)) {
  290.             $results->push($context);
  291.         }
  292.     }
  293. }
  294.  
  295. sub axis_namespace {
  296.     my $self = shift;
  297.     my ($context, $results) = @_;
  298.     
  299.     return $results unless $context->isElementNode;
  300.     foreach my $ns (@{$context->getNamespaces}) {
  301.         if ($self->test_namespace($ns)) {
  302.             $results->push($ns);
  303.         }
  304.     }
  305. }
  306.  
  307. sub axis_parent {
  308.     my $self = shift;
  309.     my ($context, $results) = @_;
  310.     
  311.     my $parent = $context->getParentNode;
  312.     return $results unless $parent;
  313.     if (node_test($self, $parent)) {
  314.         $results->push($parent);
  315.     }
  316. }
  317.  
  318. sub axis_preceding {
  319.     my $self = shift;
  320.     my ($context, $results) = @_;
  321.     
  322.     # all preceding nodes in document order, except ancestors
  323.     
  324.     START:
  325.  
  326.     my $parent = $context->getParentNode;
  327.     return $results unless $parent;
  328.  
  329.     while ($context = $context->getPreviousSibling) {
  330.         axis_descendant_or_self($self, $context, $results);
  331.     }
  332.     
  333.     $context = $parent;
  334.     goto START;
  335. }
  336.  
  337. sub axis_preceding_sibling {
  338.     my $self = shift;
  339.     my ($context, $results) = @_;
  340.     
  341.     while ($context = $context->getPreviousSibling) {
  342.         if (node_test($self, $context)) {
  343.             $results->push($context);
  344.         }
  345.     }
  346. }
  347.  
  348. sub axis_self {
  349.     my $self = shift;
  350.     my ($context, $results) = @_;
  351.     
  352.     if (node_test($self, $context)) {
  353.         $results->push($context);
  354.     }
  355. }
  356.     
  357. sub node_test {
  358.     my $self = shift;
  359.     my $node = shift;
  360.     
  361.     # if node passes test, return true
  362.     
  363.     my $test = $self->{test};
  364.  
  365.     return 1 if $test == test_nt_node;
  366.         
  367.     if ($test == test_any) {
  368.         return 1 if $node->isElementNode && defined $node->getName;
  369.     }
  370.         
  371.     local $^W;
  372.  
  373.     if ($test == test_ncwild) {
  374.         return unless $node->isElementNode;
  375.         my $match_ns = $self->{pp}->get_namespace($self->{literal}, $node);
  376.         if (my $node_nsnode = $node->getNamespace()) {
  377.             return 1 if $match_ns eq $node_nsnode->getValue;
  378.         }
  379.     }
  380.     elsif ($test == test_qname) {
  381.         return unless $node->isElementNode;
  382.         if ($self->{literal} =~ /:/) {
  383.             my ($prefix, $name) = split(':', $self->{literal}, 2);
  384.             my $match_ns = $self->{pp}->get_namespace($prefix, $node);
  385.             if (my $node_nsnode = $node->getNamespace()) {
  386. #                warn "match: '$self->{literal}' match NS: '$match_ns' got NS: '", $node_nsnode->getValue, "'\n";
  387.                 return 1 if ($match_ns eq $node_nsnode->getValue) &&
  388.                         ($name eq $node->getLocalName);
  389.             }
  390.         }
  391.         else {
  392. #            warn "Node test: ", $node->getName, "\n";
  393.             return 1 if $node->getName eq $self->{literal};
  394.         }
  395.     }
  396.     elsif ($test == test_nt_text) {
  397.         return 1 if $node->isTextNode;
  398.     }
  399.     elsif ($test == test_nt_comment) {
  400.         return 1 if $node->isCommentNode;
  401.     }
  402. #     elsif ($test == test_nt_pi && !$self->{literal}) {
  403. #         warn "Unreachable code???";
  404. #         return 1 if $node->isPINode;
  405. #     }
  406.     elsif ($test == test_nt_pi) {
  407.         return unless $node->isPINode;
  408.         if (my $val = $self->{literal}->value) {
  409.             return 1 if $node->getTarget eq $val;
  410.         }
  411.         else {
  412.             return 1;
  413.         }
  414.     }
  415.     
  416.     return; # fallthrough returns false
  417. }
  418.  
  419. sub test_attribute {
  420.     my $self = shift;
  421.     my $node = shift;
  422.     
  423. #    warn "test_attrib: '$self->{test}' against: ", $node->getName, "\n";
  424. #    warn "node type: $node->[node_type]\n";
  425.     
  426.     my $test = $self->{test};
  427.     
  428.     return 1 if ($test == test_attr_any) || ($test == test_nt_node);
  429.         
  430.     if ($test == test_attr_ncwild) {
  431.         my $match_ns = $self->{pp}->get_namespace($self->{literal}, $node);
  432.         if (my $node_nsnode = $node->getNamespace()) {
  433.             return 1 if $match_ns eq $node_nsnode->getValue;
  434.         }
  435.     }
  436.     elsif ($test == test_attr_qname) {
  437.         if ($self->{literal} =~ /:/) {
  438.             my ($prefix, $name) = split(':', $self->{literal}, 2);
  439.             my $match_ns = $self->{pp}->get_namespace($prefix, $node);
  440.             if (my $node_nsnode = $node->getNamespace()) {
  441.                 return 1 if ($match_ns eq $node_nsnode->getValue) &&
  442.                         ($name eq $node->getLocalName);
  443.             }
  444.         }
  445.         else {
  446.             return 1 if $node->getName eq $self->{literal};
  447.         }
  448.     }
  449.     
  450.     return; # fallthrough returns false
  451. }
  452.  
  453. sub test_namespace {
  454.     my $self = shift;
  455.     my $node = shift;
  456.     
  457.     # Not sure if this is correct. The spec seems very unclear on what
  458.     # constitutes a namespace test... bah!
  459.     
  460.     my $test = $self->{test};
  461.     
  462.     return 1 if $test == test_any; # True for all nodes of principal type
  463.     
  464.     if ($test == test_any) {
  465.         return 1;
  466.     }
  467.     elsif ($self->{literal} eq $node->getExpanded) {
  468.         return 1;
  469.     }
  470.     
  471.     return;
  472. }
  473.  
  474. sub filter_by_predicate {
  475.     my $self = shift;
  476.     my ($nodeset, $predicate) = @_;
  477.     
  478.     # See spec section 2.4, paragraphs 2 & 3:
  479.     # For each node in the node-set to be filtered, the predicate Expr
  480.     # is evaluated with that node as the context node, with the number
  481.     # of nodes in the node set as the context size, and with the
  482.     # proximity position of the node in the node set with respect to
  483.     # the axis as the context position.
  484.     
  485.     if (!ref($nodeset)) { # use ref because nodeset has a bool context
  486.         die "No nodeset!!!";
  487.     }
  488.     
  489. #    warn "Filter by predicate: $predicate\n";
  490.     
  491.     my $newset = XML::XPath::NodeSet->new();
  492.     
  493.     for(my $i = 1; $i <= $nodeset->size; $i++) {
  494.         # set context set each time 'cos a loc-path in the expr could change it
  495.         $self->{pp}->set_context_set($nodeset);
  496.         $self->{pp}->set_context_pos($i);
  497.         my $result = $predicate->evaluate($nodeset->get_node($i));
  498.         if ($result->isa('XML::XPath::Boolean')) {
  499.             if ($result->value) {
  500.                 $newset->push($nodeset->get_node($i));
  501.             }
  502.         }
  503.         elsif ($result->isa('XML::XPath::Number')) {
  504.             if ($result->value == $i) {
  505.                 $newset->push($nodeset->get_node($i));
  506.             }
  507.         }
  508.         else {
  509.             if ($result->to_boolean->value) {
  510.                 $newset->push($nodeset->get_node($i));
  511.             }
  512.         }
  513.     }
  514.     
  515.     return $newset;
  516. }
  517.  
  518. 1;
  519.