File Coverage

blib/lib/XML/Filter/XML_Directory_2/Base.pm
Criterion Covered Total %
statement 60 141 42.5
branch 23 54 42.5
condition 3 14 21.4
subroutine 10 28 35.7
pod 19 23 82.6
total 115 260 44.2


line stmt bran cond sub pod time code
1             {
2              
3             =head1 NAME
4              
5             XML::Filter::XML_Directory_2::Base - base class for creating XML::Directory to something else SAX filters.
6              
7             =head1 SYNOPSIS
8              
9             package XML::Filter::XML_Directory_2Foo;
10             use base qw (XML::Filter::XML_Directory_2::Base);
11              
12             =head1 DESCRIPTION
13              
14             Base class for creating XML::Directory to something else SAX filters.
15              
16             This class inherits from I.
17              
18             =cut
19              
20             package XML::Filter::XML_Directory_2::Base;
21 1     1   880 use strict;
  1         2  
  1         35  
22              
23 1     1   5 use Carp;
  1         1  
  1         94  
24 1     1   5 use Exporter;
  1         5  
  1         34  
25 1     1   5 use Digest::MD5 qw (md5_hex);
  1         1  
  1         46  
26 1     1   7958 use XML::Filter::XML_Directory_Pruner '1.3';
  1         77454  
  1         2214  
27              
28             $XML::Filter::XML_Directory_2::Base::VERSION = '1.4.4';
29             @XML::Filter::XML_Directory_2::Base::ISA = qw ( XML::Filter::XML_Directory_Pruner );
30             @XML::Filter::XML_Directory_2::Base::EXPORT = qw ();
31             @XML::Filter::XML_Directory_2::Base::EXPORT_OK = qw ();
32              
33             =head1 PACKAGE METHODS
34              
35             =head2 __PACKAGE__->attributes(\%args)
36              
37             This is a simple helper method designed to save typing.
38              
39             Value arguments are
40              
41             =over
42              
43             =item *
44              
45             The name of an attribute
46              
47             =item *
48              
49             The value of an attribute
50              
51             =back
52              
53             Returns a hash with a single key named I whose value is a hash ref for passing to the I method.
54              
55             This method does not support namespaces (yet.)
56              
57             =cut
58              
59             sub attributes {
60 0     0 1 0 my $pkg = shift;
61 0         0 my %attrs = @_;
62            
63 0         0 my %saxtributes = ();
64            
65 0         0 foreach (sort keys %attrs) {
66 0         0 $saxtributes{"{}$_"} = {
67             Name => $_,
68             Value => $attrs{$_},
69             Prefix => "",
70             LocalName => $_,
71             NameSpaceURI => "",
72             };
73             }
74              
75 0         0 return (Attributes=>\%saxtributes);
76             }
77              
78             =head1 OBJECT METHODS
79              
80             =head2 $pkg->encoding($type)
81              
82             =cut
83              
84             sub encoding {
85 0     0 1 0 my $self = shift;
86 0         0 my $type = shift;
87              
88 0 0       0 if ($type) {
89 0         0 $self->{__PACKAGE__.'__type'} = $type;
90             }
91              
92 0   0     0 return $self->{__PACKAGE__.'__type'} || "UTF-8";
93             }
94              
95             =head2 $pkg->set_encoding($type)
96              
97             Alias for I
98              
99             =cut
100              
101             sub set_encoding {
102 0     0 1 0 my $self = shift;
103 0         0 $self->encoding(@_);
104             }
105              
106             =head2 $pkg->exclude_root($bool)
107              
108             By default, XML::Directory will include the directory you pass to the I method.
109              
110             You can use this method to instruct your filter to only include the contents of the root directory and not the directory itself.
111              
112             =cut
113              
114             sub exclude_root {
115 0     0 1 0 my $self = shift;
116 0         0 my $bool = shift;
117              
118 0 0       0 if (defined($bool)) {
119 0 0       0 $self->{__PACKAGE__.'__includeroot'} = ($bool) ? 0 : 1;
120             }
121              
122 0         0 return $self->{__PACKAGE__.'__includeroot'};
123             }
124              
125             =head2 $pkg->start_level()
126              
127             Read-only.
128              
129             =cut
130              
131             sub start_level {
132 0     0 1 0 my $self = shift;
133 0         0 return $self->{__PACKAGE__.'__start'};
134             }
135              
136             =head2 $pkg->cwd()
137              
138             Read-only.
139              
140             =cut
141              
142             sub cwd {
143 0     0 1 0 my $self = shift;
144 0         0 return $self->{__PACKAGE__.'__cwd'};
145             }
146              
147             =head2 $pkg->current_directory()
148              
149             Short-cut (ahem) for $pkg->cwd()
150              
151             =cut
152              
153             sub current_directory {
154 0     0 1 0 return $_[0]->cwd();
155             }
156              
157             =head2 $pkg->current_location()
158              
159             Returns the current location relative to the directory root
160              
161             =cut
162              
163             sub current_location {
164 0     0 1 0 my $self = shift;
165 0         0 return $self->{__PACKAGE__.'__loc'};
166             }
167              
168             =head2 $pkg->set_handlers(\%args)
169              
170             Define one or more valid SAX2 thingies to be called when your package encounters a specific event. Thingies are like any other SAX2 thingy with a few requirements :
171              
172             =over
173              
174             =item *
175              
176             Must inherit from XML::SAX::Base.
177              
178             =item *
179              
180             It must define a I method.
181              
182             =back
183              
184             # If this...
185              
186             my $writer = XML::SAX::Writer->new();
187             my $rss = XML::Filter::XML_Directory_2RSS->new(Handler=>$writer);
188             $rss->set_handlers({title=>MySAX::TitleHandler->new(Handler=>$writer)});
189              
190             # Called this...
191              
192             package MySAX::TitleHandler;
193             use base qw (XML::SAX::Base);
194            
195             sub parse_uri {
196             my ($pkg,$path,$title) = @_;
197              
198             $pkg->SUPER::start_prefix_mapping({Prefix=>"me",NamespaceURI=>"..."});
199             $pkg->SUPER::start_element({Name=>"me:woot"});
200             $pkg->SUPER::characters({Data=>&get_title_from_file($path)});
201             $pkg->SUPER::end_element({Name=>"me:woot"});
202             $pkg->SUPER::end_prefix_mapping({Prefix=>"me"});
203             }
204              
205             # Then the output would look like this...
206              
207            
208             </td> </tr> <tr> <td class="h" > <a name="209">209</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> <me:woot xmlns:me="...">I Got My Title From the File</me:woot> </td> </tr> <tr> <td class="h" > <a name="210">210</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">
211             ...
212            
213            
214              
215             Valid events are defined on a per class basis. Your class needs to define a I package method that returns a list of valid handler events.
216              
217             Handlers have a higher precedence than callbacks.
218              
219             =cut
220              
221 0     0 0 0 sub handler_events { return (); }
222              
223             sub set_handlers {
224 0     0 1 0 my $self = shift;
225 0         0 my $args = shift;
226              
227 0 0       0 if (ref($args) ne "HASH") {
228 0         0 return undef;
229             }
230              
231 0         0 foreach ($self->handler_events()) {
232 0 0       0 next if (! $args->{$_});
233              
234 0 0       0 if (! UNIVERSAL::can($args->{$_},"parse_uri")) {
235 0         0 carp "Handler must define a 'parse_uri' method.\n";
236 0         0 next;
237             }
238              
239 0         0 $self->{__PACKAGE__.'__handlers'}{$_} = $args->{$_};
240             }
241              
242 0         0 return 1;
243             }
244              
245             =head2 $pkg->retrieve_handler($event_name)
246              
247             Returns the handler (object) associated with I<$event_name>
248              
249             =cut
250              
251             sub retrieve_handler {
252 0     0 1 0 my $self = shift;
253 0         0 return $self->{__PACKAGE__.'__handlers'}{$_[0]};
254             }
255              
256 0     0 0 0 sub callback_events { return (); }
257              
258             =head2 $pkg->set_callbacks(\%args)
259              
260             Register one of more callbacks for your document.
261              
262             Callbacks are like I except that they are code references instead of SAX2 thingies.
263              
264             A code reference might be used to munge the I value of an item into a URI suitable for viewing in a web browser.
265              
266             Valid events are defined on a per class basis. Your class needs to define a I package method that returns a list of valid callback events.
267              
268             Callbacks have a lower precedence than handlers.
269              
270             =cut
271              
272             sub set_callbacks {
273 0     0 1 0 my $self = shift;
274 0         0 my $args = shift;
275              
276 0 0       0 if (ref($args) ne "HASH") {
277 0         0 return undef;
278             }
279              
280 0         0 foreach ($self->callback_events()) {
281 0 0       0 next if (! $args->{$_});
282              
283 0 0       0 if (ref($args->{$_}) ne "CODE") {
284 0         0 carp "Not a CODE reference";
285 0         0 return undef;
286             }
287              
288 0         0 $self->{__PACKAGE__.'__callbacks'}{$_} = $args->{$_};
289             }
290              
291 0         0 return 1;
292             }
293              
294             =head2 $pkg->retrieve_callback($event_name)
295              
296             Return the callback (code reference) associated with I<$event_name>.
297              
298             =cut
299              
300             sub retrieve_callback {
301 0     0 1 0 my $self = shift;
302 0         0 return $self->{__PACKAGE__.'__callbacks'}{$_[0]};
303             }
304              
305             =head2 $pkg->generate_id()
306              
307             Returns an MD5 hash of the path, relative to the root, for the current file
308              
309             =cut
310              
311             sub generate_id {
312 0     0 1 0 my $self = shift;
313 0         0 return "ID".&md5_hex($self->{__PACKAGE__.'__loc'});
314             }
315              
316             =head2 $pkg->build_uri(\%data)
317              
318             Returns the absolute path for the current document.
319              
320             =cut
321              
322             sub build_uri {
323 0     0 1 0 my $self = shift;
324 0         0 my $data = shift;
325              
326 0         0 my $uri = $self->{__PACKAGE__.'__path'}.$self->{__PACKAGE__.'__cwd'};
327              
328 0 0       0 if ($data->{Name} eq "file") {
329 0         0 $uri .= "/$data->{Attributes}->{'{}name'}->{Value}";
330             }
331              
332 0         0 return $uri;
333             }
334              
335             =head2 $pkg->make_link(\%data)
336              
337             Returns the output of $pkg->build_uri.
338              
339             If your program has defined a I callback (see above) then the output will be filtered through the callback before being returned your program.
340              
341             =cut
342              
343             sub make_link {
344 0     0 1 0 my $self = shift;
345 0         0 my $data = shift;
346              
347 0         0 my $link = $self->build_uri($data);
348              
349 0 0       0 if (my $c = $self->retrieve_callback("link")) {
350 0         0 $link = &$c($link);
351             }
352              
353 0         0 return $link;
354             }
355              
356             =head2 $pkg->on_enter_start_element(\%data)
357              
358             This method should be called as the first action in your class' I method. It will perform a number of helper actions, like keeping track of the current node level and the absolute path of the current document.
359              
360             Additionalllly it will check to see if the current node should be included or excluded based on rules defined by I.
361              
362             Returns true if everything is honky-dorry.
363              
364             Returns false if the current node is to be excluded or if the document has not "started" (see docs for the I method.)
365              
366             =cut
367              
368             sub on_enter_start_element {
369 71     71 1 54311 my $self = shift;
370 71         103 my $data = shift;
371              
372 71         8873 $self->SUPER::on_enter_start_element($data);
373 71         934 $self->{__PACKAGE__.'__last'} = $data->{Name};
374              
375 71 100       224 if ($data->{Name} eq "head") {
376 1         6 $self->{__PACKAGE__.'__head'} = 1;
377             }
378              
379 71 100       304 if ($data->{Name} =~ /^(directory|file)$/) {
380 19         59 $self->{__PACKAGE__.'__'.$1} ++;
381             # map { print " "; } (0..$self->{__PACKAGE__.'__'.$1});
382             # print $self->{__PACKAGE__.'__'.$1} ." ($1) $data->{Attributes}->{'{}name'}->{Value} ".__PACKAGE__."\n";
383             }
384              
385             #
386              
387 71 100 100     256 if ((! $self->{__PACKAGE__.'__start'}) && ($data->{Name} =~ /^(file|directory)$/)) {
388              
389 1 50       5 if (! exists($self->{__PACKAGE__.'__includeroot'})) {
390 1         17 $self->{__PACKAGE__.'__start'} = $self->current_level();
391 1         13 return 1;
392             }
393              
394             else {
395              
396 0 0 0     0 if ((! $self->{__PACKAGE__.'__includeroot'}) &&
      0        
397             (($self->{__PACKAGE__.'__file'} == 1) || ($self->{__PACKAGE__.'__directory'} == 2))) {
398              
399 0         0 $self->{__PACKAGE__.'__start'} = $self->current_level();
400 0         0 $self->grow_cwd($data);
401              
402 0         0 $self->compare($data);
403              
404 0 0       0 if (! $self->skip_level()) {
405 0         0 return 1;
406             }
407              
408 0         0 $self->prune_cwd($data);
409 0         0 return 0;
410             }
411             }
412              
413             }
414              
415             #
416              
417 70 100       174 if (! $self->{__PACKAGE__.'__start'}) {
418 6         19 return 0;
419             }
420              
421 64         232 $self->compare($data);
422              
423 64 50       7825 if ($self->skip_level()) {
424 0         0 return 0;
425             }
426              
427 64         373 $self->grow_cwd($data);
428 64         129 return 1;
429             }
430              
431             =head2 $pkg->on_enter_end_element(\%data)
432              
433              
434             =cut
435              
436             sub on_enter_end_element {
437 71     71 1 10067 my $self = shift;
438 71         98 my $data = shift;
439              
440 71 100       182 if ($data->{Name} eq "head") {
441 1         3 $self->{__PACKAGE__.'__head'} = 0;
442             }
443              
444 71         143 return 1;
445             }
446              
447             =head2 $pkg->on_exit_end_element(\%data)
448              
449             This method should be called as the first action in your class' I method.
450              
451             =cut
452              
453             sub on_exit_end_element {
454 71     71 1 291 my $self = shift;
455 71         81 my $data = shift;
456              
457 71 50       191 unless ($self->skip_level()) {
458 71         366 $self->prune_cwd($data);
459             }
460              
461 71 100       264 if ($data->{Name} =~ /^(directory|file)$/) {
462 19         70 $self->{__PACKAGE__.'__'.$1} --;
463             }
464              
465 71         240 $self->SUPER::on_exit_end_element($data);
466 71         1002 return 1;
467             }
468              
469             =head2 $pkg->on_characters(\%data)
470              
471             This method should be called as the first action in your class' I method.
472              
473             =cut
474              
475             sub on_characters {
476 0     0 1 0 my $self = shift;
477 0         0 my $data = shift;
478              
479 0 0       0 if ($self->{__PACKAGE__.'__head'}) {
480 0   0     0 $self->{ __PACKAGE__.'__'.$self->{__PACKAGE__.'__last'} } ||= $data->{Data};
481             }
482              
483 0         0 return 1;
484             }
485              
486             # =head2 $pkg->grow_cwd(\%data)
487             #
488             # =cut
489              
490             sub grow_cwd {
491 64     64 0 98 my $self = shift;
492 64         79 my $data = shift;
493              
494 64 100       241 if ($data->{Name} =~ /^(file|directory)$/) {
495 18         65 $self->{__PACKAGE__.'__loc'} .= "/$data->{Attributes}->{'{}name'}->{Value}";
496             }
497              
498 64 100       155 if ($data->{Name} eq "directory") {
499 10         28 $self->{__PACKAGE__.'__cwd'} .= "/$data->{Attributes}->{'{}name'}->{Value}";
500             # print STDERR $self->{__PACKAGE__.'__cwd'}."\n";
501             }
502              
503 64         106 return 1;
504             }
505              
506             # =head2 $pkg->prune_cwd(\%data)
507             #
508             # =cut
509              
510             sub prune_cwd {
511 71     71 0 87 my $self = shift;
512 71         96 my $data = shift;
513              
514 71 100       311 if ($data->{Name} =~ /^(file|directory)$/) {
515 19         144 $self->{__PACKAGE__.'__loc'} =~ s/^(.*)\/([^\/]+)$/$1/;
516             }
517              
518 71 100       178 if ($data->{Name} eq "directory") {
519 11         58 $self->{__PACKAGE__.'__cwd'} =~ s/^(.*)\/([^\/]+)$/$1/;
520             # print STDERR "[prune] ".$self->{__PACKAGE__.'__cwd'}."\n";
521             }
522              
523              
524 71         126 return 1;
525             }
526              
527             =head1 VERSION
528              
529             1.4.4
530              
531             =head1 DATE
532              
533             July 22, 2002
534              
535             =head1 AUTHOR
536              
537             Aaron Straup Cope
538              
539             =head1 TO DO
540              
541             =over
542              
543             =item *
544              
545             Investigate mucking with the symbol table to hide having to call the various on_foo_bar methods.
546              
547             =back
548              
549             =head1 SEE ALSO
550              
551             L
552              
553             L
554              
555             =head1 LICENSE
556              
557             Copright (c) 2002, Aaron Straup Cope. All Rights Reserved.
558              
559             This is free software, you may use it and distribute it under the same terms as Perl itself.
560              
561             =cut
562              
563             return 1;
564              
565             }
566