File Coverage

blib/lib/Data/Stag/BaseHandler.pm
Criterion Covered Total %
statement 178 278 64.0
branch 53 114 46.4
condition 11 28 39.2
subroutine 29 51 56.8
pod 10 44 22.7
total 281 515 54.5


line stmt bran cond sub pod time code
1             # $Id: BaseHandler.pm,v 1.34 2008/06/03 17:31:15 cmungall Exp $
2             #
3             # This module is maintained by Chris Mungall
4              
5             =head1 NAME
6              
7             Data::Stag::BaseHandler - Base class for writing tag stream handlers
8              
9             =head1 SYNOPSIS
10              
11             # EXAMPLE 1
12             package MyPersonHandler;
13             use base qw(Data::Stag::BaseHandler);
14              
15             # handler that prints nodes as they are parsed;
16             # after each node is intercepted, it is discarded
17             # (it does not go to form the final tree)
18             sub e_person {
19             my $self = shift;
20             my $node = shift;
21             printf "Person name:%s address:%s\n",
22             $node->sget('name'), $node->sget('address');
23             return; # prune this from tree
24             }
25             1;
26            
27             # EXAMPLE 2
28             package MyStatsHandler;
29             use base qw(Data::Stag::BaseHandler);
30              
31             # handler that modifies tree as it goes
32             # changes inch10
33             # to cm25
34             sub e_measurement {
35             my $self = shift;
36             my $node = shift;
37             if ($node->sget('unit') eq 'inch') {
38             $node->set('unit', 'cm');
39             $node->set('quantity', $node->get('quantity') * 2.5);
40             }
41             return $node; # replace with new data in result tree
42             }
43             1;
44            
45             # Using the handlers
46             my $handler = MyHandler->new;
47             my $stag = Data::Stag->parse(-fh=>$fh, -handler=>$handler);
48              
49             # Using a handler from the command line:
50             unix> stag-handle.pl -m MyHandler input.xml > post-processed.xml
51              
52             =cut
53              
54             =head1 DESCRIPTION
55              
56             Default Simple Event Handler, other handlers inherit from this class
57              
58             See also L and L
59              
60             Stag has an event-handling architecture; parsers or generators
61             B or B events. Events can be hierarchical/nested, just
62             like stag nodes. These events are caught by handlers. By default,
63             uncaught events stack to form stag trees.
64              
65             Stag has built in parsers for parsing xml, sxpr and itext data. You
66             can construct your own parsers for dealing with your own formats
67             specific to your own data; these should inherit from
68             L
69              
70             Stag also has built in handlers for these formats. You can construct
71             your own - either as modules that inherit from this one, or as hashes
72             of anonymous subroutines.
73              
74             If you wish to write your own handler that writes out to another
75             format, you may wish to inherit from L
76              
77             =head2 CATCHING EVENTS
78              
79             This class catches Data::Stag node events (start, end and body) and allows the
80             subclassing module to intercept these. Unintercepted events get pushed
81             into a tree. The final tree is returned at the end of a parse() call
82              
83             This class can take SAX events and turn them into simple
84             Data::Stag events
85              
86             the events recognised are
87              
88             start_event(node-name)
89             evbody(node-data)
90             end_event(node-name)
91              
92             and also
93              
94             event(node-name, node-data|[nodes])
95              
96             which is just a wrapper for the other events
97              
98             you can either intercept these methods; or you can define methods
99              
100              
101             s_
102             e_
103              
104             that get called on the start/end of an event; you can dynamically
105             change the structure of the tree by returning nodes from these methods.
106              
107             # the follow handler prunes nodes from the tree, and writes
108             # out data from the node
109             # when parsing large datasets, it can be a good idea to prune nodes
110             # from the tree, so the result tree of the parse is not too big
111             my $h = Data::Stag->makehandler( foo => 0,
112             person => sub {
113             my $self = shift;
114             my $node = shift;
115             printf "Person name:%s address:%s\n",
116             $node->sget('name'), $node->sget('address');
117             return;
118             });
119             my $parser = MyParser->new;
120             $parser->handler($h);
121             $parser->parse(-fh=>$fh);
122             my $result_tree = $h->stag;
123              
124              
125             =head1 PUBLIC METHODS -
126              
127             =head3 new
128              
129             Title: new
130              
131             Args:
132             Return: L
133             Example:
134              
135             returns the tree that was built from all uncaught events
136              
137             =head3 tree (stag)
138              
139             Title: tree
140             Synonym: stag
141              
142             Args:
143             Return: L
144             Example: print $parser->handler->tree->xml;
145              
146             returns the tree that was built from all uncaught events
147              
148             =head1 CAUGHT EVENTS
149              
150             A L class will generate events by calling the following methods on this class:
151              
152             =over
153              
154             =item start_event NODENAME
155              
156             =item evbody DATA
157              
158             =item end_event NODENAME {optional}
159              
160             =item event NODENAME DATA
161              
162             =back
163              
164             These events can be nested/hierarchical
165              
166             If uncaught, these events are stacked into a stag tree, which can be
167             written as xml or one of the other stag formats
168              
169              
170             =head1 PROTECTED METHODS -
171              
172             =head3 s_*
173              
174             Args: handler L
175             Return:
176             Example:
177              
178             autogenerated method - called by the parser when ever it starts a
179             node; * matches the node name
180              
181             override this class providing the name of the node you wish to intercept
182              
183             =head3 e_*
184              
185             Args: handler L, node L
186             Return: node L
187             Example:
188              
189             autogenerated method - called by the parser when ever it ends a
190             node; * matches the node name
191              
192             override this class providing the name of the node you wish to intercept
193              
194             =head3 CONSUMES
195              
196             define this in your handler class to make explicit the list of node
197             names that your parser consumes; this is then used if your handler is
198             placed in a chain
199              
200             package MyHandler;
201             use base qw(Data::Stag::BaseHandler);
202             sub CONSUMES {qw(person city)}
203             sub e_person {....}
204             sub e_city {....}
205              
206             =head3 depth
207              
208             Title: depth
209              
210             Args:
211             Return: depth int
212             Example:
213              
214             depth of the nested event tree
215              
216             =head3 up
217              
218             Title: up
219              
220             Args: dist int
221             Return: node stag
222             Example: $stag->up(-2);
223              
224             when called when intercepting a node , this will look B up
225             the tree to find the container node
226              
227             For example, if our data contains the node below:
228              
229            
230            
231             1
232            
233            
234             2
235            
236            
237              
238             # and we have the following code:
239             $h = Data::Stag->makehandler(foo=>sub {
240             my ($self, $foo) = @_;
241             print $foo->up(1)->xml;
242             return});
243              
244             The handler will be called twice; it will print the structure of
245             the containing node, but the first time round, the node
246             will not be complete
247              
248             =head3 up_to
249              
250             Title: up_to
251              
252             Args: nodename str
253             Return: node stag
254             Example: $stag->up_to('blah');
255              
256             Similar to up(), but it will go up the container event nodes until it
257             finds one with the matching name
258              
259             =cut
260              
261             package Data::Stag::BaseHandler;
262              
263 20     20   124 use strict;
  20         39  
  20         745  
