File Coverage

blib/lib/SemanticWeb/OAI/ORE/N3.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package SemanticWeb::OAI::ORE::N3;
2             #$Id: N3.pm,v 1.14 2010-12-06 14:44:15 simeon Exp $
3              
4             =head1 NAME
5              
6             SemanticWeb::OAI::ORE::N3 - Parse/serialize OAI-ORE Resource Maps in N3 format
7              
8             =head1 SYNPOSIS
9              
10             Class to parse and serialize OAI-ORE ReMs in N3 format.
11              
12             =head1 DESCRIPTION
13              
14             Follows N3 specification defined by L.
15              
16             =cut
17              
18 7     7   36 use strict;
  7         14  
  7         220  
19 7     7   37 use warnings;
  7         21  
  7         197  
20 7     7   33 use Carp;
  7         13  
  7         397  
21              
22 7     7   38 use SemanticWeb::OAI::ORE::ReM;
  7         11  
  7         183  
23 7     7   42 use SemanticWeb::OAI::ORE::Constant qw(:all);
  7         12  
  7         2149  
24              
25 7     7   3966 use RDF::Notation3::RDFCore;
  0            
  0            
26             use RDF::Notation3::Triples;
27             use RDF::Core::Storage::Memory;
28             use RDF::Core::Model;
29             use RDF::Core::Resource;
30             use RDF::Core::Enumerator;
31             use Data::Dumper;
32              
33             =head1 METHODS
34              
35             =head2 new()
36              
37             =cut
38              
39             sub new {
40             my $class=shift;
41             my $self={'strict'=>'warn',
42             @_};
43             bless $self, (ref($class) || $class);
44             return($self);
45             }
46              
47              
48             =head2 parse($src,$uri_rem)
49              
50             Parse $src which is either a string containing the N3 serialization of
51             the ReM with URI $uri_rem, or a filehandle.
52              
53             =cut
54              
55             sub parse {
56             my $self=shift;
57             my ($src,$uri_rem)=@_;
58              
59             my $rdf = RDF::Notation3::RDFCore->new();
60             $rdf->set_storage(RDF::Core::Storage::Memory->new());
61             my $model;
62             if (ref($src) eq 'IO::File') {
63             $model = $rdf->parse_file($src); #takes IO::File or path
64             } elsif (ref($src)) {
65             croak("Attempt to parse N3 with a ".ref($src)." object supplied");
66             } else {
67             $model = $rdf->parse_string($src);
68             }
69              
70             # $model is an RDF::Core::Model
71             # Now we look for statements that are special to OAI-ORE
72             #print "got $model ".ref($model)."\n";
73             #print Dumper($model);
74             return($model);
75             }
76              
77              
78             =head3 serialize()
79              
80             We could do this by simply converting all data to triples
81             and then dumping them as N3. However, we attempt to do a
82             nice "pretty print" specific to ORE, with a few comments for
83             the parts.
84              
85             =cut
86              
87             sub serialize {
88             my $self=shift;
89             my $out='';
90             my $rem=$self->{rem};
91             if (ref($rem) and $rem->isa('SemanticWeb::OAI::ORE::ReM')) {
92             # Get the info from the ReM
93             my $uri_r=$rem->uri;
94             my $uri_a=$rem->aggregation;
95             my @rem_and_agg=();
96             my @agg_res=();
97             my @lines=();
98             $self->_prefixify_setup;
99             foreach my $statement (@{$rem->model->as_array()}) {
100             my ($subject,$predicate,$object,$is_literal)=@$statement;
101             my $line=$self->_make_n3_line(@$statement);
102             if ($subject eq $uri_r and $predicate eq DESCRIBES) {
103             push(@rem_and_agg,$line);
104             } elsif ($subject eq $uri_a and $predicate eq AGGREGATES) {
105             push(@agg_res,$line);
106             } else {
107             push(@lines,$line);
108             }
109             #FIXME
110             }
111             # Now print it
112             $out.="### OAI-ORE Resource Map ($uri_r)\n";
113             $out.=$self->_prefixes_section;
114             $out.="\n# Resource Map and Aggregation\n";
115             $out.=join('',(sort @rem_and_agg)) if (@rem_and_agg);
116             $out.="\n# Aggregated resources\n";
117             $out.=join('',(sort @agg_res)) if (@agg_res);
118             $out.="\n# Relations\n";
119             $out.=join('',(sort @lines)) if (@lines);
120             $out.="\n### End Resource Map\n";
121             } else {
122             carp "Can't serialize something that isn't a rem: $rem, ".ref($rem)."\n";
123             }
124             return($out);
125             }
126              
127              
128             # Create a single N3 line from subject,predicate,object labels with
129             # appropriate escaping.
130             #
131             sub _make_n3_line {
132             my $self=shift;
133             my ($subject,$predicate,$object,$object_is_literal)=@_;
134             my $line='';
135             if (my $psubject=$self->_prefixify($subject)) {
136             $line.=$psubject.' ';
137             } else {
138             $line.='<'.$subject.'> ';
139             }
140             if (my $ppredicate=$self->_prefixify_predicate($predicate)) {
141             $line.=$ppredicate.' ';
142             } else {
143             $line.='<'.$predicate.'> ';
144             }
145             if ($object_is_literal) {
146             $line.='"'._n3_escape($object).'"';
147             } elsif (my $pobject=$self->_prefixify($object)) {
148             $line.=$pobject;
149             } else {
150             $line.='<'.$object.'>';
151             }
152             return($line.".\n");
153             }
154              
155              
156             # Escape a string for use in N3 quoted string.
157             # See: http://www.w3.org/DesignIssues/Notation3
158             #
159             sub _n3_escape {
160             my ($sstr)=@_;
161             my $rstr='';
162             foreach my $c (split(//,$sstr)) {
163             if ($c eq '\\') {
164             $c='\\\\';
165             } elsif ($c eq '"') {
166             $c='\\"';
167             } elsif ($c eq "\n") {
168             $c='\\n';
169             } elsif ($c eq "\r") {
170             $c='\\r';
171             } elsif ($c eq "\t") {
172             $c='\\t';
173             }
174             $rstr.=$c;
175             }
176             return($rstr);
177             }
178              
179              
180             sub _parse_warning {
181             my $self=shift;
182             my $msg=shift;
183             print "_parse_warning: $msg\n";
184             }
185              
186              
187             sub _parse_error {
188             my $self=shift;
189             my $msg=shift;
190             if ($self->{strict} eq 'warn') {
191             return($self->_parse_warning($msg));
192             }
193             croak "_parse_error: $msg\n";
194             }
195              
196              
197             # Setup ready to use _prefixify.
198             #
199             # WARNING - have to be carefull that constants do not get quoted
200             # in the hash. See Caveats in http://perldoc.perl.org/constant.html
201             #
202             sub _prefixify_setup {
203             my $self=shift;
204             $self->{prefixes_known}={
205             ORE_NS() => ORE_PREFIX(),
206             DC_NS() => DC_PREFIX(),
207             DCT_NS() => DCT_PREFIX(),
208             RDF_NS() => RDF_PREFIX() };
209             $self->{prefixes_used}={};
210             }
211              
212              
213             # Return prefixes section of N3 output.
214             #
215             sub _prefixes_section {
216             my $self=shift;
217             my $out='';
218             if (scalar(keys %{$self->{prefixes_used}})) {
219             $out.="\n# Namespace prefixes\n";
220             foreach my $prefix (sort keys %{$self->{prefixes_used}}) {
221             $out.="\@prefix $prefix: <".$self->{prefixes_used}{$prefix}.">.\n";
222             }
223             }
224             return($out);
225             }
226              
227              
228             # Takes input $uri, return possible prefixed $uri having added
229             # prefix to list of known used prefixes.
230             #
231             sub _prefixify {
232             my $self=shift;
233             my ($uri)=@_;
234             foreach my $prefix (keys %{$self->{prefixes_known}}) {
235             if ($uri=~s%^$prefix%%) {
236             $self->{prefixes_used}{$self->{prefixes_known}{$prefix}}=$prefix;
237             return($self->{prefixes_known}{$prefix}.':'.$uri);
238             }
239             }
240             # Nothing found, return nothing
241             return();
242             }
243              
244              
245             # Special support for predicates where N3 has certain shorthands
246             #
247             sub _prefixify_predicate {
248             my $self=shift;
249             my ($uri)=@_;
250             if ($uri eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type') {
251             return('a');
252             } elsif ($uri eq 'http://www.w3.org/2002/07/owl#sameAs') {
253             return('=');
254             } elsif ($uri eq 'http://www.w3.org/2000/10/swap/log#implies') {
255             return('=>');
256             }
257             # No specials for predicate found, try normal _prefixify
258             return($self->_prefixify($uri));
259             }
260              
261              
262             =head1 SEE ALSO
263              
264             L and associated modules.
265              
266             =head1 AUTHORS
267              
268             Simeon Warner
269              
270             =head1 LICENSE AND COPYRIGHT
271              
272             Copyright 2007-2010 Simeon Warner.
273              
274             This module is free software; you can redistribute it and/or
275             modify it under the same terms as Perl itself. See L.
276              
277             =cut
278              
279             1;