File Coverage

blib/lib/RDF/Query/Expression/Function.pm
Criterion Covered Total %
statement 93 137 67.8
branch 33 52 63.4
condition 14 25 56.0
subroutine 17 21 80.9
pod 8 8 100.0
total 165 243 67.9


line stmt bran cond sub pod time code
1             # RDF::Query::Expression::Function
2             # -----------------------------------------------------------------------------
3              
4             =head1 NAME
5              
6             RDF::Query::Expression::Function - Class for Function expressions
7              
8             =head1 VERSION
9              
10             This document describes RDF::Query::Expression::Function version 2.915_01.
11              
12             =cut
13              
14             package RDF::Query::Expression::Function;
15              
16 36     36   176 use strict;
  36         67  
  36         987  
17 36     36   176 use warnings;
  36         76  
  36         957  
18 36     36   171 no warnings 'redefine';
  36         59  
  36         1132  
19 36     36   187 use base qw(RDF::Query::Expression);
  36         78  
  36         2659  
20              
21 36     36   18062 use RDF::Query::Error qw(:try);
  36         101  
  36         317  
22 36     36   6173 use Data::Dumper;
  36         67  
  36         1610  
23 36     36   178 use Scalar::Util qw(blessed reftype);
  36         58  
  36         1838  
24 36     36   170 use Carp qw(carp croak confess);
  36         66  
  36         2515  