264 20     20   103 use Exporter;
  20         34  
  20         900  
265             #use XML::Filter::Base;
266 20     20   105 use vars qw(@ISA @EXPORT_OK);
  20         34  
  20         1041  
267 20     20   102 use base qw(Exporter);
  20         38  
  20         2192  
268 20     20   118 use Carp;
  20         35  
  20         1311  
269 20     20   185 use Data::Stag;
  20         32  
  20         682  
270              
271 20     20   92 use vars qw($VERSION);
  20         30  
  20         75582  
272             $VERSION="0.14";
273              
274 0     0 0 0 sub EMITS { () }
275 0     0 1 0 sub CONSUMES { () }
276 1029     1029 0 1762 sub REPLACE { () }
277 1029     1029 0 2926 sub SKIP { () }
278              
279             sub tree {
280 535     535 1 712 my $self = shift;
281 535 100       1286 $self->{_tree} = shift if @_;
282 535   100     11064 return Data::Stag::stag_nodify($self->{_tree} || []);
283             }
284             *stag = \&tree;
285              
286             # deprecated
287             sub messages {
288 93     93 0 163 my $self = shift;
289 93 50       349 $self->{_messages} = shift if @_;
290 93         188 return $self->{_messages};
291             }
292              
293             *error_list = \&messages;
294              
295             sub message {
296 0     0 0 0 my $self = shift;
297 0         0 push(@{$self->messages},
  0         0  
298             shift);
299             }
300              
301              
302             sub new {
303 95     95 1 319 my ($class, @args) = @_;
304             # my $self = XML::Filter::Base::new(@_);
305 95         194 my $self = {};
306 95         263 bless $self, $class;
307 95         657 $self->{node} = [];
308 95         250 $self->{_stack} = [];
309 95 50       1186 $self->init(@args) if $self->can("init");
310 95         340 $self;
311             }
312              
313             sub errhandler {
314 17     17 0 161 my $self = shift;
315 17 50       66 if (@_) {
316 0         0 $self->{errhandler} = shift;
317             }
318 17         179 return $self->{errhandler};
319             }
320              
321             sub err_event {
322 0     0 0 0 my $self = shift;
323 0 0       0 if (!$self->errhandler) {
324 0         0 $self->errhandler(Data::Stag->getformathandler('xml'));
325 0         0 $self->errhandler->fh(\*STDERR);
326            
327             # my $estag = Data::Stag->new(@_);
328             # eval {
329             # confess;
330             # };
331             # $estag->set_stacktrace($@);
332             # print STDERR $estag->xml;
333             # print STDERR "NO ERRHANDLER SET\n";
334             # exit 1;
335             }
336 0 0       0 if (!$self->errhandler->depth) {
337 0         0 $self->errhandler->start_event("error_eventset");
338             }
339 0         0 $self->errhandler->event(@_);
340 0         0 return;
341             }
342              
343             sub throw {
344 0     0 0 0 my $self = shift;
345 0         0 confess("@_");
346             }
347              
348             sub err {
349 0     0 0 0 my $self = shift;
350 0         0 my $err = shift;
351 0 0       0 if (ref($err)) {
352 0         0 $self->throw("Bad error msg $err - must not by ref");
353             }
354 0         0 $self->err_event(message=>$err);
355 0         0 return;
356             }
357              
358             sub trap_h {
359 5     5 0 11 my $self = shift;
360 5 50       22 $self->{_trap_h} = shift if @_;
361 5         13 return $self->{_trap_h};
362             }
363              
364             sub catch_end_sub {
365 0     0 0 0 my $self = shift;
366 0 0       0 $self->{_catch_end_sub} = shift if @_;
367 0         0 return $self->{_catch_end_sub};
368             }
369              
370              
371             sub stack {
372 3407     3407 0 3976 my $self = shift;
373 3407 50       6463 $self->{_stack} = shift if @_;
374 3407         8383 return $self->{_stack};
375             }
376             *elt_stack = \&stack;
377              
378             sub in {
379 0     0 0 0 my $self = shift;
380 0         0 my $in = shift;
381 0 0       0 return 1 if grep {$in eq $_} @{$self->stack};
  0         0  
  0         0  
382             }
383              
384             sub depth {
385 59     59 1 102 my $self = shift;
386 59         99 return scalar(@{$self->stack});
  59         220  
387             }
388              
389              
390             sub node {
391 2     2 0 4 my $self = shift;
392 2 50       6 $self->{node} = shift if @_;
393 2         8 return $self->{node};
394             }
395              
396             sub remove_elts {
397 0     0 0 0 my $self = shift;
398 0 0       0 $self->{_remove_elts} = [@_] if @_;
399 0 0       0 return @{$self->{_remove_elts} || []};
  0         0  
400             }
401             *kill_elts = \&remove_elts;
402              
403             sub flatten_elts {
404 0     0 0 0 my $self = shift;
405 0 0       0 $self->{_flatten_elts} = [@_] if @_;
406 0 0       0 return @{$self->{_flatten_elts} || []};
  0         0  
407             }
408              
409             sub skip_elts {
410 0     0 0 0 my $self = shift;
411 0 0       0 $self->{_skip_elts} = [@_] if @_;
412 0 0       0 return @{$self->{_skip_elts} || []};
  0         0  
413             }
414             *raise_elts = \&skip_elts;
415              
416             sub rename_elts {
417 0     0 0 0 my $self = shift;
418 0         0 confess "experimental feature - deprecated";
419 0 0       0 $self->{_rename_elts} = {@_} if @_;
420 0 0       0 return %{$self->{_rename_elts} || {}};
  0         0  
421             }
422              
423             sub lookup {
424 0     0 0 0 my $tree = shift;
425 0         0 my $k = shift;
426 0         0 my @v = map {$_->[1]} grep {$_->[0] eq $k} @$tree;
  0         0  
  0         0  
427 0 0       0 if (wantarray) {
428 0         0 return @v;
429             }
430 0         0 $v[0];
431             }
432              
433             sub init {
434 93     93 0 154 my $self = shift;
435 93         9269 $self->messages([]);
436 93         264 $self->{node} = [];
437             }
438              
439             sub perlify {
440 0     0 0 0 my $word = shift;
441 0         0 $word =~ s/\-/_/g;
442 0         0 $word =~ s/\:/_/g;
443 0         0 return $word;
444             }
445              
446             # start_event is called at the beginning of any event;
447             # equivalent to the event fired at the opening of any
448             # xml in a SAX parser
449              
450             # action: checks for method of name s_EVENTNAME()
451             # calls it if it is present
452             sub start_event {
453 517     517 1 606 my $self = shift;
454 517         614 my $ev = shift;
455 517 50       1279 if (grep {$ev eq $_} $self->SKIP) {
  0         0  
456 0         0 return;
457             }
458 517         975 my %REPLACE = $self->REPLACE;
459 517 50       1011 if (%REPLACE) {
460 0   0     0 $ev = $REPLACE{$ev} || $ev;
461             }
462 517         820 my $m = 's_'.$ev;
463 517         608 $m =~ tr/\-\:/_/;
464              
465 517 50       3720 if ($self->can($m)) {
    50          
466 0         0 $self->$m($ev);
467             }
468             elsif ($self->can("catch_start")) {
469 0         0 $self->catch_start($ev);
470             }
471             else {
472             }
473              
474 517         587 push(@{$self->{_stack}}, $ev);
  517         1165  
475              
476 517         1026 my $el = [$ev];
477 517         591 push(@{$self->{node}}, $el);
  517         998  
478 517         1284 $el;
479             }
480              
481             # deprecated
482 0     0 0 0 sub S {shift->start_event(@_)}
483              
484              
485             sub evbody {
486 330     330 1 394 my $self = shift;
487 330         562 foreach my $arg (@_) {
488 330 50       554 if (ref($arg)) {
489 0         0 $self->event(@$arg);
490             }
491             else {
492 330         635 my $node = $self->{node};
493 330         373 my $el = $node->[$#{$node}];
  330         622  
494 330 50       741 confess unless $el;
495 330         1443 $el->[1] = $arg;
496             }
497             }
498 330         851 return;
499             }
500              
501             # deprecated
502 0     0 0 0 sub B {shift->evbody(@_)}
503 0     0 0 0 sub b {shift->evbody(@_)}
504              
505             sub up {
506 0     0 1 0 my $self = shift;
507 0   0     0 my $dist = shift || 1;
508 0         0 my $node = $self->node->[-$dist];
509 0         0 return Data::Stag::stag_nodify($node);
510             }
511              
512             sub up_to {
513 2     2 1 12 my $self = shift;
514 2   33     7 my $n = shift || confess "must specify node name";
515 2   50     7 my $nodes = $self->node || [];
516 2         5 my ($node) = grep {$_->[0] eq $n} @$nodes;
  4         12  
517 0         0 confess " no such node name as $n; valid names are:".
518 2 50       5 join(", ", map {$_->[0]} @$nodes)
519             unless $node;
520 2         6 return Data::Stag::stag_nodify($node);
521             }
522              
523             # end_event is called at the end of any event;
524             # equivalent to the event fired at the closing of any
525             # xml in a SAX parser
526              
527             # action: checks for method of name e_EVENTNAME()
528             # calls it if it is present
529             sub end_event {
530 512     512 1 598 my $self = shift;
531 512   50     1000 my $ev = shift || '';
532              
533 512 50       919 if (grep {$ev eq $_} $self->SKIP) {
  0         0  
534 0         0 return;
535             }
536 512         952 my %REPLACE = $self->REPLACE;
537 512 50       1011 if (%REPLACE) {
538 0   0     0 $ev = $REPLACE{$ev} || $ev;
539             }
540              
541 512         739 my $stack = $self->{_stack};
542 512         568 pop(@$stack);
543              
544 512         789 my $node = $self->{node}; # array of (0..$indent)
545 512         867 my $topnode = pop @$node; # node we are closing now
546              
547             # my %rename = $self->rename_elts;
548             # if ($rename{$ev}) {
549             # $ev = $rename{$ev};
550             # $topnode->[0] = $ev;
551             # }
552            
553 512 50       1178 if (!ref($topnode)) {
554 0         0 confess("ASSERTION ERROR: $topnode not an array");
555             }
556 512 100       1079 if (scalar(@$topnode) < 2) {
557             # NULLs are treated the same as
558             # empty strings
559             # [if we have empty tags
560             # then no evbody will be called - we have to
561             # fill in the equivalent of a null evbody here]
562             # push(@$topnode, '');
563 3         7 push(@$topnode, '');
564             }
565 512         690 my $topnodeval = $topnode->[1];
566              
567 512         774 my @R = ($topnode); # return
568              
569             # check for trapped events; trap_h is a hash keyed by node name
570             # the value is a subroutine to be called at the end of that node
571 512         718 my $trap_h = $self->{_trap_h};
572 512 100       933 if ($trap_h) {
573 126         146 my $trapped_ev = $ev;
574 126         278 my @P = @$stack;
575 126   100     464 while (!defined($trap_h->{$trapped_ev}) && scalar(@P)) {
576 296         388 my $next = pop @P;
577 296         1142 $trapped_ev = "$next/$trapped_ev";
578             }
579              
580 126 100       306 if (defined($trap_h->{$trapped_ev})) {
581 19 100       43 if ($trap_h->{$trapped_ev}) {
582             # call anonymous subroutine supplied in hash
583 18         47 @R = $trap_h->{$trapped_ev}->($self, Data::Stag::stag_nodify($topnode));
584             }
585             else {
586 1         3 @R = ();
587             }
588             }
589             }
590              
591 512         837 my $m = 'e_'.$ev;
592 512         608 $m =~ tr/\-\:/_/;
593              
594 512 50       4339 if ($self->can($m)) {
    50          
    50          
595 0         0 @R = $self->$m(Data::Stag::stag_nodify($topnode));
596             }
597             elsif ($self->can("catch_end")) {
598 0         0 @R = $self->catch_end($ev, Data::Stag::stag_nodify($topnode));
599             }
600             elsif ($self->{_catch_end_sub}) {
601 0         0 @R = $self->{_catch_end_sub}->($self, Data::Stag::stag_nodify($topnode));
602             }
603             else {
604             # do nothing
605             }
606              
607 512 100       1379 if (@$node) {
608 494         671 my $el = $node->[-1]; # next node up
609 494 100       1209 if (!$el->[1]) {
610 184         560 $el->[1] = [];
611             }
612            
613 494 50 66     2215 if (scalar(@R) && !$R[0]) {
614 0         0 @R = ();
615             }
616 494         548 push(@{$el->[1]}, @R);
  494         1123  
617             }
618              
619 512         1462 $self->tree(Data::Stag::stag_nodify($topnode));
620 512 100       1115 if (!@$stack) {
621             # final event; call end_stag if required
622 16 50       137 if ($self->can("end_stag")) {
623 0         0 $self->end_stag($self->tree);
624             }
625             }
626              
627 512         1516 return @R;
628             }
629 0     0 0 0 sub E {shift->end_event(@_)}
630 0     0 0 0 sub e {shift->end_event(@_)}
631              
632              
633             sub popnode {
634 5     5 0 8 my $self = shift;
635 5         8 my $node = $self->{node};
636 5         7 my $topnode = pop @$node;
637 5         12 return $topnode;
638             }
639              
640             sub event {
641 925     925 1 1080 my $self = shift;
642 925         1166 my $ev = shift;
643 925         1170 my $st = shift;
644 925         2228 $self->start_event($ev);
645 925 100       1620 if (ref($st)) {
646 306 50       722 if (ref($st) ne "ARRAY") {confess($st)}
  0         0  
647 306         546 foreach (@$st) {
648 884 50       3041 confess("$ev $st $_") unless ref($_);
649 884         2113 $self->event(@$_)
650             }
651             }
652             else {
653 619         1703 $self->evbody($st);
654             }
655 925         2362 $self->end_event($ev);
656             }
657             *ev = \&event;
658              
659              
660             sub print {
661 0     0 0 0 my $self = shift;
662 0         0 print "@_";
663             }
664              
665             sub printf {
666 0     0 0 0 my $self = shift;
667 0         0 printf @_;
668             }
669              
670              
671             sub start_element {
672 37     37 0 50 my ($self, $element) = @_;
673              
674 37         50 my $name = $element->{Name};
675 37         45 my $atts = $element->{Attributes};
676              
677 37 100       71 if (!$self->{sax_stack}) {
678 1         4 $self->{sax_stack} = [];
679             }
680 37         36 push(@{$self->{sax_stack}}, $name);
  37         66  
681 37         40 push(@{$self->{is_nonterminal_stack}}, 0);
  37         51  
682 37 100       39 if (@{$self->{is_nonterminal_stack}} > 1) {
  37         83  
683 36         51 $self->{is_nonterminal_stack}->[-2] = 1;
684             }
685              
686             # check if we need an event
687             # for any preceeding pcdata
688 37         46 my $str = $self->{__str};
689 37 50       69 if (defined $str) {
690             # mixed attribute text - use element '.'
691 0         0 $str =~ s/^\s*//;
692 0         0 $str =~ s/\s*$//;
693 0 0       0 if ($str) {
694 0 0       0 $self->event(".", $str) if $str;
695             }
696 0         0 $self->{__str} = undef;
697             }
698              
699 37         66 $self->start_event($name);
700 37 50 33     78 if ($atts && %$atts) {
701             # treat atts same way as SXML
702 0         0 $self->start_event('@');
703 0         0 foreach my $k (keys %$atts) {
704 0         0 $self->event("$k", $atts->{$k});
705 0         0 $self->{is_nonterminal_stack}->[-1] = 1;
706             }
707 0         0 $self->end_event('@');
708             }
709 37         71 return $element;
710             }
711              
712             sub characters {
713 24     24 0 32 my ($self, $characters) = @_;
714 24         37 my $char = $characters->{Data};
715 24 50       47 if (defined $char) {
716 24 50       58 $self->{__str} = "" unless defined $self->{__str};
717 24         39 $self->{__str} .= $char;
718             }
719 24         47 return;
720             }
721              
722             sub end_element {
723 37     37 0 46 my ($self, $element) = @_;
724 37         48 my $name = $element->{Name};
725 37         49 my $str = $self->{__str};
726 37         39 my $parent = pop(@{$self->{sax_stack}});
  37         65  
727 37         42 my $is_nt = pop(@{$self->{is_nonterminal_stack}});
  37         57  
728 37 100       85 if (defined $str) {
729 24         71 $str =~ s/^\s*//;
730 24         94 $str =~ s/\s*$//;
731 24 50 33     62 if ($str || $str eq '0') {
732 24 50       36 if ($is_nt) {
733 0         0 $self->event('.' =>
734             $str);
735            
736             }
737             else {
738 24         49 $self->evbody($str);
739             }
740             }
741             }
742 37         100 $self->end_event($name);
743 37         179 $self->{__str} = undef;
744             # $self->{Handler}->end_element($element);
745             }
746              
747 1     1 0 4 sub start_document {
748             }
749              
750 1     1 0 4 sub end_document {
751             }
752              
753              
754             1