File Coverage

blib/lib/HTTP/OAI/Response.pm
Criterion Covered Total %
statement 125 144 86.8
branch 54 70 77.1
condition 29 42 69.0
subroutine 21 23 91.3
pod 9 20 45.0
total 238 299 79.6


line stmt bran cond sub pod time code
1             package HTTP::OAI::Response;
2              
3             require POSIX;
4              
5             @ISA = qw( HTTP::Response HTTP::OAI::MemberMixin HTTP::OAI::SAX::Base );
6              
7 11     11   72 use strict;
  11         22  
  11         22050  
8              
9             our $VERSION = '4.11';
10              
11             # Backwards compatibility, pass any unknown methods to content
12             our $AUTOLOAD;
13              
14       0     sub DESTROY {}
15             sub AUTOLOAD
16             {
17 8     8   1543 my $self = shift;
18 8         55 $AUTOLOAD =~ s/^.*:://;
19              
20             # don't call a $self method here, because that might call AUTOLOAD again!
21 8         21 my $content = $self->{content}->[-1];
22 8 50       61 return defined $content ? $content->$AUTOLOAD( @_ ) : undef;
23             }
24              
25             sub new
26             {
27 20     20 1 2398 my( $class, %self ) = @_;
28              
29 20         54 my $handlers = delete $self{handlers};
30 20         43 my $cb = delete $self{onRecord};
31              
32 20   66     1420 $self{responseDate} ||= POSIX::strftime("%Y-%m-%dT%H:%M:%S",gmtime).'Z';
33 20 50 0     97 $self{requestURL} ||= CGI::self_url() if defined &CGI::self_url;
34              
35             my $self = $class->SUPER::new(
36             delete($self{code}) || 200,
37 20   50     281 delete($self{message}) || "OK",
      50        
38             HTTP::Headers->new( %self )
39             );
40              
41 20         6292 $self->{Depth} = 0;
42 20   100     91 $self->{handlers} = $handlers || {};
43 20         45 $self->{onRecord} = $cb;
44 20         307 $self->{doc} = XML::LibXML::Document->new( '1.0', 'UTF-8' );
45 20         53 $self->{content} = [];
46              
47 20         103 return $self;
48             }
49              
50             # Back compatibility
51 4     4 0 110 sub errors { shift->error(@_) }
52 1     1 0 74 sub toDOM { shift->dom }
53              
54             # data that belong to this class
55 38     38 1 238 sub content { shift->_multi('content',@_) }
56 0     0 0 0 sub doc { shift->_elem('doc',@_) }
57 12     12 0 65 sub handlers { shift->_elem('handlers',@_) }
58              
59             # data that belong to this class's headers
60 100     100 0 468 sub version { shift->headers->header('version',@_) }
61 96     96 0 2518 sub verb { shift->headers->header('verb',@_) }
62 21     21 1 484 sub error { shift->headers->header('error',@_) }
63 1     1 0 8 sub xslt { shift->headers->header('xslt',@_) }
64 1     1 0 15 sub responseDate { shift->headers->header('responseDate',@_) }
65 1     1 0 14 sub requestURL { shift->headers->header('requestURL',@_) }
66              
67             sub callback
68             {
69 12     12 0 135 my( $self, $item, $list ) = @_;
70              
71 12 50       52 if( defined $self->{onRecord} )
72             {
73 0         0 $self->{onRecord}->( $item, $self );
74             }
75             else
76             {
77 12 50       46 Carp::confess( "Requires list parameter" ) if !defined $list;
78 12         64 $list->item( $item );
79             }
80             }
81              
82             # error on 600 as well
83 31 50   31 1 124 sub is_error { my $code = shift->code; $code != 0 && $code != 200 }
  31         511  
