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.916.
11              
12             =cut
13              
14             package RDF::Query::Expression::Function;
15              
16 36     36   176 use strict;
  36         66  
  36         879  
17 36     36   172 use warnings;
  36         73  
  36         940  
18 36     36   173 no warnings 'redefine';
  36         67  
  36         1123  
19 36     36   173 use base qw(RDF::Query::Expression);
  36         65  
  36         2675  
20              
21 36     36   18713 use RDF::Query::Error qw(:try);
  36         97  
  36         328  
22 36     36   6097 use Data::Dumper;
  36         74  
  36         1608  
23 36     36   181 use Scalar::Util qw(blessed reftype);
  36         62  
  36         1817  
24 36     36   175 use Carp qw(carp croak confess);
  36         68  
  36         2565  
25              
26             ######################################################################
27              
28             our ($VERSION);
29             BEGIN {
30 36     36   40536 $VERSION = '2.916';
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 9450 my $class = shift;
76 220         338 my $uri = shift;
77 220         477 my @args = @_;
78 220 100 66     1662 unless (blessed($uri) and $uri->isa('RDF::Trine::Node::Resource')) {
79 85         275 $uri = RDF::Query::Node::Resource->new( $uri );
80             }
81 220         2133 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 616     616 1 790 my $self = shift;
92 616         1733 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 512     512 1 14881 my $self = shift;
103 512         1532 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 222 my $self = shift;
114 148         201 my $context = shift;
115            
116 148         365 my $uri = $self->uri->uri_value;
117 148 100       1740 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         183 my $func = $2;
119             return sprintf(
120             '(%s %s)',
121             $func,
122 77         222 join(' ', map { $_->sse( $context ) } $self->arguments),
  109         992  
123             );
124             } else {
125             return sprintf(
126             '(%s %s)',
127             $self->uri->sse( $context ),
128 71         178 join(' ', map { $_->sse( $context ) } $self->arguments),
  121         1263  
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 9 my $self = shift;
141 4         7 my $context = shift;
142 4         7 my $indent = shift;
143 4         12 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       75 ? $FUNCTION_MAP{ lc($2) }
147             : $self->uri->as_sparql( $context, $indent );
148 4 50 33     45 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         88 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 360     360 1 1088 my $self = shift;
227 360   100     1087 my $query = shift || 'RDF::Query';
228 360         519 my $bound = shift;
229 360         462 my $context = shift;
230 360         456 my $active_graph = shift;
231 360         786 my $uri = $self->uri;
232            
233 36     36   249 no warnings 'uninitialized';
  36         71  
  36         28661  
234 360         1114 my $uriv = $uri->uri_value;
235 360 100 100     4758 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         74 my @args = $self->arguments;
240             my $args = sub {
241 61     61   111 my $value = shift(@args);
242 61 100       251 return unless (blessed($value));
243 50         91 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       1312 ? $bound->{ $value->name }
    100          
249             : $value;
250 50         302 } otherwise {};
251 50   100     1083 return $val || 0;
252 28         140 };
253 28         115 my $func = $query->get_function( $uri );
254 28         110 my $value = $func->( $query, $args );
255 28         707 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         17 my $func = $query->get_function($uri);
281 4         10 my ($ggp) = $self->arguments;
282 4         17 return $func->( $query, $context, $bound, $ggp, $active_graph );
283             } else {
284 328 100       785 my $model = ref($query) ? $query->{model} : undef;
285 328 100       1135 if (blessed($context)) {
286 223         667 $model = $context->model;
287             }
288            
289 328         514 my @args;
290 328 100       679 if (ref($query)) {
291             # localize the model in the query object (legacy code wants the model accessible from the query object)
292 239         637 local($query->{model}) = $model;
293             @args = map {
294 239         618 $_->isa('RDF::Query::Algebra')
295             ? $_->evaluate( $query, $bound, $context, $active_graph )
296             : ($_->isa('RDF::Trine::Node::Variable'))
297 321 100       2935 ? $bound->{ $_->name }
    100          
298             : $_
299             } $self->arguments;
300             } else {
301             @args = map {
302 89         201 $_->isa('RDF::Query::Algebra')
303             ? $_->evaluate( $query, $bound, $context, $active_graph )
304             : ($_->isa('RDF::Trine::Node::Variable'))
305 103 100       896 ? $bound->{ $_->name }
    100          
306             : $_
307             } $self->arguments;
308             }
309            
310 328         2338 my $func = $query->get_function($uri);
311 328 50       797 unless ($func) {
312 0         0 throw RDF::Query::Error::ExecutionError -text => "Failed to get function for IRI $uri";
313             }
314 328         1125 my $value = $func->( $query, @args );
315 316         34140 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