File Coverage

blib/lib/RDF/Trine/Store/LanguagePreference.pm
Criterion Covered Total %
statement 112 149 75.1
branch 19 30 63.3
condition 8 17 47.0
subroutine 19 27 70.3
pod 13 13 100.0
total 171 236 72.4


line stmt bran cond sub pod time code
1             =encoding utf8
2              
3             =head1 NAME
4              
5             RDF::Trine::Store::LanguagePreference - RDF Store proxy for filtering language tagged literals
6              
7             =head1 VERSION
8              
9             This document describes RDF::Trine::Store::LanguagePreference version 1.017
10              
11             =head1 SYNOPSIS
12              
13             use RDF::Trine::Store::LanguagePreference;
14              
15             =head1 DESCRIPTION
16              
17             RDF::Trine::Store::LanguagePreference provides a RDF::Trine::Store API to
18             filter the statements made available from some underlying store object based
19             on a users' language preferences (e.g. coming from an Accept-Language HTTP
20             header value).
21              
22             =cut
23              
24             package RDF::Trine::Store::LanguagePreference;
25              
26 68     68   452 use strict;
  68         177  
  68         1927  
27 68     68   346 use warnings;
  68         200  
  68         1817  
28 68     68   361 no warnings 'redefine';
  68         149  
  68         2008  
29 68     68   379 use base qw(RDF::Trine::Store);
  68         161  
  68         4716  
30              
31 68     68   428 use Data::Dumper;
  68         168  
  68         3224  
32 68     68   392 use List::Util qw(reduce max);
  68         186  
  68         3763  
33 68     68   428 use Scalar::Util qw(refaddr reftype blessed);
  68         166  
  68         3322  
34 68     68   416 use RDF::Trine::Iterator qw(sgrep);
  68         165  
  68         5394  