84 20     20 1 109 sub is_success { !shift->is_error }
85              
86             sub parse_string
87             {
88 6     6 1 27 my( $self, $string ) = @_;
89              
90 6         11 eval { $self->SUPER::parse_string( $string ) };
  6         41  
91 6 100       1748 if( $@ )
92             {
93 1         6 $self->code( 600 );
94 1         13 $self->message( $@ );
95             }
96             }
97              
98             sub parse_file
99             {
100 3     3 1 10 my( $self, $fh ) = @_;
101              
102 3         6 eval { $self->SUPER::parse_file( $fh ) };
  3         17  
103 3 50       717 if( $@ )
104             {
105 0         0 $self->code( 600 );
106 0         0 $self->message( $@ );
107             }
108             }
109              
110             sub generate
111             {
112 1     1 0 44 my( $self, $driver ) = @_;
113              
114 1 50       41 if( $self->xslt ) {
115 0         0 $driver->processing_instruction({
116             'Target' => 'xml-stylesheet',
117             'Data' => 'type=\'text/xsl\' href=\''. $self->xslt . '\''
118             });
119             }
120              
121 1 50 33     58 if( !defined $self->version || $self->version eq "2.0" )
    0          
122             {
123 1         52 $driver->start_element( 'OAI-PMH',
124             'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd',
125             );
126 1         274 $driver->data_element( 'responseDate', $self->responseDate );
127 1         12 my $url = URI->new( $self->requestURL );
128 1 50       7533 if( $self->error )
    0          
129             {
130 1         58 $url->query( undef );
131 1         90 $driver->data_element( 'request', $url );
132              
133 1         11 for($self->error)
134             {
135 1         46 $_->generate( $driver );
136             }
137             }
138             elsif( $self->content )
139             {
140 0         0 my %attr = $url->query_form;
141 0         0 $url->query( undef );
142 0         0 $driver->data_element( 'request', $url, %attr );
143              
144 0         0 my $content = ($self->content)[-1];
145 0         0 $driver->start_element( $content->verb );
146 0         0 $content->generate_body( $driver );
147 0         0 $driver->end_element( $content->verb );
148             }
149 1         14 $driver->end_element( 'OAI-PMH' );
150             }
151             elsif( $self->version eq "2.0s" )
152             {
153 0         0 $driver->start_prefix_mapping({
154             Prefix => 'static',
155             NamespaceURI => 'http://www.openarchives.org/OAI/2.0/static-repository',
156             });
157 0         0 $driver->start_element( 'static:Repository',
158             'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd http://www.openarchives.org/OAI/2.0/static-repository http://www.openarchives.org/OAI/2.0/static-repository.xsd',
159             );
160 0         0 for($self->content)
161             {
162 0         0 $driver->start_element( 'static:' . $_->verb );
163 0         0 $_->generate_body( $driver );
164 0         0 $driver->end_element( 'static:' . $_->verb );
165             }
166 0         0 $driver->end_element( 'static:Repository' );
167             }
168             }
169              
170             sub start_element
171             {
172 482     482 1 4061 my( $self, $hash ) = @_;
173              
174 482         937 $hash->{Depth} = ++$self->{Depth};
175              
176 482 100       1393 if( $self->{Depth} == 1 )
    100          
177             {
178 18         151 $self->version( $HTTP::OAI::VERSIONS{lc($hash->{NamespaceURI})} );
179 18 100       1317 if( !defined $self->version )
180             {
181 1         57 die "Unrecognised namespace for OAI response: {$hash->{NamespaceURI}}$hash->{Name}";
182             }
183             # static repositories don't contain ListIdentifiers or GetRecord, so
184             # instead we'll perform a complete ListRecords then extract the
185             # relevant data
186 17 100       713 if( $self->version eq "2.0s" )
187             {
188 9 100 100     329 if( $self->verb eq "ListIdentifiers" || $self->verb eq "GetRecord" )
    100          
189             {
190 3         111 $self->{_verb} = $self->verb;
191 3         108 $self->verb( "ListRecords" );
192             }
193             elsif( $self->verb eq 'ListSets' )
194             {
195 1         63 $self->content( HTTP::OAI::ListSets->new );
196 1         10 $self->error(HTTP::OAI::Error->new( code => 'noSetHierarchy' ));
197 1         76 die "done\n";
198             }
199             }
200             }
201             elsif( $self->{Depth} == 2 )
202             {
203 43         82 my $elem = $hash->{LocalName};
204 43 100 66     403 if( $elem eq "error" )
    100 66        
205             {
206 2         11 $self->set_handler( my $error = HTTP::OAI::Error->new );
207 2         119 $self->error( $error );
208             }
209             elsif
210             (
211             $elem =~ /^GetRecord|Identify|ListIdentifiers|ListMetadataFormats|ListRecords|ListSets$/ &&
212             (!defined $self->verb || $elem eq $self->verb)
213             )
214             {
215 16 100 100     617 if( $self->version eq "2.0s" && $self->verb eq "ListRecords" )
216             {
217 7         262 my $metadataPrefix = $hash->{Attributes}{'{}metadataPrefix'}{Value};
218 7 100       23 if( $metadataPrefix eq $self->headers->header( 'metadataPrefix' ) )
219             {
220 4         216 $self->set_handler( my $content = "HTTP::OAI::$elem"->new );
221 4         62 $self->content( [ $content ] );
222             }
223             }
224             else
225             {
226 9         470 $self->set_handler( my $content = "HTTP::OAI::$elem"->new );
227 9         153 $self->content( [ $content ] );
228             }
229             }
230             }
231              
232 480         2386 $self->SUPER::start_element( $hash, $self );
233             }
234              
235             sub end_element
236             {
237 473     473 1 3670 my( $self, $hash ) = @_;
238              
239 473         841 $hash->{Depth} = $self->{Depth};
240              
241 473         1225 $self->SUPER::end_element( $hash, $self );
242              
243 473 100       2831 if( $self->{Depth} == 2 )
244             {
245 43         100 my $elem = $hash->{LocalName};
246 43 100 66     428 if( $elem eq "responseDate" || $elem eq "requestURL" )
    100 100        
    100          
    100          
247             {
248 7         26 $self->headers->header( $elem, $hash->{Text} );
249             }
250             elsif( $elem eq "request" )
251             {
252 7         35 $self->headers->header("request",$hash->{Text});
253 7         473 my $uri = new URI($hash->{Text});
254 7         28724 $uri->query_form(map { ($_->{LocalName},$_->{Value}) } values %{$hash->{Attributes}});
  8         56  
  7         45  
255 7         933 $self->headers->header("requestURL",$uri);
256             }
257             elsif( $elem eq "error" )
258             {
259 2         10 my $error = $self->get_handler;
260 2 50       21 if( $error->code !~ /^noRecordsMatch|noSetHierarchy$/ )
261             {
262 2         25 $self->code( 500 );
263 2         23 $self->message( $error->code . ": " . $error->message );
264             }
265             }
266             # extract ListIdentifiers and GetRecord from a static ListRecords
267             elsif( defined($self->get_handler) && $self->version eq "2.0s" )
268             {
269             # fake ListIdentifiers/GetRecord
270 7 100       448 if( defined(my $verb = $self->{_verb}) )
271             {
272 3 100       19 if( $verb eq "ListIdentifiers" )
    50          
273             {
274 1         28 my $content = HTTP::OAI::ListIdentifiers->new;
275 1         7 $content->item( map { $_->header } ($self->content)[-1]->item );
  2         16  
276 1         5 $self->content( [ $content ] );
277             }
278             elsif( $verb eq "GetRecord" )
279             {
280 2         55 my $content = HTTP::OAI::GetRecord->new;
281 2         11 $content->item( [grep { $_->identifier eq $self->headers->header('identifier') } ($self->content)[-1]->item] );
  4         113  
282 2         11 $self->content( [ $content ] );
283 2 100       8 if( !defined( ($content->item)[0] ) )
284             {
285 1         6 $self->content( [] );
286 1         11 $self->error(my $error = HTTP::OAI::Error->new( code => 'idDoesNotExist' ));
287 1         84 $self->code( 500 );
288 1         18 $self->message( $error->code . ": " . $error->message );
289             }
290             }
291             }
292 7         141 die "done\n";
293             }
294 36         1529 $self->set_handler( undef );
295             }
296 466 100       1558 if( $self->{Depth} == 1 )
297             {
298 9 50 66     45 if( $self->version eq "2.0s" && !$self->error && !$self->content )
      66        
299             {
300 1         20 $self->error(my $error = HTTP::OAI::Error->new( code => 'cannotDisseminateFormat' ));
301 1         62 $self->code( 500 );
302 1         19 $self->message( $error->code . ": " . $error->message );
303             }
304             # allow callers to do $r->next to check whether anything came back
305 9 100 100     364 if( !$self->content && defined(my $verb = $self->verb) )
306             {
307 1         52 $self->content( "HTTP::OAI::$verb"->new );
308             }
309             }
310              
311 466         5198 $self->{Depth}--;
312             }
313              
314             1;