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
209
I Got My Title From the File
210
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