35              
36             ######################################################################
37              
38             my @pos_names;
39             our $VERSION;
40             BEGIN {
41 68     68   265 $VERSION = "1.017";
42 68         170 my $class = __PACKAGE__;
43 68         182 $RDF::Trine::Store::STORE_CLASSES{ $class } = $VERSION;
44 68         73742 @pos_names = qw(subject predicate object context);
45             }
46              
47             ######################################################################
48              
49             =head1 METHODS
50              
51             Beyond the methods documented below, this class inherits methods from the
52             L<RDF::Trine::Store> class.
53              
54             =over 4
55              
56             =item C<< new ( $store, { $lang1 => $q1, $lang2 => $q2, ... } ) >>
57              
58             Returns a new storage object that will act as a proxy for the C<< $store >> object,
59             filtering language literals based on the expressed language preferences.
60              
61             =item C<new_with_config ( $hashref )>
62              
63             Returns a new storage object configured with a hashref with certain
64             keys as arguments.
65              
66             The C<storetype> key must be C<LanguagePreference> for this backend.
67              
68             The following key must also be used:
69              
70             =over
71              
72             =item C<store>
73              
74             A configuration hash for the underlying store object.
75              
76             =item C<preferred_languages>
77              
78             A hash reference mapping language tags to quality values in the range [0, 1].
79             The referent may be changed between operations to change the set of preferred
80             languages used in statement matching.
81              
82             =back
83              
84             =cut
85              
86             sub new {
87 3     3 1 1614 my $class = shift;
88 3         8 my $store = shift;
89 3         7 my $pref = shift;
90 3         14 my $self = bless({
91             store => $store,
92             preferred_languages => $pref,
93             }, $class);
94 3         10 return $self;
95             }
96              
97             =item C<< new_with_config ( \%config ) >>
98              
99             Returns a new RDF::Trine::Store object based on the supplied configuration hashref.
100              
101             =cut
102              
103             sub new_with_config {
104 0     0 1 0 my $proto = shift;
105 0         0 my $config = shift;
106 0         0 $config->{storetype} = 'LanguagePreference';
107 0         0 return $proto->SUPER::new_with_config( $config );
108             }
109              
110             sub _new_with_config {
111 0     0   0 my $class = shift;
112 0         0 my $config = shift;
113 0         0 return $class->new( @{ $config }{ qw(store preferred_languages) } );
  0         0  
114             }
115              
116             sub _config_meta {
117             return {
118 0     0   0 required_keys => [qw(store preferred_languages)],
119             fields => {
120             store => { description => 'Store config', type => 'string' },
121             preferred_languages => { description => 'Preferred languages', type => 'hash' },
122             }
123             }
124             }
125              
126              
127             =item C<< language_preferences >>
128              
129             Returns a hash of the language preference quality values.
130              
131             =cut
132              
133             sub language_preferences {
134 0     0 1 0 my $self = shift;
135 0         0 return %{ $self->{preferred_languages} };
  0         0  
136             }
137              
138             =item C<< language_preference( $lang ) >>
139              
140             Return the quality value preference for the given language.
141              
142             =cut
143              
144             sub language_preference {
145 0     0 1 0 my $self = shift;
146 0         0 my $lang = shift;
147 0         0 return $self->{preferred_languages}{$lang};
148             }
149              
150             =item C<< update_language_preference( $lang => $qvalue ) >>
151              
152             Update the quality value preference for the given language.
153              
154             =cut
155              
156             sub update_language_preference {
157 0     0 1 0 my $self = shift;
158 0         0 my $lang = shift;
159 0         0 my $q = shift;
160 0 0       0 if ($q == 0) {
161 0         0 delete $self->{preferred_languages}{$lang};
162             } else {
163 0         0 $self->{preferred_languages}{$lang} = $q;
164             }
165             }
166              
167             =item C<< get_statements ( $subject, $predicate, $object [, $context] ) >>
168              
169             Returns a stream object of all statements matching the specified subject,
170             predicate and objects. Any of the arguments may be undef to match any value.
171              
172             =cut
173              
174             sub get_statements {
175 4     4 1 10 my $self = shift;
176 4         17 my @nodes = @_[0..3];
177 4         9 my $bound = 0;
178 4         8 my %bound;
179            
180 4         9 my $use_quad = 0;
181 4 100       15 if (scalar(@_) >= 4) {
182 1         3 my $g = $nodes[3];
183 1 0 33     7 if (blessed($g) and not($g->is_variable) and not($g->is_nil)) {
      33        
184 0         0 $use_quad = 1;
185 0         0 $bound++;
186 0         0 $bound{ 3 } = $g;
187             }
188             }
189            
190 4         13 my @var_map = qw(s p o g);
191 4         16 my %var_map = map { $var_map[$_] => $_ } (0 .. $#var_map);
  16         45  
192 4         10 my @node_map;
193 4         19 foreach my $i (0 .. $#nodes) {
194 16 100 66     62 if (not(blessed($nodes[$i])) or $nodes[$i]->is_variable) {
195 13         50 $nodes[$i] = RDF::Trine::Node::Variable->new( $var_map[ $i ] );
196             }
197             }
198            
199 4         11 my $cache = {};
200 4         27 my $iter = $self->{store}->get_statements(@nodes);
201             return RDF::Trine::Iterator::sgrep(sub {
202 45     45   105 return $self->languagePreferenceAllowsStatement($_, $cache);
203 4         27 }, $iter);
204             }
205              
206             =item C<< count_statements ( $subject, $predicate, $object, $context ) >>
207              
208             Returns a count of all the statements matching the specified subject,
209             predicate, object, and context. Any of the arguments may be undef to match any
210             value.
211              
212             =cut
213              
214             sub count_statements {
215 1     1 1 3 my $self = shift;
216 1         4 my $iter = $self->get_statements(@_);
217 1         2 my $count = 0;
218 1         7 while ($iter->next) {
219 2         5 $count++;
220             }
221 1         11 return $count;
222             }
223              
224             =item C<< qvalueForLanguage ( $language, \%cache ) >>
225              
226             Returns the q-value for C<< $language >> based on the current language
227             preference. C<< %cache >> is used across multiple calls to this method for
228             performance reasons.
229              
230             =cut
231              
232             sub qvalueForLanguage {
233 484     484 1 660 my $self = shift;
234 484         664 my $lang = shift;
235 484   50     992 my $cache = shift || {};
236 484 100       944 if (exists $cache->{$lang}) {
237 440         1028 return $cache->{$lang};
238             } else {
239 44         59 my %q;
240 44         64 foreach my $l (keys %{ $self->{preferred_languages} }) {
  44         90  
241 33 100       222 if ($lang =~ /^$l/) {
242 3         8 my $q = $self->{preferred_languages}{$l};
243 3         8 $q{$l} = $q;
244             }
245             }
246 44         73 my $q;
247 44 100       61 if (scalar(@{ [ keys %q ] })) {
  44         101  
248 3         12 my @keys = sort { length($b) <=> length($a) } keys %q;
  0         0  
249 3         6 $q = $q{$keys[0]};
250             } else {
251 41         59 $q = 0.001;
252             }
253 44         87 $cache->{$lang} = $q;
254 44         105 return $q;
255             }
256             }
257              
258             =item C<< siteQValueForLanguage ( $language ) >>
259              
260             Returns an implementation-specific q-value preference for the given
261             C<< $language >>. This method may be overridden by subclasses to control the
262             default preferred language.
263              
264             =cut
265              
266             sub siteQValueForLanguage {
267 484     484 1 719 my $self = shift;
268 484         684 my $lang = shift;
269 484 100       1487 return ($lang =~ /^en/) ? 1.0 : 0.999;
270             }
271              
272             =item C<< availableLanguagesForStatement( $statement ) >>
273              
274             Returns a list of language tags that are available in the underlying store for
275             the given statement object. For example, if C<< $statement >> represented the
276             triple:
277              
278             dbpedia:Los_Angeles rdf:label "Los Angeles"@en
279              
280             and the underlying store contains the triples:
281              
282             dbpedia:Los_Angeles rdf:label "Los Angeles"@en
283             dbpedia:Los_Angeles rdf:label "ロサンゼルス"@ja
284             dbpedia:Los_Angeles rdf:label "Лос-Анджелес"@ru
285              
286             then the return value would be C<< ('en', 'ja', 'ru') >>.
287              
288             =cut
289              
290             sub availableLanguagesForStatement {
291 44     44 1 74 my $self = shift;
292 44         60 my $st = shift;
293 44         68 my %languages;
294 44         124 my @nodes = $st->nodes;
295 44         84 $nodes[2] = undef;
296 44         145 my $iter = $self->{store}->get_statements(@nodes);
297 44         144 while (my $q = $iter->next) {
298 484         1062 my $object = $q->object;
299 484 50 33     1755 if ($object->isa('RDF::Trine::Node::Literal') and $object->has_language) {
300 484         1063 my $language = $object->literal_value_language;
301 484         1410 $languages{$language}++;
302             }
303             }
304 44         478 return keys %languages;
305             }
306              
307             =item C<< languagePreferenceAllowsStatement ( $statement, \%cache ) >>
308              
309             Returns true if the C<< $statement >> is allowed by the current language
310             preference. C<< %cache >> is used across multiple calls to this method for
311             performance reasons.
312              
313             =cut
314              
315             sub languagePreferenceAllowsStatement {
316 45     45 1 76 my $self = shift;
317 45         66 my $st = shift;
318 45         71 my $cache = shift;
319 45         127 my $object = $st->object;
320 45 100 66     224 if ($object->isa('RDF::Trine::Node::Literal') and $object->has_language) {
321 44         105 my $language = $object->literal_value_language;
322 44         103 my @availableLanguages = $self->availableLanguagesForStatement($st);
323 44         122 my %availableValues = map { $_ => $self->qvalueForLanguage($_, $cache) * $self->siteQValueForLanguage($_) } @availableLanguages;
  484         938  
324 44 100   440   357 my $prefLang = reduce { $availableValues{$a} > $availableValues{$b} ? $a : $b } keys %availableValues;
  440         999  
325 44         278 return ($prefLang eq $language);
326             } else {
327 1         4 return 1;
328             }
329             }
330              
331              
332             =item C<< supports ( [ $feature ] ) >>
333              
334             If C<< $feature >> is specified, returns true if the feature is supported by the
335             store, false otherwise. If C<< $feature >> is not specified, returns a list of
336             supported features.
337              
338             =cut
339              
340             sub supports {
341 0     0 1 0 my $self = shift;
342 0         0 return;
343             }
344              
345             =begin private
346              
347             =item C<< can >>
348              
349             Delegating implementation.
350              
351             =end private
352              
353             =cut
354              
355             sub can {
356 2     2 1 3 my $proto = shift;
357 2         5 my $name = shift;
358 2         6 my %methods = map { $_ => 1 } qw(new new_with_config _new_with_config get_statements count_statements);
  10         22  
359 2 50       8 return 1 if exists $methods{$name};
360 2 50       5 if (ref($proto)) {
361 2         18 return $proto->{store}->can($name);
362             } else {
363 0           return;
364             }
365             }
366              
367             sub AUTOLOAD {
368 0     0     my $self = shift;
369 0           our $AUTOLOAD;
370 0 0         return if ($AUTOLOAD =~ /:DESTROY$/);
371 0           my ($name) = ($AUTOLOAD =~ m/^.*:(.*)$/);
372 0           my $store = $self->{store};
373 0 0         unless ($store->can($name)) {
374 0           my $class = ref($store);
375 0           Carp::confess qq[Can't locate object method "$name" via package "$class"];
376             }
377 0           return $store->$name(@_);
378             }
379              
380             1;
381              
382             __END__
383              
384             =back
385              
386             =head1 BUGS
387              
388             Please report any bugs or feature requests to through the GitHub web interface
389             at L<https://github.com/kasei/perlrdf/issues>.
390              
391             =head1 AUTHOR
392              
393             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
394              
395             =head1 COPYRIGHT
396              
397             Copyright (c) 2006-2012 Gregory Todd Williams. This
398             program is free software; you can redistribute it and/or modify it under
399             the same terms as Perl itself.
400              
401             =cut