| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # RDF::Query::Expression::Binary | 
| 2 |  |  |  |  |  |  | # ----------------------------------------------------------------------------- | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | =head1 NAME | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | RDF::Query::Expression::Binary - Algebra class for binary expressions | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | =head1 VERSION | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | This document describes RDF::Query::Expression::Binary version 2.916. | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | =cut | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | package RDF::Query::Expression::Binary; | 
| 15 |  |  |  |  |  |  |  | 
| 16 | 36 |  |  | 36 |  | 177 | use strict; | 
|  | 36 |  |  |  |  | 62 |  | 
|  | 36 |  |  |  |  | 893 |  | 
| 17 | 36 |  |  | 36 |  | 171 | use warnings; | 
|  | 36 |  |  |  |  | 68 |  | 
|  | 36 |  |  |  |  | 892 |  | 
| 18 | 36 |  |  | 36 |  | 178 | no warnings 'redefine'; | 
|  | 36 |  |  |  |  | 62 |  | 
|  | 36 |  |  |  |  | 1097 |  | 
| 19 | 36 |  |  | 36 |  | 181 | use base qw(RDF::Query::Expression); | 
|  | 36 |  |  |  |  | 60 |  | 
|  | 36 |  |  |  |  | 2444 |  | 
| 20 |  |  |  |  |  |  |  | 
| 21 | 36 |  |  | 36 |  | 187 | use Data::Dumper; | 
|  | 36 |  |  |  |  | 67 |  | 
|  | 36 |  |  |  |  | 1634 |  | 
| 22 | 36 |  |  | 36 |  | 198 | use Log::Log4perl; | 
|  | 36 |  |  |  |  | 64 |  | 
|  | 36 |  |  |  |  | 320 |  | 
| 23 | 36 |  |  | 36 |  | 1807 | use Scalar::Util qw(blessed); | 
|  | 36 |  |  |  |  | 84 |  | 
|  | 36 |  |  |  |  | 1740 |  | 
| 24 | 36 |  |  | 36 |  | 174 | use Carp qw(carp croak confess); | 
|  | 36 |  |  |  |  | 74 |  | 
|  | 36 |  |  |  |  | 2697 |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | ###################################################################### | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | our ($VERSION); | 
| 29 |  |  |  |  |  |  | BEGIN { | 
| 30 | 36 |  |  | 36 |  | 48546 | $VERSION	= '2.916'; | 
| 31 |  |  |  |  |  |  | } | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | ###################################################################### | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | =head1 METHODS | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | Beyond the methods documented below, this class inherits methods from the | 
| 38 |  |  |  |  |  |  | L<RDF::Query::Expression> class. | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | =over 4 | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | =cut | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | =item C<< sse >> | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | Returns the SSE string for this algebra expression. | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | =cut | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | sub sse { | 
| 51 | 60 |  |  | 60 | 1 | 102 | my $self	= shift; | 
| 52 | 60 |  |  |  |  | 86 | my $context	= shift; | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | return sprintf( | 
| 55 |  |  |  |  |  |  | '(%s %s %s)', | 
| 56 |  |  |  |  |  |  | $self->op, | 
| 57 | 60 |  |  |  |  | 201 | map { $_->sse( $context ) } $self->operands, | 
|  | 120 |  |  |  |  | 1505 |  | 
| 58 |  |  |  |  |  |  | ); | 
| 59 |  |  |  |  |  |  | } | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | =item C<< as_sparql >> | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | Returns the SPARQL string for this algebra expression. | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | =cut | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | sub as_sparql { | 
| 68 | 3 |  |  | 3 | 1 | 7 | my $self	= shift; | 
| 69 | 3 |  |  |  |  | 5 | my $context	= shift; | 
| 70 | 3 |  |  |  |  | 8 | my $indent	= shift; | 
| 71 | 3 |  |  |  |  | 9 | my $op		= $self->op; | 
| 72 | 3 | 100 |  |  |  | 11 | $op			= '=' if ($op eq '=='); | 
| 73 | 3 |  |  |  |  | 13 | return sprintf("(%s $op %s)", map { $_->as_sparql( $context, $indent ) } $self->operands); | 
|  | 6 |  |  |  |  | 41 |  | 
| 74 |  |  |  |  |  |  | } | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | =item C<< evaluate ( $query, \%bound ) >> | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | Evaluates the expression using the supplied bound variables. | 
| 79 |  |  |  |  |  |  | Will return a RDF::Query::Node object. | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | =cut | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | sub evaluate { | 
| 84 | 93 |  |  | 93 | 1 | 185 | my $self	= shift; | 
| 85 | 93 |  |  |  |  | 131 | my $query	= shift; | 
| 86 | 93 |  |  |  |  | 129 | my $bound	= shift; | 
| 87 | 93 |  |  |  |  | 346 | my $l		= Log::Log4perl->get_logger("rdf.query.expression.binary"); | 
| 88 | 93 |  |  |  |  | 4767 | my $op		= $self->op; | 
| 89 | 93 |  |  |  |  | 288 | my @operands	= $self->operands; | 
| 90 |  |  |  |  |  |  | my ($lhs, $rhs)	= map { | 
| 91 | 93 | 100 |  |  |  | 205 | throw RDF::Query::Error::ExecutionError ( -text => "error in evaluating operands to binary $op" ) | 
|  | 186 |  |  |  |  | 857 |  | 
| 92 |  |  |  |  |  |  | unless (blessed($_)); | 
| 93 |  |  |  |  |  |  | $_->isa('RDF::Query::Algebra') | 
| 94 |  |  |  |  |  |  | ? $_->evaluate( $query, $bound, @_ ) | 
| 95 |  |  |  |  |  |  | : ($_->isa('RDF::Trine::Node::Variable')) | 
| 96 | 185 | 100 |  |  |  | 1559 | ? $bound->{ $_->name } | 
|  |  | 100 |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | : $_ | 
| 98 |  |  |  |  |  |  | } @operands; | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 92 |  |  |  |  | 521 | $l->debug("Binary Operator '$op': " . Dumper($lhs, $rhs)); | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | ### This does overloading of infix<+> on literal values to perform string concatenation | 
| 103 |  |  |  |  |  |  | # 	if ($op eq '+') { | 
| 104 |  |  |  |  |  |  | # 		if (blessed($lhs) and $lhs->isa('RDF::Query::Node::Literal') and blessed($rhs) and $rhs->isa('RDF::Query::Node::Literal')) { | 
| 105 |  |  |  |  |  |  | # 			if (not($lhs->has_datatype) and not($rhs->has_datatype)) { | 
| 106 |  |  |  |  |  |  | # 				my $value	= $lhs->literal_value . $rhs->literal_value; | 
| 107 |  |  |  |  |  |  | # 				return RDF::Query::Node::Literal->new( $value ); | 
| 108 |  |  |  |  |  |  | # 			} | 
| 109 |  |  |  |  |  |  | # 		} | 
| 110 |  |  |  |  |  |  | # 	} | 
| 111 |  |  |  |  |  |  |  | 
| 112 | 92 | 100 |  |  |  | 8871 | if ($op =~ m#^[-+/*]$#) { | 
|  |  | 50 |  |  |  |  |  | 
| 113 | 29 | 50 | 33 |  |  | 522 | if (blessed($lhs) and blessed($rhs) and $lhs->isa('RDF::Query::Node::Literal') and $rhs->isa('RDF::Query::Node::Literal') and $lhs->is_numeric_type and $rhs->is_numeric_type) { | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 114 | 29 |  |  |  |  | 88 | my $type	= $self->promote_type( $op, $lhs->literal_datatype, $rhs->literal_datatype ); | 
| 115 | 29 |  |  |  |  | 46 | my $value; | 
| 116 | 29 | 100 |  |  |  | 116 | if ($op eq '+') { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 117 | 4 |  |  |  |  | 18 | my $lhsv	= $lhs->numeric_value; | 
| 118 | 4 |  |  |  |  | 20 | my $rhsv	= $rhs->numeric_value; | 
| 119 | 4 | 50 | 33 |  |  | 31 | if (defined($lhsv) and defined($rhsv)) { | 
| 120 | 4 |  |  |  |  | 10 | $value		= $lhsv + $rhsv; | 
| 121 |  |  |  |  |  |  | } else { | 
| 122 | 0 |  |  |  |  | 0 | throw RDF::Query::Error::ComparisonError -text => "Cannot evaluate infix:<+> on non-numeric types"; | 
| 123 |  |  |  |  |  |  | } | 
| 124 |  |  |  |  |  |  | } elsif ($op eq '-') { | 
| 125 | 3 |  |  |  |  | 13 | my $lhsv	= $lhs->numeric_value; | 
| 126 | 3 |  |  |  |  | 12 | my $rhsv	= $rhs->numeric_value; | 
| 127 | 3 | 50 | 33 |  |  | 23 | if (defined($lhsv) and defined($rhsv)) { | 
| 128 | 3 |  |  |  |  | 7 | $value		= $lhsv - $rhsv; | 
| 129 |  |  |  |  |  |  | } else { | 
| 130 | 0 |  |  |  |  | 0 | throw RDF::Query::Error::ComparisonError -text => "Cannot evaluate infix:<-> on non-numeric types"; | 
| 131 |  |  |  |  |  |  | } | 
| 132 |  |  |  |  |  |  | } elsif ($op eq '*') { | 
| 133 | 20 |  |  |  |  | 67 | my $lhsv	= $lhs->numeric_value; | 
| 134 | 20 |  |  |  |  | 78 | my $rhsv	= $rhs->numeric_value; | 
| 135 | 20 | 50 | 33 |  |  | 116 | if (defined($lhsv) and defined($rhsv)) { | 
| 136 | 20 |  |  |  |  | 41 | $value		= $lhsv * $rhsv; | 
| 137 |  |  |  |  |  |  | } else { | 
| 138 | 0 |  |  |  |  | 0 | throw RDF::Query::Error::ComparisonError -text => "Cannot evaluate infix:<*> on non-numeric types"; | 
| 139 |  |  |  |  |  |  | } | 
| 140 |  |  |  |  |  |  | } elsif ($op eq '/') { | 
| 141 | 2 |  |  |  |  | 24 | my $lhsv	= $lhs->numeric_value; | 
| 142 | 2 |  |  |  |  | 11 | my $rhsv	= $rhs->numeric_value; | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 2 |  |  |  |  | 11 | my ($lt, $rt)	= ($lhs->literal_datatype, $rhs->literal_datatype); | 
| 145 | 2 | 100 | 66 |  |  | 27 | if ($lt eq $rt and $lt eq 'http://www.w3.org/2001/XMLSchema#integer') { | 
| 146 | 1 |  |  |  |  | 2 | $type	= 'http://www.w3.org/2001/XMLSchema#decimal'; | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 2 | 50 | 33 |  |  | 16 | if (defined($lhsv) and defined($rhsv)) { | 
| 150 | 2 | 100 |  |  |  | 9 | if ($rhsv == 0) { | 
| 151 | 1 |  |  |  |  | 17 | throw RDF::Query::Error::FilterEvaluationError -text => "Illegal division by zero"; | 
| 152 |  |  |  |  |  |  | } | 
| 153 | 1 |  |  |  |  | 3 | $value		= $lhsv / $rhsv; | 
| 154 |  |  |  |  |  |  | } else { | 
| 155 | 0 |  |  |  |  | 0 | throw RDF::Query::Error::ComparisonError -text => "Cannot evaluate infix:</> on non-numeric types"; | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  | } else { | 
| 158 | 0 |  |  |  |  | 0 | throw RDF::Query::Error::ExecutionError -text => "Unrecognized binary operator '$op'"; | 
| 159 |  |  |  |  |  |  | } | 
| 160 | 28 |  |  |  |  | 103 | return RDF::Query::Node::Literal->new( $value, undef, $type, 1 ); | 
| 161 |  |  |  |  |  |  | } else { | 
| 162 | 0 |  |  |  |  | 0 | throw RDF::Query::Error::ExecutionError -text => "Numeric binary operator '$op' with non-numeric data"; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  | } elsif ($op =~ m#^([<>]=?)|!?=$#) { | 
| 165 | 63 |  |  |  |  | 191 | my @types	= qw(RDF::Query::Node::Literal RDF::Query::Node::Resource RDF::Query::Node::Blank); | 
| 166 |  |  |  |  |  |  |  | 
| 167 | 63 | 100 |  |  |  | 234 | if ($op =~ /[<>]/) { | 
| 168 |  |  |  |  |  |  | # if it's a relational operation other than equality testing, | 
| 169 |  |  |  |  |  |  | # the two nodes must be of the same type. | 
| 170 | 27 |  |  |  |  | 54 | my $ok		= 0; | 
| 171 | 27 |  |  |  |  | 59 | foreach my $type (@types) { | 
| 172 | 81 | 100 | 50 |  |  | 614 | $ok	||= 1 if ($lhs->isa($type) and $rhs->isa($type)); | 
|  |  |  | 100 |  |  |  |  | 
| 173 |  |  |  |  |  |  | } | 
| 174 | 27 | 50 | 66 |  |  | 96 | if (not($ok) and not($RDF::Query::Node::Literal::LAZY_COMPARISONS)) { | 
| 175 | 0 |  |  |  |  | 0 | throw RDF::Query::Error::TypeError -text => "Attempt to compare two nodes of different types."; | 
| 176 |  |  |  |  |  |  | } | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 | 63 |  |  |  |  | 88 | my $bool; | 
| 180 | 63 | 100 |  |  |  | 334 | if ($op eq '<') { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 181 | 14 |  |  |  |  | 183 | $bool	= ($lhs < $rhs); | 
| 182 |  |  |  |  |  |  | } elsif ($op eq '<=') { | 
| 183 | 1 |  |  |  |  | 9 | $bool	= ($lhs <= $rhs); | 
| 184 |  |  |  |  |  |  | } elsif ($op eq '>') { | 
| 185 | 12 |  |  |  |  | 58 | $bool	= ($lhs > $rhs); | 
| 186 |  |  |  |  |  |  | } elsif ($op eq '>=') { | 
| 187 | 0 |  |  |  |  | 0 | $bool	= ($lhs >= $rhs); | 
| 188 |  |  |  |  |  |  | } elsif ($op eq '==') { | 
| 189 | 36 |  |  |  |  | 165 | $bool	= ($lhs == $rhs); | 
| 190 |  |  |  |  |  |  | } elsif ($op eq '!=') { | 
| 191 | 0 |  |  |  |  | 0 | $bool	= ($lhs != $rhs); | 
| 192 |  |  |  |  |  |  | } else { | 
| 193 | 0 |  |  |  |  | 0 | throw RDF::Query::Error::ExecutionError -text => "Unrecognized binary operator '$op'"; | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  |  | 
| 196 | 63 | 100 |  |  |  | 347 | my $value	= ($bool) ? 'true' : 'false'; | 
| 197 | 63 |  |  |  |  | 260 | $l->debug("-> $value"); | 
| 198 | 63 |  |  |  |  | 593 | return RDF::Query::Node::Literal->new( $value, undef, 'http://www.w3.org/2001/XMLSchema#boolean' ); | 
| 199 |  |  |  |  |  |  | } else { | 
| 200 | 0 |  |  |  |  | 0 | $l->logdie("Unknown operator: $op"); | 
| 201 |  |  |  |  |  |  | } | 
| 202 |  |  |  |  |  |  | } | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | my $xsd				= 'http://www.w3.org/2001/XMLSchema#'; | 
| 205 |  |  |  |  |  |  | my %integer_types	= map { join('', $xsd, $_) => 1 } qw(nonPositiveInteger nonNegativeInteger positiveInteger negativeInteger short unsignedShort byte unsignedByte long unsignedLong); | 
| 206 |  |  |  |  |  |  | my %rel	= ( | 
| 207 |  |  |  |  |  |  | "${xsd}integer"				=> 0, | 
| 208 |  |  |  |  |  |  | "${xsd}int"					=> 1, | 
| 209 |  |  |  |  |  |  | "${xsd}unsignedInt"			=> 2, | 
| 210 |  |  |  |  |  |  | "${xsd}nonPositiveInteger"	=> 3, | 
| 211 |  |  |  |  |  |  | "${xsd}nonNegativeInteger"	=> 4, | 
| 212 |  |  |  |  |  |  | "${xsd}positiveInteger"		=> 5, | 
| 213 |  |  |  |  |  |  | "${xsd}negativeInteger"		=> 6, | 
| 214 |  |  |  |  |  |  | "${xsd}short"				=> 7, | 
| 215 |  |  |  |  |  |  | "${xsd}unsignedShort"		=> 8, | 
| 216 |  |  |  |  |  |  | "${xsd}byte"				=> 9, | 
| 217 |  |  |  |  |  |  | "${xsd}unsignedByte"		=> 10, | 
| 218 |  |  |  |  |  |  | "${xsd}long"				=> 11, | 
| 219 |  |  |  |  |  |  | "${xsd}unsignedLong"		=> 12, | 
| 220 |  |  |  |  |  |  | "${xsd}decimal"				=> 13, | 
| 221 |  |  |  |  |  |  | "${xsd}float"				=> 14, | 
| 222 |  |  |  |  |  |  | "${xsd}double"				=> 15, | 
| 223 |  |  |  |  |  |  | ); | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | =item C<< promote_type ( $op, $lhs_datatype, $rhs_datatype ) >> | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | Returns the XSD type URI (as a string) for the resulting value of performing the | 
| 228 |  |  |  |  |  |  | supplied operation on arguments of the indicated XSD types. | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | =cut | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | sub promote_type { | 
| 233 | 36 |  |  | 36 | 1 | 225 | my $self	= shift; | 
| 234 | 36 |  |  |  |  | 56 | my $op		= shift; | 
| 235 | 36 |  |  | 36 |  | 215 | no warnings 'uninitialized'; | 
|  | 36 |  |  |  |  | 76 |  | 
|  | 36 |  |  |  |  | 4581 |  | 
| 236 | 36 |  |  |  |  | 111 | my @types	= sort { $rel{$b} <=> $rel{$a} } @_; | 
|  | 36 |  |  |  |  | 178 |  | 
| 237 |  |  |  |  |  |  |  | 
| 238 | 36 |  |  |  |  | 60 | my $type	= $types[0]; | 
| 239 | 36 | 50 |  |  |  | 107 | $type		= "${xsd}integer" if ($integer_types{ $type }); | 
| 240 | 36 |  |  |  |  | 115 | return $type; | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | 1; | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | __END__ | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | =back | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | =head1 AUTHOR | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | Gregory Todd Williams <gwilliams@cpan.org> | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | =cut |