File Coverage

blib/lib/XML/NewsML_G2/Writer.pm
Criterion Covered Total %
statement 152 158 96.2
branch 37 46 80.4
condition 6 9 66.6
subroutine 23 24 95.8
pod 2 3 66.6
total 220 240 91.6


line stmt bran cond sub pod time code
1             package XML::NewsML_G2::Writer;
2              
3 18     18   9950 use Carp;
  18         49  
  18         1087  
4 18     18   112 use Moose;
  18         40  
  18         104  
5 18     18   108114 use Moose::Util;
  18         44  
  18         142  
6 18     18   10234 use DateTime;
  18         3376894  
  18         603  
7 18     18   4106 use DateTime::Format::XSD;
  18         655870  
  18         638  
8 18     18   145 use XML::NewsML_G2::Scheme;
  18         53  
  18         544  
9 18     18   99 use XML::NewsML_G2::Scheme_Manager;
  18         51  
  18         459  
10 18     18   98 use namespace::autoclean;
  18         42  
  18         135  
11              
12             has '_root_item', is => 'ro', lazy => 1, builder => '_build__root_item';
13              
14             has 'encoding', isa => 'Str', is => 'ro', default => 'utf-8';
15              
16             has 'scheme_manager',
17             isa => 'XML::NewsML_G2::Scheme_Manager',
18             is => 'ro',
19             lazy => 1,
20             builder => '_build_scheme_manager';
21             has 'doc',
22             isa => 'XML::LibXML::Document',
23             is => 'ro',
24             lazy => 1,
25             builder => '_build_doc';
26             has '_formatter', is => 'ro', default => sub { DateTime::Format::XSD->new() };
27              
28             has 'g2_ns',
29             isa => 'Str',
30             is => 'ro',
31             default => 'http://iptc.org/std/nar/2006-10-01/';
32             has 'xhtml_ns',
33             isa => 'Str',
34             is => 'ro',
35             default => 'http://www.w3.org/1999/xhtml';
36              
37             has 'g2_version', isa => 'Str', is => 'ro', default => '2.18';
38             has '_root_node_name', isa => 'Str', is => 'ro', default => 'newsItem';
39             has '_nature_qcode_prefix', isa => 'Str', is => 'ro', default => 'ninat';
40             has 'generator_version',
41             is => 'Str',
42             is => 'ro',
43             lazy => 1,
44             builder => '_build_generator_version';
45              
46             # attributes set by version-specific role
47             has 'schema_location', isa => 'Str', is => 'ro';
48             has 'g2_catalog_url', isa => 'Str', is => 'ro';
49             has 'g2_catalog_schemes',
50             isa => 'HashRef',
51             is => 'ro',
52             lazy => 1,
53             builder => '_build_g2_catalog_schemes';
54              
55             # builders
56              
57             sub _build__root_item {
58 0     0   0 croak 'Override in subclass';
59             }
60              
61             sub _build_g2_catalog_schemes {
62             return {
63 81     81   1868 isrol => undef,
64             nprov => undef,
65             cinat => undef,
66             ninat => undef,
67             stat => undef,
68             sig => undef,
69             genre => undef,
70             isin => undef,
71             medtop => undef,
72             crol => undef,
73             drol => undef,
74             pgrmod => undef,
75             iso3166_1a2 => 'iso3166-1a2'
76             };
77             }
78              
79             sub _build_doc {
80 81     81   194 my $self = shift;
81 81         2361 return XML::LibXML->createDocument( '1.0', $self->encoding );
82             }
83              
84             sub _build_scheme_manager {
85 2     2   7 my $self = shift;
86 2         70 return XML::NewsML_G2::Scheme_Manager->new();
87             }
88              
89             sub _build_generator_version {
90 72     72   2655 return XML::NewsML_G2->VERSION;
91             }
92              
93             # Apply roles needed for writing
94             sub BUILD {
95 81     81 0 184 my $self = shift;
96              
97 81         2674 ( my $ni_cls ) = reverse split( '::', $self->_root_item->meta->name );
98 81         3749 my $type_role = sprintf( 'XML::NewsML_G2::Role::Writer::%s', $ni_cls );
99              
100 81         2304 my $g2_version = $self->g2_version;
101 81         386 $g2_version =~ s/\./_/;
102 81         236 my $version_role = 'XML::NewsML_G2::Role::Writer_' . $g2_version;
103              
104 81         499 Moose::Util::apply_all_roles( $self, $type_role, $version_role );
105              
106 81         728000 return;
107             }
108              
109             # DOM creating methods
110              
111             sub _create_creator {
112 207     207   439 my ( $self, $creator ) = @_;
113 207         5488 my $result =
114             $self->create_element( 'creator', _name_text => $creator->name );
115              
116 207 100       9228 if ( $creator->kind ) {
117 46         181 my $rel =
118             $result->appendChild(
119             $self->create_element( 'related', rel => 'crel:isA' ) );
120 46         152 $self->scheme_manager->add_qcode( $rel, 'gyibt', $creator->kind );
121             }
122 207         1627 return $result;
123             }
124              
125             sub _create_root_element {
126 72     72   213 my ($self) = @_;
127 72         2007 my $root =
128             $self->doc->createElementNS( $self->g2_ns, $self->_root_node_name );
129 72         1859 $self->doc->setDocumentElement($root);
130 72         3364 $root->setAttributeNS( 'http://www.w3.org/2001/XMLSchema-instance',
131             'xsi:schemaLocation', $self->schema_location );
132              
133 72         2290 $root->setAttribute( 'standard', 'NewsML-G2' );
134 72         2955 $root->setAttribute( 'standardversion', $self->g2_version );
135 72         832 $root->setAttribute( 'conformance', 'power' );
136 72         2663 $root->setAttribute( 'xml:lang', $self->_root_item->language );
137              
138 72         2477 $root->setAttribute( 'guid', $self->_root_item->guid );
139 72         2350 $root->setAttribute( 'version', $self->_root_item->doc_version );
140 72         700 return $root;
141             }
142              
143             sub _create_catalogs {
144 65     65   556 my ( $self, $root ) = @_;
145              
146 65         2029 my %catalogs = ( $self->g2_catalog_url => 1 );
147              
148 65         146 my $cat;
149 65         1689 foreach my $scheme ( $self->scheme_manager->get_all_schemes() ) {
150 1384 100       39482 if ( my $catalog = $scheme->catalog ) {
    50          
151 968         1852 $catalogs{$catalog} = 1;
152             }
153             elsif ($scheme) {
154 416 100       1372 $root->appendChild( $cat = $self->create_element('catalog') )
155             unless $cat;
156 416         9941 $cat->appendChild(
157             $self->create_element(
158             'scheme',
159             alias => $scheme->alias,
160             uri => $scheme->uri
161             )
162             );
163             }
164             }
165              
166 65         1317 foreach my $url ( sort keys %catalogs ) {
167 108         1487 $root->appendChild(
168             $self->create_element( 'catalogRef', href => $url ) );
169             }
170              
171 65         1593 return;
172             }
173              
174             sub _create_copyright_holder_remoteinfo {
175 26     26   140 my ( $self, $crh ) = @_;
176 26 100       813 if ( my $remote_info = $self->news_item->copyright_holder->remote_info ) {
177 25         54 my %args;
178 25 50       704 $args{reluri} = $remote_info->reluri if $remote_info->reluri;
179 25 50       688 $args{href} = $remote_info->href if $remote_info->href;
180 25 50       160 $crh->appendChild( $self->create_element( 'remoteInfo', %args ) )
181             if keys %args;
182             }
183 26         80 return;
184             }
185              
186       66     sub _create_item_meta_title {
187             }
188              
189             sub _create_teaser {
190 32     32   132 my ( $self, $cm ) = @_;
191              
192 32 50       923 if ( $self->news_item->teaser ) {
193 0         0 $cm->appendChild(
194             my $teaser = $self->create_element(
195             'description', _text => $self->news_item->teaser
196             )
197             );
198 0         0 $self->scheme_manager->add_role( $teaser, 'drol', 'teaser' );
199             }
200 32         449 return;
201             }
202              
203             sub _create_item_meta {
204 72     72   196 my ( $self, $root ) = @_;
205              
206 72         205 my $im = $self->create_element('itemMeta');
207 72         3858 $im->appendChild( my $ic = $self->create_element('itemClass') );
208 72         217 $self->scheme_manager->add_qcode(
209             $ic,
210             $self->_nature_qcode_prefix,
211             $self->_root_item->nature
212             );
213              
214 72         1935 $im->appendChild(
215             my $p = $self->create_element(
216             'provider', _name_text => $self->_root_item->provider
217             )
218             );
219 72         1560 $self->scheme_manager->add_qcode_or_literal( $p, 'nprov',
220             $self->_root_item->provider->qcode );
221 72         2414 $im->appendChild(
222             $self->create_element(
223             'versionCreated',
224             _text => $self->_formatter->format_datetime(
225             DateTime->now( time_zone => $self->_root_item->timezone )
226             )
227             )
228             );
229              
230 72 100       228 if ( $self->_root_item->embargo ) {
231 53         1615 my $e =
232             $self->_formatter->format_datetime( $self->_root_item->embargo );
233 53         10394 $im->appendChild( $self->create_element( 'embargoed', _text => $e ) );
234             }
235              
236 72         246 $im->appendChild( my $ps = $self->create_element('pubStatus') );
237 72         237 $self->scheme_manager->add_qcode( $ps, 'stat',
238             $self->_root_item->doc_status );
239 72         2471 $im->appendChild(
240             $self->create_element(
241             'generator',
242             versioninfo => $self->generator_version,
243             _text => 'XML::NewsML_G2'
244             )
245             );
246 72 100       206 if ( $self->_root_item->has_service ) {
247 53         1377 $im->appendChild(
248             my $svc = $self->create_element(
249             'service', _name_text => $self->_root_item->service
250             )
251             );
252 53         1174 $self->scheme_manager->add_qcode( $svc, 'svc',
253             $self->_root_item->service->qcode );
254              
255             }
256 72         1168 $self->_create_item_meta_title($im);
257              
258 72 100       1946 if ( $self->_root_item->embargo_text ) {
259 53         1332 $im->appendChild(
260             my $e = $self->create_element(
261             'edNote', _text => $self->_root_item->embargo_text
262             )
263             );
264 53         152 $self->scheme_manager->add_role( $e, 'role', 'embargotext' );
265             }
266 72 100       2500 if ( $self->_root_item->closing ) {
267 53         1332 $im->appendChild(
268             my $e = $self->create_element(
269             'edNote', _text => $self->_root_item->closing
270             )
271             );
272 53         184 $self->scheme_manager->add_role( $e, 'role', 'closing' );
273             }
274 72 100       2538 if ( $self->_root_item->note ) {
275 53         1433 $im->appendChild(
276             my $e = $self->create_element(
277             'edNote', _text => $self->_root_item->note
278             )
279             );
280 53         454 $self->scheme_manager->add_role( $e, 'role', 'note' );
281             }
282              
283 72 50       2470 if ( $self->_root_item->doc_version > 1 ) {
284 0         0 $im->appendChild( my $s = $self->create_element('signal') );
285 0         0 $self->scheme_manager->add_qcode( $s, 'sig', 'correction' );
286             }
287              
288 72         171 foreach ( @{ $self->_root_item->indicators } ) {
  72         2120  
289 104         833 $im->appendChild( my $s = $self->create_element('signal') );
290 104         298 $self->scheme_manager->add_qcode( $s, 'ind', lc );
291             }
292              
293 72         759 foreach
294             my $attr (qw(see_alsos derived_froms processed_froms evolved_froms)) {
295 288 50       9139 if ( $self->_root_item->$attr ) {
296 288         7189 my $arrayref = $self->_root_item->$attr;
297 288         745 for my $v (@$arrayref) {
298 159         1778 ( my $rel = $attr ) =~ s/_(\w)/uc $1/ge;
  159         593  
299 159         586 $rel =~ s/s$//;
300 159         4733 my $linkelem = $self->create_element(
301             'link',
302             rel => "irel:$rel",
303             version => $v->version
304             );
305 159         341 for my $attribute (qw/residref href/) {
306 318 100       9296 next unless $v->$attribute;
307 159         3778 $linkelem->setAttribute( $attribute => $v->$attribute );
308             }
309              
310 159         1412 $im->appendChild($linkelem);
311             }
312             }
313             }
314              
315 72         669 $root->appendChild($im);
316 72         236 return;
317             }
318              
319             sub _import_iptc_catalog {
320 81     81   173 my $self = shift;
321              
322 81         174 while ( my ( $attr, $alias ) = each %{ $self->g2_catalog_schemes } ) {
  1200         30109  
323 1119   66     3841 $alias ||= $attr;
324 1119 50       26898 my $getter_setter = $self->scheme_manager->can($attr)
325             or die "Unknown scheme '$attr'\n";
326             next
327 1119 100       26546 if ( $getter_setter->( $self->scheme_manager ) )
328             ; # attribute ist already set by user
329 244         7092 my $scheme = XML::NewsML_G2::Scheme->new(
330             alias => $alias,
331             catalog => $self->g2_catalog_url
332             );
333 244         5738 $getter_setter->( $self->scheme_manager, $scheme );
334             }
335 81         198 return;
336             }
337              
338             # public methods
339              
340             sub create_element {
341 6537     6537 1 76425 my ( $self, $name, %attrs ) = @_;
342 6537         10885 my $text = delete $attrs{_text};
343 6537         9127 my $cdata = delete $attrs{_cdata};
344 6537         9026 my $name_text = delete $attrs{_name_text};
345 6537   66     157473 my $ns = delete $attrs{_ns} || $self->g2_ns;
346 6537         151821 my $elem = $self->doc->createElementNS( $ns, $name );
347 6537         19014 for my $attr_name ( sort keys %attrs ) {
348 2876         14730 $elem->setAttribute( $attr_name, $attrs{$attr_name} );
349             }
350 6537 100       28930 if ($text) {
    50          
    100          
351 2866         69861 $elem->appendChild( $self->doc->createTextNode($text) );
352             }
353             elsif ($cdata) {
354 0         0 $elem->appendChild( $self->doc->createCDATASection($cdata) );
355             }
356             elsif ($name_text) {
357 1189 100 66     30846 $name_text = $name_text->name
358             if ( ref $name_text and $name_text->can('name') );
359 1189         2951 $elem->appendChild(
360             $self->create_element( 'name', _text => $name_text ) );
361             }
362 6537         36054 return $elem;
363             }
364              
365             sub create_dom {
366 72     72 1 10509 my $self = shift;
367              
368 72         439 $self->_import_iptc_catalog();
369 72         532 my $root = $self->_create_root_element();
370 72         500 $self->_create_catalogs($root);
371 72         1095 $self->_create_rights_info($root);
372 72         1722 $self->_create_item_meta($root);
373 72         3221 $self->_create_content_meta($root);
374 68         5757 $self->_create_content($root);
375 68         4070 return $self->doc;
376              
377             }
378              
379             __PACKAGE__->meta->make_immutable;
380              
381             1;
382             __END__
383              
384             =head1 NAME
385              
386             XML::NewsML_G2::Writer - base class for XML DOM tree creation
387             conforming to NewsML-G2 News Items, Package Items and News Messages
388              
389             =for test_synopsis
390             my ($ni, $sm);
391              
392             =head1 SYNOPSIS
393              
394             my $w = XML::NewsML_G2::Writer::News_Item->new
395             (news_item => $ni, scheme_manager => $sm, g2_version => 2.18);
396              
397             my $p = $w->create_element('p', class => 'main', _text => 'blah');
398              
399             my $dom = $w->create_dom();
400              
401             =head1 DESCRIPTION
402              
403             This module acts as a NewsML-G2 version-independent base class for all
404             writer classes. Depending on whether you want to create output for a
405             News Item, Package Item or News Message, use one of the subclasses
406             L<XML::NewsML_G2::Writer::News_Item>,
407             L<XML::NewsML_G2::Writer::Package_Item> or
408             L<XML::NewsML_G2::Writer::News_Message> instead.
409              
410             =head1 ATTRIBUTES
411              
412             =over 4
413              
414             =item news_item
415              
416             L<XML::NewsML_G2::News_Item> instance used to create the output document
417              
418             =item encoding
419              
420             Encoding used to create the output document, defaults to utf-8
421              
422             =item scheme_manager
423              
424             L<XML::NewsML_G2::Scheme_Manager> instance used to create qcodes
425              
426             =item doc
427              
428             L<XML::LibXML::Document> instance used to create the output document
429              
430             =item g2_ns
431              
432             XML Namespace of NewsML-G2
433              
434             =item xhtml_n2
435              
436             XML Namespace of XHTML
437              
438             =item g2_version
439              
440             Use this attribute to specify the NewsML-G2 version to be
441             created. Defaults to 2.18, other valid options are: 2.9, 2.12 and
442             2.15. Be aware that only the later versions offer all features.
443              
444             =item schema_location
445              
446             Specified by subclass.
447              
448             =item g2_catalog_url
449              
450             URL of the G2 catalog, specified by subclass.
451              
452             =item g2_catalog_schemes
453              
454             Reference to a hash of schemes that are covered by the G2 catalog. If
455             the value is undefined, it defaults to the name of the scheme.
456              
457             =item generator_version
458              
459             Version of the generating software, as written to the output. Defaults
460             to the version of XML::NewsML_G2, but can be overwritten here (mainly
461             to ease automated testing).
462              
463             =back
464              
465             =head1 METHODS
466              
467             =over 4
468              
469             =item create_element
470              
471             Helper method that creates XML elements, e.g. to be used in the
472             C<paragraphs> element of the L<XML::NewsML_G2::News_Item>.
473              
474             =item create_dom
475              
476             Returns the L<XML::LibXML::Document> element containing the requested
477             output. Be careful I<not> to use C<< $dom->serialize(2) >> for formatting,
478             as this creates invalid NewsML-G2 files because it adds whitespace
479             where none is allowed (e.g. in xs:dateTime elements).
480              
481             =back
482              
483             =head1 AUTHOR
484              
485             Philipp Gortan C<< <philipp.gortan@apa.at> >>
486              
487             =head1 LICENCE AND COPYRIGHT
488              
489             Copyright (c) 2013-2014, APA-IT. All rights reserved.
490              
491             See L<XML::NewsML_G2> for the license.