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.915_01.
11              
12             =cut
13              
14             package RDF::Query::Expression::Binary;
15              
16 36     36   168 use strict;
  36         65  
  36         931  
17 36     36   173 use warnings;
  36         55  
  36         896  
18 36     36   176 no warnings 'redefine';
  36         53  
  36         1081  
19 36     36   166 use base qw(RDF::Query::Expression);
  36         53  
  36         2395  
20              
21 36     36   183 use Data::Dumper;
  36         64  
  36         1711  
22 36     36   191 use Log::Log4perl;
  36         70  
  36         369  
23 36     36   1753 use Scalar::Util qw(blessed);
  36         64  
  36         1693  
24 36     36   183 use Carp qw(carp croak confess);
  36         65  
  36         2570  
25              
26             ######################################################################
27              
28             our ($VERSION);
29             BEGIN {
30 36     36   47576 $VERSION = '2.915_01';
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 87 my $self = shift;
52 60         79 my $context = shift;
53            
54             return sprintf(
55             '(%s %s %s)',
56             $self->op,
57 60         198 map { $_->sse( $context ) } $self->operands,
  120         1359  
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 8 my $self = shift;
69 3         5 my $context = shift;
70 3         5 my $indent = shift;
71 3         10 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         39  
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 91     91 1 171 my $self = shift;
85 91         128 my $query = shift;
86 91         127 my $bound = shift;
87 91         299 my $l = Log::Log4perl->get_logger("rdf.query.expression.binary");
88 91         4422 my $op = $self->op;
89 91         285 my @operands = $self->operands;
90             my ($lhs, $rhs) = map {
91 91 100       195 throw RDF::Query::Error::ExecutionError ( -text => "error in evaluating operands to binary $op" )
  182         798  
92             unless (blessed($_));
93             $_->isa('RDF::Query::Algebra')
94             ? $_->evaluate( $query, $bound, @_ )
95             : ($_->isa('RDF::Trine::Node::Variable'))
96 181 100       1442 ? $bound->{ $_->name }
    100          
97             : $_
98             } @operands;
99            
100 90         422 $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 90 100       8038 if ($op =~ m#^[-+/*]$#) {
    50          
113 29 50 33     512 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         87 my $type = $self->promote_type( $op, $lhs->literal_datatype, $rhs->literal_datatype );
115 29         42 my $value;
116 29 100       122 if ($op eq '+') {
    100          
    100          
    50          
117 4         16 my $lhsv = $lhs->numeric_value;
118 4         88 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         10 my $lhsv = $lhs->numeric_value;
126 3         14 my $rhsv = $rhs->numeric_value;
127 3 50 33     22 if (defined($lhsv) and defined($rhsv)) {
128 3         5 $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         63 my $lhsv = $lhs->numeric_value;
134 20         79 my $rhsv = $rhs->numeric_value;
135 20 50 33     114 if (defined($lhsv) and defined($rhsv)) {
136 20         37 $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         12 my $lhsv = $lhs->numeric_value;
142 2         11 my $rhsv = $rhs->numeric_value;
143            
144 2         12 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         3 $type = 'http://www.w3.org/2001/XMLSchema#decimal';
147             }
148            
149 2 50 33     17 if (defined($lhsv) and defined($rhsv)) {
150 2 100       8 if ($rhsv == 0) {
151 1         21 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         104 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 61         165 my @types = qw(RDF::Query::Node::Literal RDF::Query::Node::Resource RDF::Query::Node::Blank);
166            
167 61 100       215 if ($op =~ /[<>]/) {
168             # if it's a relational operation other than equality testing,
169             # the two nodes must be of the same type.
170 25         40 my $ok = 0;
171 25         52 foreach my $type (@types) {
172 75 100 50     539 $ok ||= 1 if ($lhs->isa($type) and $rhs->isa($type));
      100        
173             }
174 25 50 66     84 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 61         90 my $bool;
180 61 100       301 if ($op eq '<') {
    100          
    100          
    50          
    50          
    0          
181 14         176 $bool = ($lhs < $rhs);
182             } elsif ($op eq '<=') {
183 1         5 $bool = ($lhs <= $rhs);
184             } elsif ($op eq '>') {
185 10         47 $bool = ($lhs > $rhs);
186             } elsif ($op eq '>=') {
187 0         0 $bool = ($lhs >= $rhs);
188             } elsif ($op eq '==') {
189 36         151 $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 61 100       297 my $value = ($bool) ? 'true' : 'false';
197 61         253 $l->debug("-> $value");
198 61         542 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 204 my $self = shift;
234 36         55 my $op = shift;
235 36     36   221 no warnings 'uninitialized';
  36         82  
  36         4711  
236 36         117 my @types = sort { $rel{$b} <=> $rel{$a} } @_;
  36         173  
237            
238 36         56 my $type = $types[0];
239 36 50       114 $type = "${xsd}integer" if ($integer_types{ $type });
240 36         118 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