25              
26             ######################################################################
27              
28             our ($VERSION);
29             BEGIN {
30 36     36   39236 $VERSION = '2.915_01';
31             }
32              
33             ######################################################################
34              
35             our %FUNCTION_MAP = (
36             str => "STR",
37             strdt => "STRDT",
38             strlang => "STRLANG",
39             lang => "LANG",
40             langmatches => "LANGMATCHES",
41             sameterm => "sameTerm",
42             datatype => "DATATYPE",
43             bound => "BOUND",
44             isuri => "isURI",
45             isiri => "isIRI",
46             isblank => "isBlank",
47             isliteral => "isLiteral",
48             regex => "REGEX",
49             iri => "IRI",
50             uri => "IRI",
51             bnode => "BNODE",
52             in => "IN",
53             notin => "NOT IN",
54             if => "IF",
55             'logical-or' => "||",
56             'logical-and' => "&&",
57             );
58              
59             =head1 METHODS
60              
61             Beyond the methods documented below, this class inherits methods from the
62             L<RDF::Query::Expression> class.
63              
64             =over 4
65              
66             =cut
67              
68             =item C<new ( $uri, @arguments )>
69              
70             Returns a new Expr structure.
71              
72             =cut
73              
74             sub new {
75 220     220 1 10649 my $class = shift;
76 220         352 my $uri = shift;
77 220         462 my @args = @_;
78 220 100 66     1631 unless (blessed($uri) and $uri->isa('RDF::Trine::Node::Resource')) {
79 85         328 $uri = RDF::Query::Node::Resource->new( $uri );
80             }
81 220         2242 return $class->SUPER::new( $uri, @args );
82             }
83              
84             =item C<< uri >>
85              
86             Returns the URI of the function.
87              
88             =cut
89              
90             sub uri {
91 610     610 1 806 my $self = shift;
92 610         1806 return $self->op;
93             }
94              
95             =item C<< arguments >>
96              
97             Returns a list of the arguments to the function.
98              
99             =cut
100              
101             sub arguments {
102 506     506 1 14765 my $self = shift;
103 506         1440 return $self->operands;
104             }
105              
106             =item C<< sse >>
107              
108             Returns the SSE string for this algebra expression.
109              
110             =cut
111              
112             sub sse {
113 148     148 1 212 my $self = shift;
114 148         202 my $context = shift;
115            
116 148         335 my $uri = $self->uri->uri_value;
117 148 100       1592 if ($uri =~ m/^(sop|sparql):(in|notin|str|strdt|strlang|if|iri|uri|bnode|lang|langmatches|sameTerm|datatype|regex|bound|is(URI|IRI|Blank|Literal))/i) {
118 77         173 my $func = $2;
119             return sprintf(
120             '(%s %s)',
121             $func,
122 77         218 join(' ', map { $_->sse( $context ) } $self->arguments),
  109         877  
123             );
124             } else {
125             return sprintf(
126             '(%s %s)',
127             $self->uri->sse( $context ),
128 71         179 join(' ', map { $_->sse( $context ) } $self->arguments),
  121         1197  
129             );
130             }
131             }
132              
133             =item C<< as_sparql >>
134              
135             Returns the SPARQL string for this algebra expression.
136              
137             =cut
138              
139             sub as_sparql {
140 4     4 1 8 my $self = shift;
141 4         9 my $context = shift;
142 4         7 my $indent = shift;
143 4         14 my @args = $self->arguments;
144 4         13 my $uri = $self->uri->uri_value;
145             my $func = ($uri =~ m/^(sop|sparql):(logical-and|logical-or|in|notin|str|strdt|strlang|if|iri|uri|bnode|lang|langmatches|sameTerm|datatype|regex|bound|is(URI|IRI|Blank|Literal))/i)
146 4 50       70 ? $FUNCTION_MAP{ lc($2) }
147             : $self->uri->as_sparql( $context, $indent );
148 4 50 33     43 if ($func eq 'IN' or $func eq 'NOT IN') {
    50 33        
149 0         0 my $term = shift(@args);
150             my $string = sprintf(
151             "%s %s (%s)",
152             $term->as_sparql( $context, $indent ),
153             $func,
154 0         0 join(', ', map { $_->as_sparql( $context, $indent ) } @args),
  0         0  
155             );
156 0         0 return $string;
157             } elsif ($func eq '||' or $func eq '&&') {
158             my $string = sprintf(
159             "(%s) $func (%s)",
160 0         0 (map { $_->as_sparql( $context, $indent ) } @args),
  0         0  
161             );
162 0         0 return $string;
163             } else {
164             my $string = sprintf(
165             "%s(%s)",
166             $func,
167 4         10 join(', ', map { $_->as_sparql( $context, $indent ) } @args),
  5         24  
168             );
169 4         86 return $string;
170             }
171             }
172              
173             =item C<< type >>
174              
175             Returns the type of this algebra expression.
176              
177             =cut
178              
179             sub type {
180 0     0 1 0 return 'FUNCTION';
181             }
182              
183             =item C<< qualify_uris ( \%namespaces, $base_uri ) >>
184              
185             Returns a new algebra pattern where all referenced Resource nodes representing
186             QNames (ns:local) are qualified using the supplied %namespaces.
187              
188             =cut
189              
190             sub qualify_uris {
191 0     0 1 0 my $self = shift;
192 0         0 my $class = ref($self);
193 0         0 my $ns = shift;
194 0         0 my $base_uri = shift;
195 0         0 my @args;
196 0         0 foreach my $arg ($self->construct_args) {
197 0 0 0     0 if (blessed($arg) and $arg->isa('RDF::Query::Algebra')) {
    0 0        
198 0         0 push(@args, $arg->qualify_uris( $ns, $base_uri ));
199             } elsif (blessed($arg) and $arg->isa('RDF::Query::Node::Resource')) {
200 0         0 my $uri = $arg->uri;
201 0 0       0 if (ref($uri)) {
202 0         0 my ($n,$l) = @$uri;
203 0 0       0 unless (exists($ns->{ $n })) {
204 0         0 throw RDF::Query::Error::QuerySyntaxError -text => "Namespace $n is not defined";
205             }
206 0         0 my $resolved = RDF::Query::Node::Resource->new( join('', $ns->{ $n }, $l), $base_uri );
207 0         0 push(@args, $resolved);
208             } else {
209 0         0 push(@args, $arg);
210             }
211             } else {
212 0         0 push(@args, $arg);
213             }
214             }
215 0         0 return $class->new( @args );
216             }
217              
218             =item C<< evaluate ( $query, \%bound, $context ) >>
219              
220             Evaluates the expression using the supplied bound variables.
221             Will return a RDF::Query::Node object.
222              
223             =cut
224              
225             sub evaluate {
226 354     354 1 1061 my $self = shift;
227 354   100     1003 my $query = shift || 'RDF::Query';
228 354         503 my $bound = shift;
229 354         451 my $context = shift;
230 354         443 my $active_graph = shift;
231 354         778 my $uri = $self->uri;
232            
233 36     36   203 no warnings 'uninitialized';
  36         65  
  36         28275  
234 354         1134 my $uriv = $uri->uri_value;
235 354 100 100     4762 if ($uriv =~ /^sparql:logical-(.+)$/ or $uriv =~ /^sparql:(not)?in$/ or $uriv eq 'sparql:coalesce') {
    50 100        
    100          
236             # logical operators must have their arguments passed lazily, because
237             # some of them can still succeed even if some of their arguments throw
238             # TypeErrors (e.g. true || fail ==> true).
239 28         73 my @args = $self->arguments;
240             my $args = sub {
241 61     61   97 my $value = shift(@args);
242 61 100       286 return unless (blessed($value));
243 50         74 my $val = 0;
244             try {
245             $val = $value->isa('RDF::Query::Expression')
246             ? $value->evaluate( $query, $bound, $context, $active_graph )
247             : ($value->isa('RDF::Trine::Node::Variable'))
248 50 100       1193 ? $bound->{ $value->name }
    100          
249             : $value;
250 50         299 } otherwise {};
251 50   100     1014 return $val || 0;
252 28         139 };
253 28         109 my $func = $query->get_function( $uri );
254 28         107 my $value = $func->( $query, $args );
255 28         697 return $value;
256             } elsif ($uriv =~ /^sparql:if$/) {
257 0         0 my @args = $self->arguments;
258 0         0 my $ebv = RDF::Query::Node::Resource->new( "sparql:ebv" );
259 0         0 my $expr = shift(@args);
260 0         0 my $index = 1;
261 0         0 my $ok = 1;
262             try {
263 0     0   0 my $exprval = $query->var_or_expr_value( $bound, $expr, $context );
264 0         0 my $func = RDF::Query::Expression::Function->new( $ebv, $exprval );
265 0         0 my $value = $func->evaluate( $query, {}, $context, $active_graph );
266 0 0       0 my $bool = ($value->literal_value eq 'true') ? 1 : 0;
267 0 0       0 if ($bool) {
268 0         0 $index = 0;
269             }
270             } catch RDF::Query::Error::TypeError with {
271 0     0   0 $ok = 0;
272 0         0 };
273 0 0       0 if ($ok) {
274 0         0 my $expr2 = $args[$index];
275 0         0 return $query->var_or_expr_value( $bound, $expr2, $context );
276             } else {
277 0         0 return;
278             }
279             } elsif ($uriv eq 'sparql:exists') {
280 4         16 my $func = $query->get_function($uri);
281 4         11 my ($ggp) = $self->arguments;
282 4         17 return $func->( $query, $context, $bound, $ggp, $active_graph );
283             } else {
284 322 100       813 my $model = ref($query) ? $query->{model} : undef;
285 322 100       1184 if (blessed($context)) {
286 217         668 $model = $context->model;
287             }
288            
289 322         505 my @args;
290 322 100       662 if (ref($query)) {
291             # localize the model in the query object (legacy code wants the model accessible from the query object)
292 233         604 local($query->{model}) = $model;
293             @args = map {
294 233         575 $_->isa('RDF::Query::Algebra')
295             ? $_->evaluate( $query, $bound, $context, $active_graph )
296             : ($_->isa('RDF::Trine::Node::Variable'))
297 309 100       2693 ? $bound->{ $_->name }
    100          
298             : $_
299             } $self->arguments;
300             } else {
301             @args = map {
302 89         228 $_->isa('RDF::Query::Algebra')
303             ? $_->evaluate( $query, $bound, $context, $active_graph )
304             : ($_->isa('RDF::Trine::Node::Variable'))
305 103 100       1012 ? $bound->{ $_->name }
    100          
306             : $_
307             } $self->arguments;
308             }
309            
310 322         2208 my $func = $query->get_function($uri);
311 322 50       782 unless ($func) {
312 0         0 throw RDF::Query::Error::ExecutionError -text => "Failed to get function for IRI $uri";
313             }
314 322         1088 my $value = $func->( $query, @args );
315 310         33289 return $value;
316             }
317             }
318              
319             1;
320              
321             __END__
322              
323             =back
324              
325             =head1 AUTHOR
326              
327             Gregory Todd Williams <gwilliams@cpan.org>
328              
329             =cut