File Coverage

blib/lib/Net/OAI/Record/NamespaceFilter.pm
Criterion Covered Total %
statement 135 169 79.8
branch 56 90 62.2
condition 5 11 45.4
subroutine 12 17 70.5
pod 12 14 85.7
total 220 301 73.0


line stmt bran cond sub pod time code
1             package Net::OAI::Record::NamespaceFilter;
2              
3 1     1   1230 use strict;
  1         2  
  1         27  
4 1     1   5 use base qw( XML::SAX::Base );
  1         2  
  1         69  
5 1     1   5 use Storable;
  1         2  
  1         1955  
6             our $VERSION = 'v1.016.10';
7              
8             our $AUTOLOAD;
9              
10             =head1 NAME
11              
12             Net::OAI::Record::NamespaceFilter - general filter class
13              
14             =head1 SYNOPSIS
15              
16             =head1 DESCRIPTION
17              
18             It will forward any element belonging to a namespace from this list
19             to the associated SAX filter and all of the element's children
20             (regardless of their respective namespace) to the same one. It can be used either as a
21             C or C.
22              
23             This SAX filter takes a hashref C as argument, with namespace
24             URIs for keys ('*' for "any") and either
25              
26             over 4
27              
28             =item undef
29              
30             Matching elements and their subelements are suppressed.
31              
32             If the list of namespaces ist empty or C is connected to
33             the filter, it effectively acts as a plug to Net::OAI::Harvester. This
34             might come handy if you are planning to get to the raw result by other
35             means, e.g. by tapping the user agent or accessing the result's xml() method:
36              
37             $plug = Net::OAI::Harvester::Record::NamespaceFilter();
38             $harvester = Net::OAI::Harvester->new( [
39             baseURL => ...,
40             recordHandler => $plug,
41             ] );
42              
43             my $unparsed;
44             open (my $TAP, ">", \$unparsed);
45             $harvester->userAgent()->add_handler(response_data => sub {
46             my($response, $ua, $h, $data) = @_;
47             print $TAP $data;
48             });
49              
50             $list = $harvester->listRecords(
51             metadataPrefix => 'a_strange_one',
52             recordHandler => $plug,
53             );
54              
55             print $unparsed; # complete OAI response
56             print $list->xml(); # should be the same
57              
58              
59             =item a class name of a SAX filter
60              
61             For any record element of the OAI response a new instance
62             is created.
63              
64              
65             =item a code reference for an constructor
66              
67             Must return a SAX filter ready to accept a new document.
68              
69             The following example returns a text representation of each single
70             record:
71              
72             # end_document() events will return \$x
73             my $constructor = sub { my $x = "";
74             return XML::SAX::Writer->new(Output => \$x);
75             };
76             $my harvester = Net::OAI::Harvester->new( [
77             baseURL => ...,
78             ] );
79              
80             my $filter = Net::OAI::Harvester::Record::NamespaceFilter(
81             '*' => $constructor);
82            
83             my $list = $harvester->listRecords(
84             metadataPrefix => 'oai_dc',
85             recordHandler => $filter,
86             );
87              
88             while( my $r = $list->next() ) {
89             my $xmlstringref = $r->recorddata()->result('*');
90             ...
91             };
92              
93             Note:
94              
95              
96             =item an already instantiated SAX filter
97              
98             In this case C and C events are
99             E forwarded to the filter.
100              
101             open my $fh, ">", $some_file;
102             $builder = XML::SAX::Writer->new(Output => $fh);
103             $builder->start_document();
104             my $rootEL = { Name => 'collection',
105             LocalName => 'collection',
106             NamespaceURI => "http://www.loc.gov/MARC21/slim",
107             Prefix => "",
108             Attributes => {}
109             };
110             $builder->start_element( $rootEL );
111              
112             # filter for OAI-Namespace in records: forward all
113             my $filter = Net::OAI::Harvester::Record::NamespaceFilter(
114             'http://www.loc.gov/MARC21/slim' => $builder);
115              
116             my $harvester = Net::OAI::Harvester->new( [
117             baseURL => ...,
118             ] );
119              
120             my $list = $harvester->listRecords(
121             metadataPrefix => 'a_strange_one',
122             metadataHandler => $filter,
123             );
124             # handle resumption tokens if more than the first
125             # chunk shall be stored into $fh ....
126              
127             $builder->end_element( $rootEL );
128             $builder->end_document();
129             close($fh);
130              
131             =back
132              
133              
134             =head1 METHODS
135              
136              
137             =head2 new( [%namespaces] )
138              
139             =cut
140              
141             sub new {
142 3     3 1 3487 my ( $class, %opts ) = @_;
143 3   33     30 my $self = bless { namespaces => {%opts} }, ref( $class ) || $class;
144 3         12 $self->{ _activeStack } = [];
145 3         9 $self->{ _tagStack } = [];
146 3         7 $self->{ _result } = [];
147 3         8 $self->{ _prefixmap } = {};
148 3         20 $self->set_handler( undef );
149 3         36 delete $self->{ _noHandler }; # follows set_handler()
150 3         10 $self->{ _handlers } = {};
151 3         7 $self->{ _performing } = {};
152 3         7 while ( my ($key, $value) = each %{$self->{ namespaces }} ) {
  5         24  
153 2 50       17 if ( ! defined $value ) { # no handler
    50          
    100          
154             #warn "new(): case 1 for $key";
155             }
156             elsif ( ! ref($value) ) { # class name
157             #warn "new(): case 2 for $key";
158 0         0 Net::OAI::Harvester::_verifyHandler( $value );
159             }
160             elsif ( ref($value) eq "CODE" ) { # constructor
161             #warn "new(): case 3 for $key";
162             # can't verify now
163             }
164             else { # active instance
165             #warn "new(): case 4 for $key";
166 1         3 $self->{ _handlers }->{ $key } = $value;
167 1         4 $self->{ _performing }->{ $value }--;
168             }
169             };
170 3         11 return( $self );
171             }
172              
173             =head2 result ( [namespace] )
174              
175             If called with a I, it returns the result of the handler,
176             i.e. what C returned for the record in question.
177             Otherwise it returns a hashref for all the results with the
178             corresponding namespaces as keys.
179              
180             =cut
181              
182             sub result {
183 202     202 1 122309 my ( $self, $ns ) = @_;
184 202 100       572 if ( defined $ns ) {
185 201   100     941 return $self->{ _result }->{$ns} || undef}
186             else {
187 1         4 return $self->{ _result }}
188             }
189              
190              
191             ## Storable hooks
192              
193             sub STORABLE_freeze {
194 400     400 0 45927 my ($obj, $cloning) = @_;
195 400 50       1021 return if $cloning;
196 400         81023 return "", $obj->{ _result }; # || undef;
197             }
198              
199             sub STORABLE_thaw {
200 400     400 0 14692 my ($obj, $cloning, $serialized, $listref) = @_;
201 400 50       967 return if $cloning;
202 400         8295 $obj->{ _result } = $listref;
203             #warn "thawed @$listref";
204             }
205              
206              
207             ## SAX handlers
208              
209             sub start_document {
210 0     0 1 0 my ($self, $document) = @_;
211 0         0 die "start_document()";
212 0         0 warn "\t\t_activeStack: @{$self->{ _activeStack }}\n";
  0         0  
213 0         0 warn "\t\t_tagStack: @{$self->{ _tagStack }}\n";
  0         0  
214 0         0 $self->SUPER::start_document( $document );
215             }
216             sub end_document {
217 0     0 1 0 my ($self, $document) = @_;
218 0         0 die "end_document()";
219 0         0 warn "\t\t_activeStack: @{$self->{ _activeStack }}\n";
  0         0  
220 0         0 warn "\t\t_tagStack: @{$self->{ _tagStack }}\n";
  0         0  
221 0         0 $self->SUPER::end_document( $document );
222             }
223              
224             sub start_prefix_mapping {
225 2005     2005 1 16898 my ($self, $mapping) = @_;
226             #warn "NamespaceFilter: deferred prefix mapping for @{[%$mapping]}\n";
227 2005 100       6090 $self->SUPER::start_prefix_mapping( $mapping ) unless $self->{ _noHandler };
228 2005 50       9689 return if $self->{ _activeStack }->[0];
229             #warn ">>>possibly deferred prefix mapping for @{[%$mapping]}\n";
230 2005         5069 $self->{ _prefixmap }->{ $mapping->{Prefix} } = $mapping;
231 2005         4947 my $activehdl = $self->get_handler();
232 2005 0 33     14913 die "wrong assumption" unless (! defined $activehdl) or $self->{ _performing }->{ $activehdl };
233 2005         2624 my $switched;
234 2005         2532 foreach my $hdl ( keys %{$self->{ _performing }} ) {
  2005         5253  
235             #warn "\t-->forwarding prefix mapping @{[%$mapping]}\n\t\tto $hdl at @{$self->{ _tagStack }}\n";
236 5         19 $self->set_handler( $hdl );
237 5         72 $self->SUPER::start_prefix_mapping( $mapping );
238 5         166 $switched = 1;
239             }
240 2005 100       13907 $self->set_handler( $activehdl ) if $switched;
241             }
242              
243             sub end_prefix_mapping {
244 1203     1203 1 21252 my ($self, $mapping) = @_;
245 1203 100       3812 $self->SUPER::end_prefix_mapping( $mapping ) unless $self->{ _noHandler };
246 1203 50       5431 return if $self->{ _activeStack }->[0];
247             #warn "<<{ _tagStack }}\n";
248 1203 50       3978 die "mapping @{[%$mapping]} already removed" unless $self->{ _prefixmap }->{ $mapping->{Prefix} };
  0         0  
249 1203         3007 my $activehdl = $self->get_handler(); # always undef
250 1203 0 33     8539 die "wrong assumption" unless (! defined $activehdl) or $self->{ _performing }->{ $activehdl };
251 1203         1480 my $switched;
252 1203         1683 foreach my $hdl ( keys %{$self->{ _performing }} ) {
  1203         3184  
253             #warn "\t--->forwarding removed mapping to $hdl";
254 3         11 $self->set_handler( $hdl );
255 3         32 $self->SUPER::end_prefix_mapping( $mapping );
256 3         82 $switched = 1;
257             }
258 1203         2941 delete $self->{ _prefixmap }->{ $mapping->{Prefix} };
259 1203 100       6988 $self->set_handler( $activehdl ) if $switched;
260             }
261              
262             sub start_element {
263 10645     10645 1 69262 my ( $self, $element ) = @_;
264             #warn "\t((( ".$element->{ Name }." (((";
265             #warn "\t\t_activeStack: @{$self->{ _activeStack }}\n";
266             #warn "\t\t_tagStack: @{$self->{ _tagStack }}\n";
267 10645 100       25027 if ( $self->{ _activeStack }->[0] ) { # handler already set up
268             }
269             else {
270 6115 100       15425 unless ( $self->{ _tagStack }->[0] ) { # should be the start of a new record
271             #warn "initializing for $element->{Name}\n";
272 401         783 $self->{ _result } = {};
273             # start_document here for all defined handlers?
274 401         1557 my $activehdl = $self->get_handler(); # always undef
275 401 50       3018 die "handler $activehdl already active" if defined $activehdl;
276 401         504 my $switched;
277              
278 401         656 while ( my ($key, $value) = each %{$self->{ namespaces }} ) {
  602         9142  
279 201         483 $self->{ _result }->{ $key } = undef;
280 201         281 my $hdl;
281 201 50       1162 if ( ! defined $value ) { # no handler
    50          
    100          
282             #warn "start_element(): case 1 for $key";
283             }
284             elsif ( ! ref($value) ) { # class name
285             #warn "start_element(): case 2 for $key";
286 0         0 $hdl = $value->new();
287             }
288             elsif ( ref($value) eq "CODE" ) { # constructor
289             #warn "start_element(): case 3 for $key";
290 200         744 $hdl = &$value();
291 200         38954 Net::OAI::Harvester::_verifyHandler( $hdl );
292             }
293             else { # always active instance
294             #warn "start_element(): case 4 for $key. Handler is $value";
295             # bugfix for XML::SAX::Writer?
296 1         2 $switched = 1;
297 1         4 $self->set_handler( $value );
298 1         9 foreach my $mapping ( values %{$self->{ _prefixmap }} ) {
  1         6  
299             #warn "bugfix supply of deferred @{[%$mapping]}";
300 4         134 $self->SUPER::start_prefix_mapping( $mapping )}
301 1         34 next;
302             }
303              
304 200         484 $self->{ _handlers }->{ $key } = $hdl;
305 200 50       472 next unless defined $hdl;
306 200 50       915 next if $self->{ _performing }->{ $hdl }++;
307 200         279 $switched = 1;
308 200         644 $self->set_handler( $hdl );
309             #warn "dispatching start_document for $hdl";
310 200         2531 $self->SUPER::start_document({});
311 200         63203 foreach my $mapping ( values %{$self->{ _prefixmap }} ) {
  200         785  
312             #warn "supplying deferred @{[%$mapping]} for $hdl";
313 800         27223 $self->SUPER::start_prefix_mapping( $mapping )}
314             }
315 401 100       1774 $self->set_handler( $activehdl ) if $switched;
316             };
317              
318 6115 100       27052 if ( exists $self->{ namespaces }->{$element->{ NamespaceURI }} ) {
    50          
319 201 50       963 if ( defined (my $hdl = $self->{ _handlers }->{$element->{ NamespaceURI }}) ) {
320 201         528 $self->set_handler( $hdl );
321 201         2126 $self->{ _noHandler } = 0;
322             };
323             }
324             elsif ( exists $self->{ namespaces }->{'*'} ) {
325 0 0       0 if ( defined (my $hdl = $self->{ _handlers }->{'*'}) ) {
326 0         0 $self->set_handler( $hdl );
327 0         0 $self->{ _noHandler } = 0;
328             };
329             }
330             else {
331 5914         7469 push (@{$self->{ _tagStack }}, $element->{ Name });
  5914         15020  
332 5914         20378 return;
333             };
334             };
335              
336 4731         6319 push (@{$self->{ _activeStack }}, $element->{ Name });
  4731         11590  
337 4731 50       12443 return if $self->{ _noHandler };
338 4731         12538 $self->SUPER::start_element( $element );
339             }
340              
341             sub end_element {
342 10645     10645 1 70362 my ( $self, $element ) = @_;
343             #warn "\t))) ".$element->{ Name }." )))";
344             #warn "\t\t_activeStack: @{$self->{ _activeStack }}\n";
345             #warn "\t\t_tagStack: @{$self->{ _tagStack }}\n";
346 10645 100       32515 if ( $self->{ _activeStack }->[0] ) {
    50          
347 4731 50       11191 unless ( $self->{ _noHandler } ) {
348 4731         11856 $self->SUPER::end_element( $element );
349             };
350 4731         523940 pop (@{$self->{ _activeStack }});
  4731         10364  
351 4731 100       20273 return if $self->{ _activeStack }->[0];
352 201 50       637 unless ( $self->{ _noHandler } ) {
353 201         700 $self->set_handler(undef);
354 201         3420 $self->{ _noHandler } = 1;
355             }
356             }
357             elsif ( $self->{ _tagStack }->[0] ) {
358 5914         6927 pop (@{$self->{ _tagStack }});
  5914         11948  
359             }
360 6115 100       25200 return if $self->{ _tagStack }->[0];
361             # create end_document() event here for all handlers?
362             #warn "finalizing for $element->{Name}";
363 401         1209 my $activehdl = $self->get_handler(); # always undef
364 401 50       3094 die "handler $activehdl still active" if defined $activehdl;
365 401         560 my $switched;
366 401         597 while ( my ($key, $value) = each %{$self->{ namespaces }} ) {
  602         2305  
367 201 50       945 if ( ! defined $value ) {
    50          
368             #warn "end_element(): case 1 for $key";
369 0         0 $self->{ _result }->{ $key } = "";
370             }
371             elsif ( my $hdl = $self->{ _handlers }->{ $key } ) {
372 201 50       1333 if ( ! $self->{ _performing }->{ $hdl } ) {
    100          
373 0         0 warn "already(?) inactive handler $hdl for $key";
374 0         0 delete $self->{ _handlers }->{ $key };
375 0         0 next;
376             }
377             elsif ( $self->{ _performing }->{ $hdl } < 0 ) { # always active handler
378             #warn "end_element(): case 4 for $key";
379 1         3 $self->{ _result }->{ $key } = undef;
380 1         3 next;
381             };
382             #warn "end_element(): case 2/3 for $key";
383 200         383 delete $self->{ _handlers }->{ $key };
384 200         556 delete $self->{ _performing }->{ $hdl };
385 200         303 $switched = 1;
386 200         602 $self->set_handler( $hdl );
387             # revoke some stored namespace mappings, too?
388 200         2437 my $result = $self->SUPER::end_document({});
389             #warn "dispatching end_document for $hdl yielded $result";
390 200         15198 $self->{ _result }->{ $key } = $result;
391             }
392             else {
393 0         0 die " $key not listed as _handler";
394             };
395             };
396 401 100       1793 $self->set_handler( $activehdl ) if $switched;
397             }
398              
399             sub characters {
400 23211     23211 1 144324 my ( $self, $characters ) = @_;
401 23211 100       53622 return if $self->{ _noHandler };
402 22811         55609 return $self->SUPER::characters( $characters );
403             }
404              
405             sub ignorable_whitespace {
406 0     0 1   my ( $self, $characters ) = @_;
407 0 0         return if $self->{ _noHandler };
408 0           return $self->SUPER::ignorable_whitespace( $characters );
409             }
410              
411             sub comment {
412 0     0 1   my ( $self, $comment ) = @_;
413 0 0         return if $self->{ _noHandler };
414 0           return $self->SUPER::comment( $comment );
415             }
416              
417             sub processing_instruction {
418 0     0 1   my ( $self, $pi ) = @_;
419 0 0         return if $self->{ _noHandler };
420 0           return $self->SUPER::processing_instruction( $pi );
421             }
422              
423             1;
424