[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 # $Id: Expr.pm,v 1.20 2003/01/26 19:33:24 matt Exp $ 2 3 package XML::XPath::Expr; 4 use strict; 5 6 sub new { 7 my $class = shift; 8 my ($pp) = @_; 9 bless { predicates => [], pp => $pp }, $class; 10 } 11 12 sub as_string { 13 my $self = shift; 14 local $^W; # Use of uninitialized value! grrr 15 my $string = "(" . $self->{lhs}->as_string; 16 $string .= " " . $self->{op} . " " if defined $self->{op}; 17 $string .= $self->{rhs}->as_string if defined $self->{rhs}; 18 $string .= ")"; 19 foreach my $predicate (@{$self->{predicates}}) { 20 $string .= "[" . $predicate->as_string . "]"; 21 } 22 return $string; 23 } 24 25 sub as_xml { 26 my $self = shift; 27 local $^W; # Use of uninitialized value! grrr 28 my $string; 29 if (defined $self->{op}) { 30 $string .= $self->op_xml(); 31 } 32 else { 33 $string .= $self->{lhs}->as_xml(); 34 } 35 foreach my $predicate (@{$self->{predicates}}) { 36 $string .= "<Predicate>\n" . $predicate->as_xml() . "</Predicate>\n"; 37 } 38 return $string; 39 } 40 41 sub op_xml { 42 my $self = shift; 43 my $op = $self->{op}; 44 45 my $tag; 46 for ($op) { 47 /^or$/ && do { 48 $tag = "Or"; 49 }; 50 /^and$/ && do { 51 $tag = "And"; 52 }; 53 /^=$/ && do { 54 $tag = "Equals"; 55 }; 56 /^!=$/ && do { 57 $tag = "NotEquals"; 58 }; 59 /^<=$/ && do { 60 $tag = "LessThanOrEquals"; 61 }; 62 /^>=$/ && do { 63 $tag = "GreaterThanOrEquals"; 64 }; 65 /^>$/ && do { 66 $tag = "GreaterThan"; 67 }; 68 /^<$/ && do { 69 $tag = "LessThan"; 70 }; 71 /^\+$/ && do { 72 $tag = "Plus"; 73 }; 74 /^-$/ && do { 75 $tag = "Minus"; 76 }; 77 /^div$/ && do { 78 $tag = "Div"; 79 }; 80 /^mod$/ && do { 81 $tag = "Mod"; 82 }; 83 /^\*$/ && do { 84 $tag = "Multiply"; 85 }; 86 /^\|$/ && do { 87 $tag = "Union"; 88 }; 89 } 90 91 return "<$tag>\n" . $self->{lhs}->as_xml() . $self->{rhs}->as_xml() . "</$tag>\n"; 92 } 93 94 sub set_lhs { 95 my $self = shift; 96 $self->{lhs} = $_[0]; 97 } 98 99 sub set_op { 100 my $self = shift; 101 $self->{op} = $_[0]; 102 } 103 104 sub set_rhs { 105 my $self = shift; 106 $self->{rhs} = $_[0]; 107 } 108 109 sub push_predicate { 110 my $self = shift; 111 112 die "Only 1 predicate allowed on FilterExpr in W3C XPath 1.0" 113 if @{$self->{predicates}}; 114 115 push @{$self->{predicates}}, $_[0]; 116 } 117 118 sub get_lhs { $_[0]->{lhs}; } 119 sub get_rhs { $_[0]->{rhs}; } 120 sub get_op { $_[0]->{op}; } 121 122 sub evaluate { 123 my $self = shift; 124 my $node = shift; 125 126 # If there's an op, result is result of that op. 127 # If no op, just resolve Expr 128 129 # warn "Evaluate Expr: ", $self->as_string, "\n"; 130 131 my $results; 132 133 if ($self->{op}) { 134 die ("No RHS of ", $self->as_string) unless $self->{rhs}; 135 $results = $self->op_eval($node); 136 } 137 else { 138 $results = $self->{lhs}->evaluate($node); 139 } 140 141 if (my @predicates = @{$self->{predicates}}) { 142 if (!$results->isa('XML::XPath::NodeSet')) { 143 die "Can't have predicates execute on object type: " . ref($results); 144 } 145 146 # filter initial nodeset by each predicate 147 foreach my $predicate (@{$self->{predicates}}) { 148 $results = $self->filter_by_predicate($results, $predicate); 149 } 150 } 151 152 return $results; 153 } 154 155 sub op_eval { 156 my $self = shift; 157 my $node = shift; 158 159 my $op = $self->{op}; 160 161 for ($op) { 162 /^or$/ && do { 163 return op_or($node, $self->{lhs}, $self->{rhs}); 164 }; 165 /^and$/ && do { 166 return op_and($node, $self->{lhs}, $self->{rhs}); 167 }; 168 /^=$/ && do { 169 return op_equals($node, $self->{lhs}, $self->{rhs}); 170 }; 171 /^!=$/ && do { 172 return op_nequals($node, $self->{lhs}, $self->{rhs}); 173 }; 174 /^<=$/ && do { 175 return op_le($node, $self->{lhs}, $self->{rhs}); 176 }; 177 /^>=$/ && do { 178 return op_ge($node, $self->{lhs}, $self->{rhs}); 179 }; 180 /^>$/ && do { 181 return op_gt($node, $self->{lhs}, $self->{rhs}); 182 }; 183 /^<$/ && do { 184 return op_lt($node, $self->{lhs}, $self->{rhs}); 185 }; 186 /^\+$/ && do { 187 return op_plus($node, $self->{lhs}, $self->{rhs}); 188 }; 189 /^-$/ && do { 190 return op_minus($node, $self->{lhs}, $self->{rhs}); 191 }; 192 /^div$/ && do { 193 return op_div($node, $self->{lhs}, $self->{rhs}); 194 }; 195 /^mod$/ && do { 196 return op_mod($node, $self->{lhs}, $self->{rhs}); 197 }; 198 /^\*$/ && do { 199 return op_mult($node, $self->{lhs}, $self->{rhs}); 200 }; 201 /^\|$/ && do { 202 return op_union($node, $self->{lhs}, $self->{rhs}); 203 }; 204 205 die "No such operator, or operator unimplemented in ", $self->as_string, "\n"; 206 } 207 } 208 209 # Operators 210 211 use XML::XPath::Boolean; 212 213 sub op_or { 214 my ($node, $lhs, $rhs) = @_; 215 if($lhs->evaluate($node)->to_boolean->value) { 216 return XML::XPath::Boolean->True; 217 } 218 else { 219 return $rhs->evaluate($node)->to_boolean; 220 } 221 } 222 223 sub op_and { 224 my ($node, $lhs, $rhs) = @_; 225 if( ! $lhs->evaluate($node)->to_boolean->value ) { 226 return XML::XPath::Boolean->False; 227 } 228 else { 229 return $rhs->evaluate($node)->to_boolean; 230 } 231 } 232 233 sub op_equals { 234 my ($node, $lhs, $rhs) = @_; 235 236 my $lh_results = $lhs->evaluate($node); 237 my $rh_results = $rhs->evaluate($node); 238 239 if ($lh_results->isa('XML::XPath::NodeSet') && 240 $rh_results->isa('XML::XPath::NodeSet')) { 241 # True if and only if there is a node in the 242 # first set and a node in the second set such 243 # that the result of performing the comparison 244 # on the string-values of the two nodes is true. 245 foreach my $lhnode ($lh_results->get_nodelist) { 246 foreach my $rhnode ($rh_results->get_nodelist) { 247 if ($lhnode->string_value eq $rhnode->string_value) { 248 return XML::XPath::Boolean->True; 249 } 250 } 251 } 252 return XML::XPath::Boolean->False; 253 } 254 elsif (($lh_results->isa('XML::XPath::NodeSet') || 255 $rh_results->isa('XML::XPath::NodeSet')) && 256 (!$lh_results->isa('XML::XPath::NodeSet') || 257 !$rh_results->isa('XML::XPath::NodeSet'))) { 258 # (that says: one is a nodeset, and one is not a nodeset) 259 260 my ($nodeset, $other); 261 if ($lh_results->isa('XML::XPath::NodeSet')) { 262 $nodeset = $lh_results; 263 $other = $rh_results; 264 } 265 else { 266 $nodeset = $rh_results; 267 $other = $lh_results; 268 } 269 270 # True if and only if there is a node in the 271 # nodeset such that the result of performing 272 # the comparison on <type>(string_value($node)) 273 # is true. 274 if ($other->isa('XML::XPath::Number')) { 275 foreach my $node ($nodeset->get_nodelist) { 276 if ($node->string_value == $other->value) { 277 return XML::XPath::Boolean->True; 278 } 279 } 280 } 281 elsif ($other->isa('XML::XPath::Literal')) { 282 foreach my $node ($nodeset->get_nodelist) { 283 if ($node->string_value eq $other->value) { 284 return XML::XPath::Boolean->True; 285 } 286 } 287 } 288 elsif ($other->isa('XML::XPath::Boolean')) { 289 if ($nodeset->to_boolean->value == $other->value) { 290 return XML::XPath::Boolean->True; 291 } 292 } 293 294 return XML::XPath::Boolean->False; 295 } 296 else { # Neither is a nodeset 297 if ($lh_results->isa('XML::XPath::Boolean') || 298 $rh_results->isa('XML::XPath::Boolean')) { 299 # if either is a boolean 300 if ($lh_results->to_boolean->value == $rh_results->to_boolean->value) { 301 return XML::XPath::Boolean->True; 302 } 303 return XML::XPath::Boolean->False; 304 } 305 elsif ($lh_results->isa('XML::XPath::Number') || 306 $rh_results->isa('XML::XPath::Number')) { 307 # if either is a number 308 local $^W; # 'number' might result in undef 309 if ($lh_results->to_number->value == $rh_results->to_number->value) { 310 return XML::XPath::Boolean->True; 311 } 312 return XML::XPath::Boolean->False; 313 } 314 else { 315 if ($lh_results->to_literal->value eq $rh_results->to_literal->value) { 316 return XML::XPath::Boolean->True; 317 } 318 return XML::XPath::Boolean->False; 319 } 320 } 321 } 322 323 sub op_nequals { 324 my ($node, $lhs, $rhs) = @_; 325 if (op_equals($node, $lhs, $rhs)->value) { 326 return XML::XPath::Boolean->False; 327 } 328 return XML::XPath::Boolean->True; 329 } 330 331 sub op_le { 332 my ($node, $lhs, $rhs) = @_; 333 op_gt($node, $rhs, $lhs); 334 } 335 336 sub op_ge { 337 my ($node, $lhs, $rhs) = @_; 338 339 my $lh_results = $lhs->evaluate($node); 340 my $rh_results = $rhs->evaluate($node); 341 342 if ($lh_results->isa('XML::XPath::NodeSet') && 343 $rh_results->isa('XML::XPath::NodeSet')) { 344 345 foreach my $lhnode ($lh_results->get_nodelist) { 346 foreach my $rhnode ($rh_results->get_nodelist) { 347 my $lhNum = XML::XPath::Number->new($lhnode->string_value); 348 my $rhNum = XML::XPath::Number->new($rhnode->string_value); 349 if ($lhNum->value >= $rhNum->value) { 350 return XML::XPath::Boolean->True; 351 } 352 } 353 } 354 return XML::XPath::Boolean->False; 355 } 356 elsif (($lh_results->isa('XML::XPath::NodeSet') || 357 $rh_results->isa('XML::XPath::NodeSet')) && 358 (!$lh_results->isa('XML::XPath::NodeSet') || 359 !$rh_results->isa('XML::XPath::NodeSet'))) { 360 # (that says: one is a nodeset, and one is not a nodeset) 361 362 my ($nodeset, $other); 363 my ($true, $false); 364 if ($lh_results->isa('XML::XPath::NodeSet')) { 365 $nodeset = $lh_results; 366 $other = $rh_results; 367 # we do this because unlike ==, these ops are direction dependant 368 ($false, $true) = (XML::XPath::Boolean->False, XML::XPath::Boolean->True); 369 } 370 else { 371 $nodeset = $rh_results; 372 $other = $lh_results; 373 # ditto above comment 374 ($true, $false) = (XML::XPath::Boolean->False, XML::XPath::Boolean->True); 375 } 376 377 # True if and only if there is a node in the 378 # nodeset such that the result of performing 379 # the comparison on <type>(string_value($node)) 380 # is true. 381 foreach my $node ($nodeset->get_nodelist) { 382 if ($node->to_number->value >= $other->to_number->value) { 383 return $true; 384 } 385 } 386 return $false; 387 } 388 else { # Neither is a nodeset 389 if ($lh_results->isa('XML::XPath::Boolean') || 390 $rh_results->isa('XML::XPath::Boolean')) { 391 # if either is a boolean 392 if ($lh_results->to_boolean->to_number->value 393 >= $rh_results->to_boolean->to_number->value) { 394 return XML::XPath::Boolean->True; 395 } 396 } 397 else { 398 if ($lh_results->to_number->value >= $rh_results->to_number->value) { 399 return XML::XPath::Boolean->True; 400 } 401 } 402 return XML::XPath::Boolean->False; 403 } 404 } 405 406 sub op_gt { 407 my ($node, $lhs, $rhs) = @_; 408 409 my $lh_results = $lhs->evaluate($node); 410 my $rh_results = $rhs->evaluate($node); 411 412 if ($lh_results->isa('XML::XPath::NodeSet') && 413 $rh_results->isa('XML::XPath::NodeSet')) { 414 415 foreach my $lhnode ($lh_results->get_nodelist) { 416 foreach my $rhnode ($rh_results->get_nodelist) { 417 my $lhNum = XML::XPath::Number->new($lhnode->string_value); 418 my $rhNum = XML::XPath::Number->new($rhnode->string_value); 419 if ($lhNum->value > $rhNum->value) { 420 return XML::XPath::Boolean->True; 421 } 422 } 423 } 424 return XML::XPath::Boolean->False; 425 } 426 elsif (($lh_results->isa('XML::XPath::NodeSet') || 427 $rh_results->isa('XML::XPath::NodeSet')) && 428 (!$lh_results->isa('XML::XPath::NodeSet') || 429 !$rh_results->isa('XML::XPath::NodeSet'))) { 430 # (that says: one is a nodeset, and one is not a nodeset) 431 432 my ($nodeset, $other); 433 my ($true, $false); 434 if ($lh_results->isa('XML::XPath::NodeSet')) { 435 $nodeset = $lh_results; 436 $other = $rh_results; 437 # we do this because unlike ==, these ops are direction dependant 438 ($false, $true) = (XML::XPath::Boolean->False, XML::XPath::Boolean->True); 439 } 440 else { 441 $nodeset = $rh_results; 442 $other = $lh_results; 443 # ditto above comment 444 ($true, $false) = (XML::XPath::Boolean->False, XML::XPath::Boolean->True); 445 } 446 447 # True if and only if there is a node in the 448 # nodeset such that the result of performing 449 # the comparison on <type>(string_value($node)) 450 # is true. 451 foreach my $node ($nodeset->get_nodelist) { 452 if ($node->to_number->value > $other->to_number->value) { 453 return $true; 454 } 455 } 456 return $false; 457 } 458 else { # Neither is a nodeset 459 if ($lh_results->isa('XML::XPath::Boolean') || 460 $rh_results->isa('XML::XPath::Boolean')) { 461 # if either is a boolean 462 if ($lh_results->to_boolean->value > $rh_results->to_boolean->value) { 463 return XML::XPath::Boolean->True; 464 } 465 } 466 else { 467 if ($lh_results->to_number->value > $rh_results->to_number->value) { 468 return XML::XPath::Boolean->True; 469 } 470 } 471 return XML::XPath::Boolean->False; 472 } 473 } 474 475 sub op_lt { 476 my ($node, $lhs, $rhs) = @_; 477 op_gt($node, $rhs, $lhs); 478 } 479 480 sub op_plus { 481 my ($node, $lhs, $rhs) = @_; 482 my $lh_results = $lhs->evaluate($node); 483 my $rh_results = $rhs->evaluate($node); 484 485 my $result = 486 $lh_results->to_number->value 487 + 488 $rh_results->to_number->value 489 ; 490 return XML::XPath::Number->new($result); 491 } 492 493 sub op_minus { 494 my ($node, $lhs, $rhs) = @_; 495 my $lh_results = $lhs->evaluate($node); 496 my $rh_results = $rhs->evaluate($node); 497 498 my $result = 499 $lh_results->to_number->value 500 - 501 $rh_results->to_number->value 502 ; 503 return XML::XPath::Number->new($result); 504 } 505 506 sub op_div { 507 my ($node, $lhs, $rhs) = @_; 508 my $lh_results = $lhs->evaluate($node); 509 my $rh_results = $rhs->evaluate($node); 510 511 my $result = eval { 512 $lh_results->to_number->value 513 / 514 $rh_results->to_number->value 515 ; 516 }; 517 if ($@) { 518 # assume divide by zero 519 # This is probably a terrible way to handle this! 520 # Ah well... who wants to live forever... 521 return XML::XPath::Literal->new('Infinity'); 522 } 523 return XML::XPath::Number->new($result); 524 } 525 526 sub op_mod { 527 my ($node, $lhs, $rhs) = @_; 528 my $lh_results = $lhs->evaluate($node); 529 my $rh_results = $rhs->evaluate($node); 530 531 my $result = 532 $lh_results->to_number->value 533 % 534 $rh_results->to_number->value 535 ; 536 return XML::XPath::Number->new($result); 537 } 538 539 sub op_mult { 540 my ($node, $lhs, $rhs) = @_; 541 my $lh_results = $lhs->evaluate($node); 542 my $rh_results = $rhs->evaluate($node); 543 544 my $result = 545 $lh_results->to_number->value 546 * 547 $rh_results->to_number->value 548 ; 549 return XML::XPath::Number->new($result); 550 } 551 552 sub op_union { 553 my ($node, $lhs, $rhs) = @_; 554 my $lh_result = $lhs->evaluate($node); 555 my $rh_result = $rhs->evaluate($node); 556 557 if ($lh_result->isa('XML::XPath::NodeSet') && 558 $rh_result->isa('XML::XPath::NodeSet')) { 559 my %found; 560 my $results = XML::XPath::NodeSet->new; 561 foreach my $lhnode ($lh_result->get_nodelist) { 562 $found{"$lhnode"}++; 563 $results->push($lhnode); 564 } 565 foreach my $rhnode ($rh_result->get_nodelist) { 566 $results->push($rhnode) 567 unless exists $found{"$rhnode"}; 568 } 569 $results->sort; 570 return $results; 571 } 572 die "Both sides of a union must be Node Sets\n"; 573 } 574 575 sub filter_by_predicate { 576 my $self = shift; 577 my ($nodeset, $predicate) = @_; 578 579 # See spec section 2.4, paragraphs 2 & 3: 580 # For each node in the node-set to be filtered, the predicate Expr 581 # is evaluated with that node as the context node, with the number 582 # of nodes in the node set as the context size, and with the 583 # proximity position of the node in the node set with respect to 584 # the axis as the context position. 585 586 if (!ref($nodeset)) { # use ref because nodeset has a bool context 587 die "No nodeset!!!"; 588 } 589 590 # warn "Filter by predicate: $predicate\n"; 591 592 my $newset = XML::XPath::NodeSet->new(); 593 594 for(my $i = 1; $i <= $nodeset->size; $i++) { 595 # set context set each time 'cos a loc-path in the expr could change it 596 $self->{pp}->set_context_set($nodeset); 597 $self->{pp}->set_context_pos($i); 598 my $result = $predicate->evaluate($nodeset->get_node($i)); 599 if ($result->isa('XML::XPath::Boolean')) { 600 if ($result->value) { 601 $newset->push($nodeset->get_node($i)); 602 } 603 } 604 elsif ($result->isa('XML::XPath::Number')) { 605 if ($result->value == $i) { 606 $newset->push($nodeset->get_node($i)); 607 } 608 } 609 else { 610 if ($result->to_boolean->value) { 611 $newset->push($nodeset->get_node($i)); 612 } 613 } 614 } 615 616 return $newset; 617 } 618 619 1;
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 17 22:47:18 2015 | Cross-referenced by PHPXref 0.7.1 |