File Coverage

blib/lib/RDF/Query/Expression/Binary.pm
Criterion Covered Total %
statement 98 109 89.9
branch 43 56 76.7
condition 17 38 44.7
subroutine 14 14 100.0
pod 4 4 100.0
total 176 221 79.6


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