File Coverage

blib/lib/XML/Atom/OWL.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 XML::Atom::OWL;
2              
3 1     1   31933 use 5.010;
  1         3  
  1         51  
4 1     1   1271 use common::sense;
  1         11  
  1         6  
5              
6 1     1   64 use Carp 1.00;
  1         37  
  1         105  
7 1     1   5638 use DateTime 0;
  1         330927  
  1         57  
8 1     1   1308 use Encode 0 qw(encode_utf8);
  1         11800  
  1         96  
9 1     1   461 use HTTP::Link::Parser 0.100;
  0            
  0            
10             use LWP::UserAgent 0;
11             use MIME::Base64 0 qw(decode_base64);
12             use RDF::Trine 0.135;
13             use Scalar::Util 0 qw(blessed);
14             use URI 1.30;
15             use URI::URL 0;
16             use XML::LibXML 1.70 qw(:all);
17              
18             use constant AAIR_NS => 'http://xmlns.notu.be/aair#';
19             use constant ATOM_NS => 'http://www.w3.org/2005/Atom';
20             use constant AWOL_NS => 'http://bblfish.net/work/atom-owl/2006-06-06/#';
21             use constant AS_NS => 'http://activitystrea.ms/spec/1.0/';
22             use constant AX_NS => 'http://buzzword.org.uk/rdf/atomix#';
23             use constant FH_NS => 'http://purl.org/syndication/history/1.0';
24             use constant FOAF_NS => 'http://xmlns.com/foaf/0.1/';
25             use constant IANA_NS => 'http://www.iana.org/assignments/relation/';
26             use constant RDF_NS => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
27             use constant RDF_TYPE => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
28             use constant THR_NS => 'http://purl.org/syndication/thread/1.0';
29             use constant XSD_NS => 'http://www.w3.org/2001/XMLSchema#';
30              
31             our $VERSION = '0.103';
32              
33             sub new
34             {
35             my $class = shift;
36             my $content = shift;
37             my $baseuri = shift;
38             my $options = shift || undef;
39             my $store = shift || undef;
40             my $domtree;
41            
42             unless (defined $content)
43             {
44             my $ua = LWP::UserAgent->new;
45             $ua->agent(sprintf('%s/%s ', __PACKAGE__, $VERSION));
46             $ua->default_header("Accept" => "application/atom+xml, application/xml;q=0.1, text/xml;q=0.1");
47             my $response = $ua->get($baseuri);
48             croak "HTTP response not successful\n"
49             unless $response->is_success;
50             croak "Non-Atom HTTP response\n"
51             unless $response->content_type =~ m`^(text/xml)|(application/(atom\+xml|xml))$`;
52             $content = $response->decoded_content;
53             }
54              
55             if (blessed($content) and $content->isa('XML::LibXML::Document'))
56             {
57             ($domtree, $content) = ($content, $content->toString);
58             }
59             else
60             {
61             my $xml_parser = XML::LibXML->new;
62             $domtree = $xml_parser->parse_string($content);
63             }
64              
65             $store = RDF::Trine::Store::DBI->temporary_store
66             unless defined $store;
67              
68             my $self = bless {
69             'content' => $content,
70             'baseuri' => $baseuri,
71             'options' => $options,
72             'DOM' => $domtree,
73             'sub' => {},
74             'RESULTS' => RDF::Trine::Model->new($store),
75             }, $class;
76              
77             return $self;
78             }
79              
80             sub uri
81             {
82             my $this = shift;
83             my $param = shift || '';
84             my $opts = shift || {};
85            
86             if ((ref $opts) =~ /^XML::LibXML/)
87             {
88             my $x = {'element' => $opts};
89             $opts = $x;
90             }
91            
92             if ($param =~ /^([a-z][a-z0-9\+\.\-]*)\:/i)
93             {
94             # seems to be an absolute URI, so can safely return "as is".
95             return $param;
96             }
97             elsif ($opts->{'require-absolute'})
98             {
99             return undef;
100             }
101            
102             my $base = $this->{baseuri};
103             if ($opts->{'element'})
104             {
105             $base = $this->get_node_base($opts->{'element'});
106             }
107            
108             my $url = url $param, $base;
109             my $rv = $url->abs->as_string;
110              
111             while ($rv =~ m!^(http://.*)(\.\./|\.)+(\.\.|\.)?$!i)
112             {
113             $rv = $1;
114             }
115            
116             return $rv;
117             }
118              
119             sub dom
120             {
121             my $this = shift;
122             return $this->{DOM};
123             }
124              
125              
126             sub graph
127             {
128             my $this = shift;
129             $this->consume;
130             return $this->{RESULTS};
131             }
132              
133             sub graphs
134             {
135             my $this = shift;
136             $this->consume;
137             return { $this->{'baseuri'} => $this->{RESULTS} };
138             }
139              
140             sub root_identifier
141             {
142             my $self = shift;
143             $self->consume;
144             if ($self->{'root_identifier'} =~ /^_:(.*)/)
145             {
146             return RDF::Trine::Node::Blank->new($1);
147             }
148             else
149             {
150             return RDF::Trine::Node::Resource->new($self->{'root_identifier'});
151             }
152             }
153              
154             sub set_callbacks
155             # Set callback functions for handling RDF triples.
156             {
157             my $this = shift;
158              
159             if ('HASH' eq ref $_[0])
160             {
161             $this->{'sub'} = $_[0];
162             }
163             elsif (defined $_[0])
164             {
165             die("What kind of callback hashref was that??\n");
166             }
167             else
168             {
169             $this->{'sub'} = undef;
170             }
171            
172             return $this;
173             }
174              
175             sub consume
176             {
177             my $self = shift;
178              
179             return $self if $self->{'comsumed'};
180              
181             my $root = $self->dom->documentElement;
182            
183             if ($root->namespaceURI eq ATOM_NS and $root->localname eq 'feed')
184             {
185             $self->{'root_identifier'} = $self->consume_feed($root);
186             }
187             elsif ($root->namespaceURI eq ATOM_NS and $root->localname eq 'entry')
188             {
189             $self->{'root_identifier'} = $self->consume_entry($root);
190             }
191            
192             $self->{'comsumed'}++;
193            
194             return $self;
195             }
196              
197             sub consume_feed
198             {
199             my $self = shift;
200             my $feed = shift;
201             my $skip_entries = shift || 0;
202            
203             # Feed
204             my $feed_identifier = $self->bnode($feed);
205             $self->rdf_triple($feed, $feed_identifier, RDF_TYPE, AWOL_NS.'Feed');
206              
207             # Common stuff
208             $self->consume_feed_or_entry($feed, $feed_identifier);
209              
210             # fh:archive and fh:complete
211             if ($feed->getChildrenByTagNameNS(FH_NS, 'archive'))
212             {
213             $self->rdf_triple($feed, $feed_identifier, RDF_TYPE, AX_NS.'ArchiveFeed');
214             }
215             my $complete = 0;
216             if ($feed->getChildrenByTagNameNS(FH_NS, 'complete'))
217             {
218             $complete = 1;
219             $self->rdf_triple($feed, $feed_identifier, RDF_TYPE, AX_NS.'CompleteFeed');
220             }
221            
222             my $last_listid;
223              
224             # entry
225             unless ($skip_entries)
226             {
227             my @elems = $feed->getChildrenByTagNameNS(ATOM_NS, 'entry');
228             foreach my $e (@elems)
229             {
230             my $entry_identifier = $self->consume_entry($e);
231             $self->rdf_triple($e, $feed_identifier, AWOL_NS.'entry', $entry_identifier);
232              
233             # If this feed is known to be complete, include an rdf:List
234             # to assist in open-world reasoning.
235             if ($complete)
236             {
237             my $listid = $self->bnode;
238             if (defined $last_listid)
239             {
240             $self->rdf_triple($e, $last_listid, RDF_NS.'rest', $listid);
241             }
242             else
243             {
244             $self->rdf_triple($e, $feed_identifier, AX_NS.'entry-list', $listid);
245             }
246             $self->rdf_triple($e, $listid, RDF_TYPE, RDF_NS.'List');
247             $self->rdf_triple($e, $listid, RDF_NS.'first', $entry_identifier);
248             $last_listid = $listid;
249             }
250             }
251             }
252             if ($complete)
253             {
254             if (defined $last_listid)
255             {
256             $self->rdf_triple($feed, $last_listid, RDF_NS.'rest', RDF_NS.'nil');
257             }
258             else
259             {
260             $self->rdf_triple($feed, $feed_identifier, AX_NS.'entry-list', RDF_NS.'nil');
261             }
262             }
263            
264             # icon and logo
265             foreach my $role (qw(icon logo))
266             {
267             my @elems = $feed->getChildrenByTagNameNS(ATOM_NS, $role);
268             foreach my $e (@elems)
269             {
270             my $img = $self->uri($e->textContent, $e);
271             $self->rdf_triple($e, $feed_identifier, AWOL_NS.$role, $img);
272             $self->rdf_triple($e, $img, RDF_TYPE, FOAF_NS.'Image');
273             }
274             }
275              
276             # generator
277             {
278             my @elems = $feed->getChildrenByTagNameNS(ATOM_NS, 'generator');
279             foreach my $e (@elems)
280             {
281             my $gen_identifier = $self->consume_generator($e);
282             $self->rdf_triple($e, $feed_identifier, AWOL_NS.'generator', $gen_identifier);
283             }
284             }
285            
286             # subtitle
287             {
288             my @elems = $feed->getChildrenByTagNameNS(ATOM_NS, 'subtitle');
289             foreach my $e (@elems)
290             {
291             my $content_identifier = $self->consume_textconstruct($e);
292             $self->rdf_triple($e, $feed_identifier, AWOL_NS.'subtitle', $content_identifier);
293             }
294             }
295              
296             return $feed_identifier;
297             }
298              
299             sub consume_entry
300             {
301             my $self = shift;
302             my $entry = shift;
303            
304             # Entry
305             my $entry_identifier = $self->bnode($entry);
306             $self->rdf_triple($entry, $entry_identifier, RDF_TYPE, AWOL_NS.'Entry');
307              
308             # Common stuff
309             $self->consume_feed_or_entry($entry, $entry_identifier);
310            
311             # published
312             {
313             my @elems = $entry->getChildrenByTagNameNS(ATOM_NS, 'published');
314             foreach my $e (@elems)
315             {
316             $self->rdf_triple_literal($e, $entry_identifier, AWOL_NS.'published', $e->textContent, XSD_NS.'dateTime');
317             }
318             }
319              
320             # summary
321             {
322             my @elems = $entry->getChildrenByTagNameNS(ATOM_NS, 'content');
323             foreach my $e (@elems)
324             {
325             my $content_identifier = $self->consume_content($e);
326             $self->rdf_triple($e, $entry_identifier, AWOL_NS.'content', $content_identifier);
327             }
328             }
329            
330             # source
331             {
332             my @elems = $entry->getChildrenByTagNameNS(ATOM_NS, 'source');
333             foreach my $e (@elems)
334             {
335             my $feed_identifier = $self->consume_feed($e, 1);
336             $self->rdf_triple($e, $entry_identifier, AWOL_NS.'source', $feed_identifier);
337             }
338             }
339              
340             # summary
341             {
342             my @elems = $entry->getChildrenByTagNameNS(ATOM_NS, 'summary');
343             foreach my $e (@elems)
344             {
345             my $content_identifier = $self->consume_textconstruct($e);
346             $self->rdf_triple($e, $entry_identifier, AWOL_NS.'summary', $content_identifier);
347             }
348             }
349              
350             # thr:in-reply-to
351             {
352             my @elems = $entry->getChildrenByTagNameNS(THR_NS, 'in-reply-to');
353             foreach my $e (@elems)
354             {
355             my $irt_id = $self->consume_inreplyto($e);
356             $self->rdf_triple($e, $entry_identifier, AX_NS.'in-reply-to', $irt_id);
357             }
358             }
359              
360             # thr:total
361             {
362             my @elems = $entry->getChildrenByTagNameNS(THR_NS, 'total');
363             foreach my $e (@elems)
364             {
365             my $total = $e->textContent;
366             $self->rdf_triple_literal($e, $entry_identifier, AX_NS.'total', $total, XSD_NS.'integer');
367             }
368             }
369            
370             return $entry_identifier;
371             }
372              
373             sub consume_feed_or_entry
374             {
375             my $self = shift;
376             my $fore = shift;
377             my $id = shift;
378            
379             my @elems = $fore->getChildrenByTagNameNS(ATOM_NS, 'id');
380             foreach my $e (@elems)
381             {
382             my $_id = $self->uri($e->textContent, $e);
383             $self->rdf_triple_literal($e, $id, AWOL_NS.'id', $_id, XSD_NS.'anyURI');
384             }
385            
386             my $is_as = 0;
387            
388             # activitystreams:object, activitystreams:target
389             foreach my $role (qw(object target))
390             {
391             my @elems = $fore->getChildrenByTagNameNS(AS_NS, $role);
392             foreach my $e (@elems)
393             {
394             $is_as++;
395             my $obj_id = $self->consume_entry($e, $id);
396             $self->rdf_triple($e, $id, AAIR_NS.'activity'.ucfirst($role), $obj_id);
397             }
398             }
399              
400             # activitystreams:verb
401             {
402             my @elems = $fore->getChildrenByTagNameNS(AS_NS, 'verb');
403             foreach my $e (@elems)
404             {
405             $is_as++;
406             my $url = $e->textContent;
407             $url =~ s/(^\s*)|(\s*$)//g;
408             $url = url $url, 'http://activitystrea.ms/schema/1.0/';
409             $self->rdf_triple($e, $id, AAIR_NS.'activityVerb', "$url");
410             }
411             if ($is_as && !@elems)
412             {
413             $self->rdf_triple($fore, $id, AAIR_NS.'activityVerb', "http://activitystrea.ms/schema/1.0/post");
414             }
415             }
416              
417             # activitystreams:object-type
418             {
419             my @elems = $fore->getChildrenByTagNameNS(AS_NS, 'object-type');
420             foreach my $e (@elems)
421             {
422             my $url = $e->textContent;
423             $url =~ s/(^\s*)|(\s*$)//g;
424             $url = url $url, 'http://activitystrea.ms/schema/1.0/';
425             $self->rdf_triple($e, $id, RDF_NS.'type', "$url");
426             }
427             }
428              
429             # authors and contributors
430             foreach my $role (qw(author contributor))
431             {
432             my @elems = $fore->getChildrenByTagNameNS(ATOM_NS, $role);
433             foreach my $e (@elems)
434             {
435             my $person_identifier = $self->consume_person($e);
436             $self->rdf_triple($e, $id, AWOL_NS.$role, $person_identifier);
437            
438             if ($role eq 'author' and $is_as)
439             {
440             $self->rdf_triple($e, $person_identifier, RDF_NS.'type', AAIR_NS.'Actor');
441             $self->rdf_triple($e, $id, AAIR_NS.'activityActor', $person_identifier);
442             }
443             }
444             }
445              
446             # updated
447             {
448             my @elems = $fore->getChildrenByTagNameNS(ATOM_NS, 'updated');
449             foreach my $e (@elems)
450             {
451             $self->rdf_triple_literal($e, $id, AWOL_NS.'updated', $e->textContent, XSD_NS.'dateTime');
452             }
453             }
454              
455             # link
456             {
457             my @elems = $fore->getChildrenByTagNameNS(ATOM_NS, 'link');
458             foreach my $e (@elems)
459             {
460             my $link_identifier = $self->consume_link($e, $id);
461             $self->rdf_triple($e, $id, AWOL_NS.'link', $link_identifier);
462             }
463             }
464              
465             # title and rights
466             foreach my $role (qw(title rights))
467             {
468             my @elems = $fore->getChildrenByTagNameNS(ATOM_NS, $role);
469             foreach my $e (@elems)
470             {
471             my $content_identifier = $self->consume_textconstruct($e);
472             $self->rdf_triple($e, $id, AWOL_NS.$role, $content_identifier);
473             }
474             }
475            
476             # category
477             {
478             my @elems = $fore->getChildrenByTagNameNS(ATOM_NS, 'category');
479             foreach my $e (@elems)
480             {
481             my $cat_identifier = $self->consume_category($e, $id);
482             $self->rdf_triple($e, $id, AWOL_NS.'category', $cat_identifier);
483             }
484             }
485              
486             # Unknown Extensions!
487             {
488             my @elems = $fore->getChildrenByTagName('*');
489             foreach my $e (@elems)
490             {
491             next if $e->namespaceURI eq ATOM_NS;
492             next if $e->namespaceURI eq AS_NS;
493             next if $e->namespaceURI eq FH_NS;
494             next if $e->namespaceURI eq THR_NS;
495            
496             my $xml = $self->xmlify_inclusive($e);
497             $self->rdf_triple_literal($e, $id, AX_NS.'extension-element', $xml, RDF_NS.'XMLLiteral');
498             }
499             }
500              
501             return $id;
502             }
503              
504             sub consume_textconstruct
505             {
506             my $self = shift;
507             my $elem = shift;
508            
509             my $id = $self->bnode($elem);
510             $self->rdf_triple($elem, $id, RDF_TYPE, AWOL_NS.'TextContent');
511            
512             my $lang = $self->get_node_lang($elem);
513            
514             if (lc $elem->getAttribute('type') eq 'xhtml')
515             {
516             my $cnt = $self->xmlify($elem, $lang);
517             $self->rdf_triple_literal($elem, $id, AWOL_NS.'xhtml', $cnt, RDF_NS.'XMLLiteral');
518             }
519              
520             elsif (lc $elem->getAttribute('type') eq 'html')
521             {
522             my $cnt = $elem->textContent;
523             $self->rdf_triple_literal($elem, $id, AWOL_NS.'html', $cnt, undef, $lang);
524             }
525              
526             else
527             {
528             my $cnt = $elem->textContent;
529             $self->rdf_triple_literal($elem, $id, AWOL_NS.'text', $cnt, undef, $lang);
530             }
531            
532             return $id;
533             }
534              
535             sub consume_content
536             {
537             my $self = shift;
538             my $elem = shift;
539            
540             my $id = $self->bnode($elem);
541             $self->rdf_triple($elem, $id, RDF_TYPE, AWOL_NS.'Content');
542            
543             my $lang = $self->get_node_lang($elem);
544             my $base = $self->get_node_base($elem);
545            
546             if ($elem->hasAttribute('src'))
547             {
548             my $link = $self->uri($elem->getAttribute('src'), $elem);
549             $self->rdf_triple($elem, $id, AWOL_NS.'src', $link);
550            
551             if ($self->{'options'}->{'no_fetch_content_src'})
552             {
553             $self->rdf_triple_literal($elem, $id, AWOL_NS.'type', $elem->getAttribute('type'))
554             if $elem->hasAttribute('type');
555             }
556             else
557             {
558             my $ua = LWP::UserAgent->new;
559             $ua->agent(sprintf('%s/%s ', __PACKAGE__, $VERSION));
560             if ($elem->hasAttribute('type'))
561             {
562             $ua->default_header("Accept" => $elem->getAttribute('type').", */*;q=0.1");
563             }
564             else
565             {
566             $ua->default_header("Accept" => "application/xhtml+xml, text/html, text/plain, */*;q=0.1");
567             }
568             my $response = $ua->get($self->uri($elem->getAttribute('src'), $elem));
569             if ($response->is_success)
570             {
571             $self->rdf_triple_literal($elem, $id, AWOL_NS.'body', $response->decoded_content);
572            
573             if ($response->content_type)
574             { $self->rdf_triple_literal($elem, $id, AWOL_NS.'type', $response->content_type); }
575             elsif ($elem->hasAttribute('type'))
576             { $self->rdf_triple_literal($elem, $id, AWOL_NS.'type', $elem->getAttribute('type')); }
577              
578             if ($response->content_language =~ /^\s*([a-z]{2,3})\b/i)
579             { $self->rdf_triple_literal($elem, $id, AWOL_NS.'lang', lc $1, XSD_NS.'language'); }
580              
581             if ($response->base)
582             { $self->rdf_triple($elem, $id, AWOL_NS.'base', $response->base); }
583             }
584             else
585             {
586             $self->rdf_triple_literal($elem, $id, AWOL_NS.'type', $elem->getAttribute('type'))
587             if $elem->hasAttribute('type');
588             }
589             }
590             }
591            
592             elsif (lc $elem->getAttribute('type') eq 'text' or !$elem->hasAttribute('type'))
593             {
594             my $cnt = $elem->textContent;
595             $self->rdf_triple_literal($elem, $id, AWOL_NS.'body', $cnt, undef, $lang);
596             $self->rdf_triple_literal($elem, $id, AWOL_NS.'type', 'text/plain');
597             $self->rdf_triple_literal($elem, $id, AWOL_NS.'lang', $lang, XSD_NS.'language') if $lang;
598             $self->rdf_triple($elem, $id, AWOL_NS.'base', $base) if $base;
599             }
600            
601             elsif (lc $elem->getAttribute('type') eq 'xhtml')
602             {
603             my $cnt = $self->xmlify($elem, $lang);
604             $self->rdf_triple_literal($elem, $id, AWOL_NS.'body', $cnt, RDF_NS.'XMLLiteral');
605             $self->rdf_triple_literal($elem, $id, AWOL_NS.'type', 'application/xhtml+xml');
606             $self->rdf_triple_literal($elem, $id, AWOL_NS.'lang', $lang, XSD_NS.'language') if $lang;
607             $self->rdf_triple($elem, $id, AWOL_NS.'base', $base) if $base;
608             }
609              
610             elsif (lc $elem->getAttribute('type') eq 'html')
611             {
612             my $cnt = $elem->textContent;
613             $self->rdf_triple_literal($elem, $id, AWOL_NS.'body', $cnt, undef, $lang);
614             $self->rdf_triple_literal($elem, $id, AWOL_NS.'type', 'text/html');
615             $self->rdf_triple_literal($elem, $id, AWOL_NS.'lang', $lang, XSD_NS.'language') if $lang;
616             $self->rdf_triple($elem, $id, AWOL_NS.'base', $base) if $base;
617             }
618              
619             elsif ($elem->getAttribute('type') =~ m'([\+\/]xml)$'i)
620             {
621             my $cnt = $self->xmlify($elem, $lang);
622             $self->rdf_triple_literal($elem, $id, AWOL_NS.'body', $cnt, RDF_NS.'XMLLiteral');
623             $self->rdf_triple_literal($elem, $id, AWOL_NS.'type', $elem->getAttribute('type'));
624             $self->rdf_triple_literal($elem, $id, AWOL_NS.'lang', $lang, XSD_NS.'language') if $lang;
625             $self->rdf_triple($elem, $id, AWOL_NS.'base', $base) if $base;
626             }
627            
628             elsif ($elem->getAttribute('type') =~ m'^text\/'i)
629             {
630             my $cnt = $elem->textContent;
631             $self->rdf_triple_literal($elem, $id, AWOL_NS.'body', $cnt, undef, $lang);
632             $self->rdf_triple_literal($elem, $id, AWOL_NS.'type', $elem->getAttribute('type'));
633             $self->rdf_triple_literal($elem, $id, AWOL_NS.'lang', $lang, XSD_NS.'language') if $lang;
634             $self->rdf_triple($elem, $id, AWOL_NS.'base', $base) if $base;
635             }
636            
637             elsif ($elem->hasAttribute('type'))
638             {
639             my $cnt = $elem->textContent;
640             $self->rdf_triple_literal($elem, $id, AWOL_NS.'body', decode_base64($cnt));
641             $self->rdf_triple_literal($elem, $id, AWOL_NS.'type', $elem->getAttribute('type'));
642             $self->rdf_triple_literal($elem, $id, AWOL_NS.'lang', $lang, XSD_NS.'language') if $lang;
643             $self->rdf_triple($elem, $id, AWOL_NS.'base', $base) if $base;
644             }
645              
646             return $id;
647             }
648              
649             sub consume_person
650             {
651             my $self = shift;
652             my $person = shift;
653            
654             # Person
655             my $person_identifier = $self->bnode($person);
656             $self->rdf_triple($person, $person_identifier, RDF_TYPE, AWOL_NS.'Person');
657            
658             # name
659             {
660             my @elems = $person->getChildrenByTagNameNS(ATOM_NS, 'name');
661             foreach my $e (@elems)
662             {
663             $self->rdf_triple_literal($e, $person_identifier, AWOL_NS.'name', $e->textContent);
664             }
665             }
666              
667             # uri
668             {
669             my @elems = $person->getChildrenByTagNameNS(ATOM_NS, 'uri');
670             foreach my $e (@elems)
671             {
672             my $link = $self->uri($e->textContent, $e);
673             $self->rdf_triple($e, $person_identifier, AWOL_NS.'uri', $link);
674             }
675             }
676              
677             # email
678             {
679             my @elems = $person->getChildrenByTagNameNS(ATOM_NS, 'email');
680             foreach my $e (@elems)
681             {
682             $self->rdf_triple($e, $person_identifier, AWOL_NS.'email', 'mailto:'.$e->textContent);
683             }
684             }
685              
686             return $person_identifier;
687             }
688              
689             sub consume_generator
690             {
691             my $self = shift;
692             my $elem = shift;
693            
694             # Person
695             my $identifier = $self->bnode($elem);
696             $self->rdf_triple($elem, $identifier, RDF_TYPE, AWOL_NS.'Generator');
697            
698             # name
699             {
700             my $lang = $self->get_node_lang($elem);
701             $self->rdf_triple_literal($elem, $identifier, AWOL_NS.'name', $elem->textContent, undef, $lang);
702             }
703              
704             # uri
705             if ($elem->hasAttribute('uri'))
706             {
707             my $link = $self->uri($elem->getAttribute('uri'), $elem);
708             $self->rdf_triple($elem, $identifier, AWOL_NS.'uri', $link);
709             }
710              
711             # version
712             if ($elem->hasAttribute('uri'))
713             {
714             $self->rdf_triple($elem, $identifier, AWOL_NS.'version', $elem->getAttribute('version'));
715             }
716              
717             return $identifier;
718             }
719              
720             sub consume_inreplyto
721             {
722             my $self = shift;
723             my $link = shift;
724            
725             my $id = $self->bnode($link);
726             $self->rdf_triple($link, $id, RDF_TYPE, AWOL_NS.'Entry');
727            
728             if ($link->hasAttribute('ref'))
729             {
730             $self->rdf_triple_literal($link, $id, AWOL_NS.'id', $link->getAttribute('ref'), XSD_NS.'anyURI');
731             }
732            
733             if ($link->hasAttribute('href'))
734             {
735             my $href = $self->uri($link->getAttribute('href'), $link);
736             $self->rdf_triple($link, $id, IANA_NS.'self', $href);
737             }
738            
739             # TODO: "type".
740            
741             if ($link->hasAttribute('source'))
742             {
743             my $fid = $self->bnode;
744             my $href = $self->uri($link->getAttribute('href'), $link);
745             $self->rdf_triple($link, $id, AWOL_NS.'source', $fid);
746             $self->rdf_triple($link, $fid, RDF_TYPE, AWOL_NS.'Feed');
747             $self->rdf_triple($link, $fid, IANA_NS.'self', $href);
748             }
749            
750             return $id;
751             }
752              
753             sub consume_link
754             {
755             my $self = shift;
756             my $link = shift;
757             my $subject = shift || undef;
758            
759             # Link
760             my $link_identifier = $self->bnode($link);
761             $self->rdf_triple($link, $link_identifier, RDF_TYPE, AWOL_NS.'Link');
762              
763             # Destination
764             my $destination_identifier = $self->bnode;
765             $self->rdf_triple($link, $destination_identifier, RDF_TYPE, AWOL_NS.'Content');
766             $self->rdf_triple($link, $link_identifier, AWOL_NS.'to', $destination_identifier);
767              
768             # rel
769             {
770             my $rel = HTTP::Link::Parser::relationship_uri(
771             $link->hasAttribute('rel') ? $link->getAttribute('rel') : 'alternate');
772             $self->rdf_triple($link, $link_identifier, AWOL_NS.'rel', $rel);
773            
774             if ($link->hasAttribute('href') and defined $subject)
775             {
776             my $href = $self->uri($link->getAttribute('href'), $link);
777             $self->rdf_triple($link, $subject, $rel, $href);
778             }
779             }
780            
781             # href
782             if ($link->hasAttribute('href'))
783             {
784             my $href = $self->uri($link->getAttribute('href'), $link);
785             $self->rdf_triple($link, $destination_identifier, AWOL_NS.'src', $href);
786             }
787              
788             # hreflang
789             if ($link->hasAttribute('hreflang'))
790             {
791             my $hreflang = $link->getAttribute('hreflang');
792             $self->rdf_triple_literal($link, $destination_identifier, AWOL_NS.'lang', $hreflang);
793             }
794              
795             # length
796             if ($link->hasAttribute('length'))
797             {
798             my $length = $link->getAttribute('length');
799             $self->rdf_triple_literal($link, $destination_identifier, AWOL_NS.'length', $length, XSD_NS.'integer');
800             }
801              
802             # type
803             if ($link->hasAttribute('type'))
804             {
805             my $type = $link->getAttribute('type');
806             $self->rdf_triple_literal($link, $destination_identifier, AWOL_NS.'type', $type);
807             }
808              
809             # title: TODO - check this uses AWOL properly.
810             if ($link->hasAttribute('title'))
811             {
812             my $lang = $self->get_node_lang($link);
813             my $title = $link->getAttribute('title');
814             $self->rdf_triple_literal($link, $link_identifier, AWOL_NS.'title', $title, undef, $lang);
815             }
816              
817             # thr:count
818             if ($link->hasAttributeNS(THR_NS, 'count'))
819             {
820             my $count = $link->getAttributeNS(THR_NS, 'count');
821             $self->rdf_triple_literal($link, $link_identifier, AX_NS.'count', $count, XSD_NS.'integer');
822             }
823              
824             # thr:updated
825             if ($link->hasAttributeNS(THR_NS, 'updated'))
826             {
827             my $u = $link->getAttributeNS(THR_NS, 'updated');
828             $self->rdf_triple_literal($link, $link_identifier, AX_NS.'updated', $u, XSD_NS.'dateTime');
829             }
830              
831             return $link_identifier;
832             }
833              
834             sub consume_category
835             {
836             my $self = shift;
837             my $elem = shift;
838            
839             # Link
840             my $id = $self->bnode($elem);
841             $self->rdf_triple($elem, $id, RDF_TYPE, AWOL_NS.'Category');
842              
843             # term
844             if ($elem->hasAttribute('term'))
845             {
846             $self->rdf_triple_literal($elem, $id, AWOL_NS.'term', $elem->getAttribute('term'));
847             }
848            
849             # label
850             if ($elem->hasAttribute('label'))
851             {
852             my $lang = $self->get_node_lang($elem);
853             $self->rdf_triple_literal($elem, $id, AWOL_NS.'label', $elem->getAttribute('label'), undef, $lang);
854             }
855              
856             # scheme
857             if ($elem->hasAttribute('scheme'))
858             {
859             my $link = $self->uri($elem->getAttribute('scheme'), $elem);
860             $self->rdf_triple($elem, $id, AWOL_NS.'scheme', $link);
861             }
862              
863             return $id;
864             }
865              
866             sub xmlify
867             # Function only used internally.
868             {
869             my $this = shift;
870             my $dom = shift;
871             my $lang = shift;
872             my $rv;
873              
874             $lang = $this->get_node_lang($dom)
875             unless $lang;
876              
877             foreach my $kid ($dom->childNodes)
878             {
879             my $fakelang = 0;
880             if (($kid->nodeType == XML_ELEMENT_NODE) && defined $lang)
881             {
882             unless ($kid->hasAttributeNS(XML_XML_NS, 'lang'))
883             {
884             $kid->setAttributeNS(XML_XML_NS, 'lang', $lang);
885             $fakelang++;
886             }
887             }
888            
889             $rv .= $kid->toStringEC14N(1);
890            
891             if ($fakelang)
892             {
893             $kid->removeAttributeNS(XML_XML_NS, 'lang');
894             }
895             }
896            
897             return $rv;
898             }
899              
900              
901             sub xmlify_inclusive
902             # Function only used internally.
903             {
904             my $this = shift;
905             my $dom = shift;
906             my $lang = shift;
907             my $rv;
908            
909             $lang = $this->get_node_lang($dom)
910             unless $lang;
911            
912             my $fakelang = 0;
913             if (($dom->nodeType == XML_ELEMENT_NODE) && defined $lang)
914             {
915             unless ($dom->hasAttributeNS(XML_XML_NS, 'lang'))
916             {
917             $dom->setAttributeNS(XML_XML_NS, 'lang', $lang);
918             $fakelang++;
919             }
920             }
921            
922             $rv = $dom->toStringEC14N(1);
923            
924             if ($fakelang)
925             {
926             $dom->removeAttributeNS(XML_XML_NS, 'lang');
927             }
928            
929             return $rv;
930             }
931              
932             sub get_node_lang
933             {
934             my $this = shift;
935             my $node = shift;
936              
937             if ($node->hasAttributeNS(XML_XML_NS, 'lang'))
938             {
939             return valid_lang($node->getAttributeNS(XML_XML_NS, 'lang')) ?
940             $node->getAttributeNS(XML_XML_NS, 'lang'):
941             undef;
942             }
943              
944             if ($node != $this->{'DOM'}->documentElement
945             && defined $node->parentNode
946             && $node->parentNode->nodeType == XML_ELEMENT_NODE)
947             {
948             return $this->get_node_lang($node->parentNode);
949             }
950            
951             return undef;
952             }
953              
954             sub get_node_base
955             {
956             my $this = shift;
957             my $node = shift;
958              
959             my @base;
960              
961             while (1)
962             {
963             push @base, $node->getAttributeNS(XML_XML_NS, 'base')
964             if $node->hasAttributeNS(XML_XML_NS, 'base');
965            
966             $node = $node->parentNode;
967             last unless blessed($node) && $node->isa('XML::LibXML::Element');
968             }
969            
970             my $rv = url $this->uri; # document URI.
971            
972             while (my $b = pop @base)
973             {
974             $rv = url $b, $rv->abs->as_string;
975             }
976            
977             return $rv->abs->as_string;
978             }
979              
980             sub rdf_triple
981             # Function only used internally.
982             {
983             my $this = shift;
984              
985             my $suppress_triple = 0;
986             $suppress_triple = $this->{'sub'}->{'pretriple_resource'}($this, @_)
987             if defined $this->{'sub'}->{'pretriple_resource'};
988             return if $suppress_triple;
989            
990             my $element = shift; # A reference to the XML::LibXML element being parsed
991             my $subject = shift; # Subject URI or bnode
992             my $predicate = shift; # Predicate URI
993             my $object = shift; # Resource URI or bnode
994             my $graph = shift; # Graph URI or bnode (if named graphs feature is enabled)
995              
996             # First make sure the object node type is ok.
997             my $to;
998             if ($object =~ m/^_:(.*)/)
999             {
1000             $to = RDF::Trine::Node::Blank->new($1);
1001             }
1002             else
1003             {
1004             $to = RDF::Trine::Node::Resource->new($object);
1005             }
1006              
1007             # Run the common function
1008             return $this->rdf_triple_common($element, $subject, $predicate, $to, $graph);
1009             }
1010              
1011             sub rdf_triple_literal
1012             # Function only used internally.
1013             {
1014             my $this = shift;
1015              
1016             my $suppress_triple = 0;
1017             $suppress_triple = $this->{'sub'}->{'pretriple_literal'}($this, @_)
1018             if defined $this->{'sub'}->{'pretriple_literal'};
1019             return if $suppress_triple;
1020              
1021             my $element = shift; # A reference to the XML::LibXML element being parsed
1022             my $subject = shift; # Subject URI or bnode
1023             my $predicate = shift; # Predicate URI
1024             my $object = shift; # Resource Literal
1025             my $datatype = shift; # Datatype URI (possibly undef or '')
1026             my $language = shift; # Language (possibly undef or '')
1027             my $graph = shift; # Graph URI or bnode (if named graphs feature is enabled)
1028              
1029             # Now we know there's a literal
1030             my $to;
1031            
1032             # Work around bad Unicode handling in RDF::Trine.
1033             $object = encode_utf8($object);
1034              
1035             if (defined $datatype)
1036             {
1037             if ($datatype eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral')
1038             {
1039             if ($this->{'options'}->{'use_rtnlx'})
1040             {
1041             eval
1042             {
1043             require RDF::Trine::Node::Literal::XML;
1044             $to = RDF::Trine::Node::Literal::XML->new($element->childNodes);
1045             };
1046             }
1047            
1048             if ( $@ || !defined $to)
1049             {
1050             my $orig = $RDF::Trine::Node::Literal::USE_XMLLITERALS;
1051             $RDF::Trine::Node::Literal::USE_XMLLITERALS = 0;
1052             $to = RDF::Trine::Node::Literal->new($object, undef, $datatype);
1053             $RDF::Trine::Node::Literal::USE_XMLLITERALS = $orig;
1054             }
1055             }
1056             else
1057             {
1058             $to = RDF::Trine::Node::Literal->new($object, undef, $datatype);
1059             }
1060             }
1061             else
1062             {
1063             $to = RDF::Trine::Node::Literal->new($object, $language, undef);
1064             }
1065              
1066             # Run the common function
1067             $this->rdf_triple_common($element, $subject, $predicate, $to, $graph);
1068             }
1069              
1070             sub rdf_triple_common
1071             # Function only used internally.
1072             {
1073             my $this = shift; # A reference to the Parser object
1074             my $element = shift; # A reference to the XML::LibXML element being parsed
1075             my $subject = shift; # Subject URI or bnode
1076             my $predicate = shift; # Predicate URI
1077             my $to = shift; # RDF::Trine::Node Resource URI or bnode
1078             my $graph = shift; # Graph URI or bnode (if named graphs feature is enabled)
1079              
1080             # First, make sure subject and predicates are the right kind of nodes
1081             my $tp = RDF::Trine::Node::Resource->new($predicate);
1082             my $ts;
1083             if ($subject =~ m/^_:(.*)/)
1084             {
1085             $ts = RDF::Trine::Node::Blank->new($1);
1086             }
1087             else
1088             {
1089             $ts = RDF::Trine::Node::Resource->new($subject);
1090             }
1091              
1092             my $statement;
1093              
1094             # If we are configured for it, and graph name can be found, add it.
1095             if (ref($this->{'options'}->{'named_graphs'}) && ($graph))
1096             {
1097             $this->{Graphs}->{$graph}++;
1098            
1099             my $tg;
1100             if ($graph =~ m/^_:(.*)/)
1101             {
1102             $tg = RDF::Trine::Node::Blank->new($1);
1103             }
1104             else
1105             {
1106             $tg = RDF::Trine::Node::Resource->new($graph);
1107             }
1108              
1109             $statement = RDF::Trine::Statement::Quad->new($ts, $tp, $to, $tg);
1110             }
1111             else
1112             {
1113             $statement = RDF::Trine::Statement->new($ts, $tp, $to);
1114             }
1115              
1116             my $suppress_triple = 0;
1117             $suppress_triple = $this->{'sub'}->{'ontriple'}($this, $element, $statement)
1118             if ($this->{'sub'}->{'ontriple'});
1119             return if $suppress_triple;
1120              
1121             $this->{RESULTS}->add_statement($statement);
1122             }
1123              
1124             sub bnode
1125             # Function only used internally.
1126             {
1127             my $this = shift;
1128             my $element = shift;
1129            
1130             if (defined $this->{'bnode_generator'})
1131             {
1132             return $this->{'bnode_generator'}->bnode($element);
1133             }
1134            
1135             return sprintf('_:AwolAutoNode%03d', $this->{bnodes}++);
1136             }
1137              
1138             sub valid_lang
1139             {
1140             my $value_to_test = shift;
1141              
1142             return 1 if (defined $value_to_test) && ($value_to_test eq '');
1143             return 0 unless defined $value_to_test;
1144            
1145             # Regex for recognizing RFC 4646 well-formed tags
1146             # http://www.rfc-editor.org/rfc/rfc4646.txt
1147             # http://tools.ietf.org/html/draft-ietf-ltru-4646bis-21
1148              
1149             # The structure requires no forward references, so it reverses the order.
1150             # It uses Java/Perl syntax instead of the old ABNF
1151             # The uppercase comments are fragments copied from RFC 4646
1152              
1153             # Note: the tool requires that any real "=" or "#" or ";" in the regex be escaped.
1154              
1155             my $alpha = '[a-z]'; # ALPHA
1156             my $digit = '[0-9]'; # DIGIT
1157             my $alphanum = '[a-z0-9]'; # ALPHA / DIGIT
1158             my $x = 'x'; # private use singleton
1159             my $singleton = '[a-wyz]'; # other singleton
1160             my $s = '[_-]'; # separator -- lenient parsers will use [_-] -- strict will use [-]
1161              
1162             # Now do the components. The structure is slightly different to allow for capturing the right components.
1163             # The notation (?:....) is a non-capturing version of (...): so the "?:" can be deleted if someone doesn't care about capturing.
1164              
1165             my $language = '([a-z]{2,8}) | ([a-z]{2,3} $s [a-z]{3})';
1166            
1167             # ABNF (2*3ALPHA) / 4ALPHA / 5*8ALPHA --- note: because of how | works in regex, don't use $alpha{2,3} | $alpha{4,8}
1168             # We don't have to have the general case of extlang, because there can be only one extlang (except for zh-min-nan).
1169              
1170             # Note: extlang invalid in Unicode language tags
1171              
1172             my $script = '[a-z]{4}' ; # 4ALPHA
1173              
1174             my $region = '(?: [a-z]{2}|[0-9]{3})' ; # 2ALPHA / 3DIGIT
1175              
1176             my $variant = '(?: [a-z0-9]{5,8} | [0-9] [a-z0-9]{3} )' ; # 5*8alphanum / (DIGIT 3alphanum)
1177              
1178             my $extension = '(?: [a-wyz] (?: [_-] [a-z0-9]{2,8} )+ )' ; # singleton 1*("-" (2*8alphanum))
1179              
1180             my $privateUse = '(?: x (?: [_-] [a-z0-9]{1,8} )+ )' ; # "x" 1*("-" (1*8alphanum))
1181              
1182             # Define certain grandfathered codes, since otherwise the regex is pretty useless.
1183             # Since these are limited, this is safe even later changes to the registry --
1184             # the only oddity is that it might change the type of the tag, and thus
1185             # the results from the capturing groups.
1186             # http://www.iana.org/assignments/language-subtag-registry
1187             # Note that these have to be compared case insensitively, requiring (?i) below.
1188              
1189             my $grandfathered = '(?:
1190             (en [_-] GB [_-] oed)
1191             | (i [_-] (?: ami | bnn | default | enochian | hak | klingon | lux | mingo | navajo | pwn | tao | tay | tsu ))
1192             | (no [_-] (?: bok | nyn ))
1193             | (sgn [_-] (?: BE [_-] (?: fr | nl) | CH [_-] de ))
1194             | (zh [_-] min [_-] nan)
1195             )';
1196              
1197             # old: | zh $s (?: cmn (?: $s Hans | $s Hant )? | gan | min (?: $s nan)? | wuu | yue );
1198             # For well-formedness, we don't need the ones that would otherwise pass.
1199             # For validity, they need to be checked.
1200              
1201             # $grandfatheredWellFormed = (?:
1202             # art $s lojban
1203             # | cel $s gaulish
1204             # | zh $s (?: guoyu | hakka | xiang )
1205             # );
1206              
1207             # Unicode locales: but we are shifting to a compatible form
1208             # $keyvalue = (?: $alphanum+ \= $alphanum+);
1209             # $keywords = ($keyvalue (?: \; $keyvalue)*);
1210              
1211             # We separate items that we want to capture as a single group
1212              
1213             my $variantList = $variant . '(?:' . $s . $variant . ')*' ; # special for multiples
1214             my $extensionList = $extension . '(?:' . $s . $extension . ')*' ; # special for multiples
1215              
1216             my $langtag = "
1217             ($language)
1218             ($s ( $script ) )?
1219             ($s ( $region ) )?
1220             ($s ( $variantList ) )?
1221             ($s ( $extensionList ) )?
1222             ($s ( $privateUse ) )?
1223             ";
1224              
1225             # Here is the final breakdown, with capturing groups for each of these components
1226             # The variants, extensions, grandfathered, and private-use may have interior '-'
1227            
1228             my $r = ($value_to_test =~
1229             /^(
1230             ($langtag)
1231             | ($privateUse)
1232             | ($grandfathered)
1233             )$/xi);
1234             return $r;
1235             }
1236              
1237             'A man, a plan, a canal: Panama'; # E, r u true?
1238              
1239             __END__
1240              
1241             =head1 NAME
1242              
1243             XML::Atom::OWL - parse an Atom file into RDF
1244              
1245             =head1 SYNOPSIS
1246              
1247             use XML::Atom::OWL;
1248            
1249             $parser = XML::Atom::OWL->new($xml, $baseuri);
1250             $graph = $parser->graph;
1251              
1252             =head1 DESCRIPTION
1253              
1254             This has a pretty similar interface to L<RDF::RDFa::Parser>.
1255              
1256             =head2 Constructor
1257              
1258             =over 4
1259              
1260             =item C<< new($xml, $baseuri, \%options, $storage) >>
1261              
1262             This method creates a new XML::Atom::OWL object and returns it.
1263              
1264             The $xml variable may contain an XML (Atom) string, or an
1265             L<XML::LibXML::Document> object. If a string, the document is parsed
1266             using L<XML::LibXML>, which will throw an exception if it is not
1267             well-formed. XML::Atom::OWL does not catch the exception.
1268              
1269             The base URI is used to resolve relative URIs found in the document.
1270              
1271             Currently only one option is defined, 'no_fetch_content_src', a boolean
1272             indicating whether <content src> URLs should be automatically fetched
1273             and added to the model as if inline content had been provided. They are
1274             fetched by default, but it's pretty rare for feeds to include this attribute.
1275              
1276             $storage is an RDF::Trine::Storage object. If undef, then a new
1277             temporary store is created.
1278              
1279             =back
1280              
1281             =head2 Public Methods
1282              
1283             =over 4
1284              
1285             =item C<< uri >>
1286              
1287             Returns the base URI of the document being parsed. This will usually be the
1288             same as the base URI provided to the constructor.
1289              
1290             Optionally it may be passed a parameter - an absolute or relative URI - in
1291             which case it returns the same URI which it was passed as a parameter, but
1292             as an absolute URI, resolved relative to the document's base URI.
1293              
1294             This seems like two unrelated functions, but if you consider the consequence
1295             of passing a relative URI consisting of a zero-length string, it in fact makes
1296             sense.
1297              
1298             =item C<< dom >>
1299              
1300             Returns the parsed XML::LibXML::Document.
1301              
1302             =item C<< graph >>
1303              
1304             This method will return an RDF::Trine::Model object with all
1305             statements of the full graph.
1306              
1307             This method automatically calls C<consume>.
1308              
1309             =item C<< root_identifier >>
1310              
1311             Returns the blank node or URI for the root element of the Atom
1312             document as an RDF::Trine::Node
1313              
1314             Calls C<consume> automatically.
1315              
1316             =item C<< set_callbacks(\%callbacks) >>
1317              
1318             Set callback functions for the parser to call on certain events. These are only necessary if
1319             you want to do something especially unusual.
1320              
1321             $p->set_callbacks({
1322             'pretriple_resource' => sub { ... } ,
1323             'pretriple_literal' => sub { ... } ,
1324             'ontriple' => undef ,
1325             });
1326              
1327             For details of the callback functions, see the section CALLBACKS. C<set_callbacks> must
1328             be used I<before> C<consume>. C<set_callbacks> itself returns a reference to the parser
1329             object itself.
1330              
1331             =item C<< consume >>
1332              
1333             The document is parsed. Triples extracted from the document are passed
1334             to the callbacks as each one is found; triples are made available in the
1335             model returned by the C<graph> method.
1336              
1337             This function returns the parser object itself, making it easy to
1338             abbreviate several of XML::Atom::OWL's functions:
1339              
1340             my $iterator = XML::Atom::OWL->new(undef, $uri)
1341             ->consume->graph->as_stream;
1342              
1343             You probably only need to call this explicitly if you're using callbacks.
1344              
1345             =back
1346              
1347             =head1 CALLBACKS
1348              
1349             Several callback functions are provided. These may be set using the C<set_callbacks> function,
1350             which taskes a hashref of keys pointing to coderefs. The keys are named for the event to fire the
1351             callback on.
1352              
1353             =head2 pretriple_resource
1354              
1355             This is called when a triple has been found, but before preparing the triple for
1356             adding to the model. It is only called for triples with a non-literal object value.
1357              
1358             The parameters passed to the callback function are:
1359              
1360             =over 4
1361              
1362             =item * A reference to the C<XML::Atom::OWL> object
1363              
1364             =item * A reference to the C<XML::LibXML::Element> being parsed
1365              
1366             =item * Subject URI or bnode (string)
1367              
1368             =item * Predicate URI (string)
1369              
1370             =item * Object URI or bnode (string)
1371              
1372             =item * Graph URI or bnode (string or undef)
1373              
1374             =back
1375              
1376             The callback should return 1 to tell the parser to skip this triple (not add it to
1377             the graph); return 0 otherwise.
1378              
1379             =head2 pretriple_literal
1380              
1381             This is the equivalent of pretriple_resource, but is only called for triples with a
1382             literal object value.
1383              
1384             The parameters passed to the callback function are:
1385              
1386             =over 4
1387              
1388             =item * A reference to the C<XML::Atom::OWL> object
1389              
1390             =item * A reference to the C<XML::LibXML::Element> being parsed
1391              
1392             =item * Subject URI or bnode (string)
1393              
1394             =item * Predicate URI (string)
1395              
1396             =item * Object literal (string)
1397              
1398             =item * Datatype URI (string or undef)
1399              
1400             =item * Language (string or undef)
1401              
1402             =item * Graph URI or bnode (string or undef)
1403              
1404             =back
1405              
1406             Beware: sometimes both a datatype I<and> a language will be passed.
1407             This goes beyond the normal RDF data model.)
1408              
1409             The callback should return 1 to tell the parser to skip this triple (not add it to
1410             the graph); return 0 otherwise.
1411              
1412             =head2 ontriple
1413              
1414             This is called once a triple is ready to be added to the graph. (After the pretriple
1415             callbacks.) The parameters passed to the callback function are:
1416              
1417             =over 4
1418              
1419             =item * A reference to the C<XML::Atom::OWL> object
1420              
1421             =item * A reference to the C<XML::LibXML::Element> being parsed
1422              
1423             =item * An RDF::Trine::Statement object.
1424              
1425             =back
1426              
1427             The callback should return 1 to tell the parser to skip this triple (not add it to
1428             the graph); return 0 otherwise. The callback may modify the RDF::Trine::Statement
1429             object.
1430              
1431             =head1 BUGS
1432              
1433             Please report any bugs to L<http://rt.cpan.org/>.
1434              
1435             =head1 SEE ALSO
1436              
1437             L<RDF::Trine>, L<XML::Atom::FromOWL>.
1438              
1439             L<http://www.perlrdf.org/>.
1440              
1441             =head1 AUTHOR
1442              
1443             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
1444              
1445             =head1 COPYRIGHT AND LICENCE
1446              
1447             Copyright 2010-2011 Toby Inkster
1448              
1449             This library is free software; you can redistribute it and/or modify it
1450             under the same terms as Perl itself.
1451              
1452             =head1 DISCLAIMER OF WARRANTIES
1453              
1454             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
1455             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
1456             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1457