File Coverage

blib/lib/XML/LibXSLT/Easy.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package XML::LibXSLT::Easy;
4 1     1   47398 use Moose;
  0            
  0            
5              
6             our $VERSION = "0.03";
7              
8             use Carp qw(croak);
9              
10             use Devel::PartialDump qw(warn dump);
11              
12             use XML::LibXML;
13             use XML::LibXSLT;
14              
15             use Path::Class;
16             use URI;
17             use URI::file;
18             use URI::data;
19              
20             use Scope::Guard;
21              
22             use MooseX::MultiMethods;
23             use MooseX::Types::Moose qw(Str FileHandle Item Undef);
24             use MooseX::Types::Path::Class qw(File);
25             use MooseX::Types::URI qw(Uri);
26              
27             use MooseX::Types -declare => [qw(Stylesheet Document)];
28              
29             BEGIN {
30             class_type Stylesheet, { class => "XML::LibXSLT::StylesheetWrapper" };
31             class_type Document, { class => "XML::LibXML::Document" };
32             }
33              
34              
35             use namespace::clean -except => [qw(meta)];
36              
37             has xml => (
38             isa => "XML::LibXML",
39             is => "rw",
40             lazy_build => 1,
41             handles => [qw(
42             parse_string
43             parse_fh
44             parse_file
45             base_uri
46             )],
47             );
48              
49             has xml_options => (
50             isa => "HashRef",
51             is => "rw",
52             default => sub { {} },
53             );
54              
55             sub _build_xml {
56             my $self = shift;
57             XML::LibXML->new( %{ $self->xml_options } );
58             }
59              
60             has xslt=> (
61             isa => "XML::LibXSLT",
62             is => "rw",
63             lazy_build => 1,
64             handles => [qw(
65             parse_stylesheet
66             transform
67             )],
68             );
69              
70             has xslt_options => (
71             isa => "HashRef",
72             is => "rw",
73             default => sub { {} },
74             );
75              
76             sub process {
77             my ( $self, %args ) = @_;
78              
79             my ( $xml, $xsl, $out, $uri ) = @args{qw(xml xsl out input_uri)};
80              
81             $uri ||= $self->get_uri($xml);
82              
83             my $doc = $self->parse($xml);
84              
85             if ( $uri ) {
86             my $prev_base = $self->base_uri;
87             my $sg = Scope::Guard->new(sub { $self->base_uri($prev_base) });
88             $self->base_uri($uri);
89             }
90              
91             unless ( defined $xsl ) {
92             croak "Can't process <?xml-stylesheet> without knowing the URI of the input" unless $uri;
93             $xsl = $self->get_xml_stylesheet_pi( $doc, $uri, %args );
94             }
95              
96             my $stylesheet = $self->stylesheet($xsl);
97              
98             $self->output( $out, $stylesheet, $stylesheet->transform($doc) );
99             }
100              
101             sub _build_xslt {
102             my $self = shift;
103             XML::LibXSLT->new( %{ $self->xslt_options } );
104             }
105              
106             sub get_xml_stylesheet_pi {
107             my ( $self, $doc, $uri, %args ) = @_;
108              
109             # from AxKit::PageKit::Content
110             my @stylesheet_hrefs;
111             for my $pi_node ($doc->findnodes('processing-instruction()')) {
112             my $pi_str = $pi_node->getData;
113             if ( $pi_str =~ m!type="text/xsl! or $pi_str !~ /type=/ ) {
114             my ($stylesheet_href) = ($pi_str =~ m!href="([^"]*)"!);
115              
116             my $xsl_uri = URI->new($stylesheet_href);
117              
118             if ( $xsl_uri->scheme ) { # scheme means abs
119             return $xsl_uri;
120             } else {
121             if ( $uri->isa("URI::data") ) {
122             croak "<?xml-stylesheet>'s href is relative but the base URI is in the 'data:' scheme and cannot be used as a base";
123             }
124              
125             if ( $uri->isa("URI::file") ) {
126             my $file = file($uri->file);
127             return $file->parent->file($stylesheet_href);
128             } elsif ( $uri->scheme ) {
129             return $xsl_uri->abs($uri)
130             } else {
131             croak "<?xml-stylesheet>'s href is relative buit the URI base neither absolute nor a 'file:' one";
132             }
133             }
134             }
135             }
136              
137             croak "No <?xml-stylesheet> processing instruction in document, please specify stylesheet explicitly";
138             }
139              
140             multi method get_uri ( Uri $uri ) { $uri }
141             multi method get_uri ( File $file ) { URI::file->new($file) }
142             multi method get_uri ( Str $str ) {
143             if ( -f $str ) {
144             URI::file->new($str);
145             } else {
146             URI::data->new($str);
147             }
148             }
149              
150             multi method stylesheet ( Stylesheet $s ) { $s }
151             multi method stylesheet ( Document $doc ) { $self->parse_stylesheet($doc) }
152             multi method stylesheet ( Any $thing ) {
153             $self->stylesheet( $self->parse($thing) );
154             }
155              
156             multi method parse ( Document $doc ) { $doc }
157             multi method parse ( FileHandle $fh ) { $self->parse_fh($fh) }
158             multi method parse ( File $file ) { $self->parse_file($file) }
159             multi method parse ( Str $thing, @args ) {
160             if ( -f $thing ) {
161             $self->parse_file($thing, @args);
162             } else {
163             $self->parse_string($thing, @args);
164             }
165             }
166              
167             # includes file URIs
168             multi method parse ( Uri $uri, @args ) {
169             $self->parse_file( $uri, @args );
170             }
171              
172             multi method output ( FileHandle $fh, @args ) { $self->output_fh($fh, @args) }
173             multi method output ( Str $file, @args ) { $self->output_file($file, @args) }
174             multi method output ( File $file, @args ) { $self->output_File($file, @args) }
175             multi method output ( Undef $x, @args ) { $self->output_string(@args) }
176              
177             sub output_string {
178             my ( $self, $s, $r ) = @_;
179             $s->output_string($r);
180             }
181              
182             sub output_fh {
183             my ( $self, $o, $s, $r ) = @_;
184             $s->output_fh($r, $o);
185             }
186              
187             sub output_file {
188             my ( $self, $o, $s, $r ) = @_;
189             $s->output_file($r, $o);
190             }
191              
192             __PACKAGE__
193              
194             __END__
195              
196             =pod
197              
198             =head1 NAME
199              
200             XML::LibXSLT::Easy - DWIM XSLT processing with L<XML::LibXSLT>
201              
202             =head1 SYNOPSIS
203              
204             use XML::LibXSLT::Easy;
205              
206             my $p = XML::LibXSLT::Easy->new;
207              
208             my $output = $p->process( xml => "foo.xml", xsl => "foo.xsl" );
209              
210             # takes various types of arguments
211             $p->process( xml => $doc, xsl => $filehandle, out => $filename );
212              
213             =head1 DESCRIPTION
214              
215             =cut
216              
217