File Coverage

blib/lib/Data/Stag/BaseGenerator.pm
Criterion Covered Total %
statement 73 175 41.7
branch 18 60 30.0
condition 6 29 20.6
subroutine 15 36 41.6
pod 9 30 30.0
total 121 330 36.6


line stmt bran cond sub pod time code
1             # $Id: BaseGenerator.pm,v 1.15 2004/12/21 02:26:25 cmungall Exp $
2             #
3             # Copyright (C) 2002 Chris Mungall
4             #
5             # See also - http://stag.sourceforge.net
6             #
7             # This module is free software.
8             # You may distribute this module under the same terms as perl itself
9              
10             package Data::Stag::BaseGenerator;
11              
12             =head1 NAME
13              
14             Data::Stag::BaseGenerator - base class for parsers and other event generators
15              
16             =head1 SYNOPSIS
17              
18             # writing the parser
19             package MyParser;
20             use base qw(Data::Stag::BaseGenerator);
21            
22             sub parse_fh {
23             my ($self, $fh) = shift;
24              
25             my $lnum = 0;
26             $self->start_event('data');
27             while (<$fh>) {
28             ++$lnum;
29             $self->line_no($lnum);
30             # do stuff
31             $self->start_event('foo');
32              
33             # ...
34             $self->event(blah=>5);
35              
36             #
37             if (/incorrect_line/) {
38             $self->parse_err('line not in correct format');
39             }
40              
41             # ...
42             $self->end_event('foo');
43             }
44             $self->pop_stack_to_depth(0);
45             }
46             1;
47              
48             # using the parser
49             my $p = MyParser->new;
50             my $h = MyHandler->new; # see Data::Stag::BaseHandler
51             my $eh = Data::Stag->makehandler;
52             $p->handler($h);
53             $p->errhandler($eh);
54             $p->parse($file);
55              
56             # result tree
57             print $h->stag->xml;
58              
59             # write parse errs on standard err
60             printf \*STDERR $p->errhandler->stag->xml;
61              
62             # using the parser from the command line
63             unix> stag-parse.pl -p MyParser -w xml -e err.xml > out.xml
64              
65             # using the parser from the command line via intermediate handler
66             unix> stag-handle.pl -p MyParser -m MyHandler -w xml -e err.xml > out.xml
67              
68             =cut
69              
70             =head1 DESCRIPTION
71              
72             This is the base class for all parsers and event generators
73              
74             parsers/generators take some input (usually a filehandle, but a
75             generator could be a socket listener, for example) and fire stag
76             events
77              
78             stag events are
79              
80             =over
81              
82             =item start_event NODENAME
83              
84             =item evbody DATA
85              
86             =item end_event NODENAME {optional}
87              
88             =item event NODENAME DATA
89              
90             =back
91              
92             These events can be nested/hierarchical
93              
94             If uncaught, these events are stacked into a stag tree, which can be
95             written as xml or one of the other stag formats
96              
97             specialised handlers can be written to catch the events your parser
98             throws
99              
100             For example, you may wish to write a pod parser that generates nested
101             events like this:
102              
103            
104            
105             head1
106             NAME
107             Data::Stag - Structured Tags datastructures
108            
109             ...
110            
111              
112             (see the source for Data::Stag::PodParser for details)
113              
114             You can write handlers that take the pod-xml and generate something -
115             for example HTML
116              
117             parsers may encounter unexpected things along the way - they may throw
118             an exception, and fall over - or they may choose to fire an error
119             event. by default, error event streams are diverted to STDERR. You can
120             create your own error handlers
121              
122             =head1 PUBLIC METHODS
123              
124             =head3 new
125              
126             Title: new
127              
128             Args:
129             Return: L
130             Example:
131              
132             CONSTRUCTOR
133              
134             =head3 handler
135              
136             Title: handler
137             Function: GET/SET ACCESSOR METHOD
138             Args: handler L optional
139             Return: L
140             Example: $p->handler(MyHandler->new);
141              
142             each parser has a handler - all events generated are passed onto the
143             handler; the default handler simply sits there collecting events
144              
145             =head3 errhandler
146              
147             Title: errhandler
148             Function: GET/SET ACCESSOR METHOD
149             Args: handler L optional
150             Return: L
151             Example: $p->errhandler(Data::Stag->makehandler);
152              
153             each parser has an error handler - if the parser encounters things it
154             does not expect, it can pass errors to the errorhandler
155              
156             if no errorhandler is set, an XML event handler that writes to STDERR is used
157              
158             =head3 cache_errors
159              
160             Title: cache_errors
161             Args:
162             Return:
163             Example: $p->cache_errors
164              
165             If this is called, all errors will be cached rather than written to STDERR
166              
167             The error list can be accessed like this
168              
169             $p->parse($fn);
170             @errs = $p->errhandler->stag->get_error;
171              
172             =head2 parse
173              
174             Example - $parser->parse($file1, $file2);
175             Returns -
176             Args - filenames str-LIST
177              
178             parses a file
179              
180             =head2 parse
181              
182             Example - $parser->parse_fh($fh)
183             Returns -
184             Args - fh FILEHANDLE
185              
186             parses an open filehandle
187              
188             =cut
189              
190             =head1 PROTECTED METHODS
191              
192             These methods are only of interest if you are making your own
193             parser/generator class
194              
195             =over
196              
197             =item start_event NODENAME
198              
199             =item evbody DATA
200              
201             =item end_event NODENAME {optional}
202              
203             =item event NODENAME DATA
204              
205             =back
206              
207             =head1 SEE ALSO
208              
209             L
210             L
211              
212             =cut
213              
214 11     11   72 use Exporter;
  11         25  
  11         645  
215             @ISA = qw(Exporter);
216              
217 11     11   59 use Carp;
  11         29  
  11         769  
218 11     11   66 use FileHandle;
  11         29  
  11         70  
219 11     11   5483 use Data::Stag::Util qw(rearrange);
  11         25  
  11         617  
220 11     11   6901 use Data::Stag::null;
  11         30  
  11         670  
221 11     11   65 use strict qw(subs vars refs);
  11         106  
  11         29054  
222              
223             # Exceptions
224              
225             sub throw {
226 0     0 0 0 my $self = shift;
227 0         0 confess("@_");
228             }
229              
230             sub warn {
231 0     0 0 0 my $self = shift;
232 0         0 warn("@_");
233             }
234              
235             #sub last_evcall_type {
236             # my $self = shift;
237             # $self->{_last_evcall_type} = shift if @_;
238             # return $self->{_last_evcall_type};
239             #}
240              
241              
242             sub stack {
243 0     0 0 0 my $self = shift;
244 0 0       0 $self->{_stack} = shift if @_;
245 0 0       0 $self->{_stack} = [] unless $self->{_stack};
246 0         0 return $self->{_stack};
247             }
248              
249             sub stack_top {
250 0     0 0 0 my $self = shift;
251 0         0 $self->stack->[-1];
252             }
253              
254             sub push_stack {
255 0     0 0 0 my $self = shift;
256 0         0 push(@{$self->stack}, @_);
  0         0  
257             }
258              
259             sub pop_stack {
260 0     0 0 0 my $self = shift;
261 0         0 my $top = $self->stack_top;
262 0         0 $self->end_event($top);
263 0         0 $top;
264            
265             }
266              
267             sub pop_stack_to_depth {
268 0     0 0 0 my $self = shift;
269 0         0 my $depth = shift;
270 0         0 while ($depth < $self->stack_depth) {
271 0         0 $self->end_event;
272             }
273             }
274              
275             sub pop_all {
276 0     0 0 0 my $self = shift;
277 0         0 $self->pop_stack_to_depth(0);
278             }
279              
280             sub stack_depth {
281 0     0 0 0 my $self = shift;
282 0         0 scalar(@{$self->stack});
  0         0  
283             }
284              
285             *error_list = \&messages;
286              
287             sub message {
288 0     0 0 0 my $self = shift;
289 0         0 my $msg = shift;
290 0 0       0 unless (ref($msg)) {
291 0         0 $msg =
292             {msg=>$msg,
293             line=>$self->line,
294             line_no=>$self->line_no,
295             file=>$self->file};
296             }
297 0         0 push(@{$self->messages},
  0         0  
298             $msg);
299             }
300              
301              
302              
303             sub new {
304 17     17 1 53 my ($class, $init_h) = @_;
305 17         50 my $self = {};
306 17         250 $self->{handler} = Data::Stag::null->new;
307 17 50       67 if ($init_h) {
308 0         0 map {$self->{$_} = $init_h->{$_}} keys %$init_h;
  0         0  
309             }
310 17         97 bless $self, $class;
311 17 50       180 $self->init if $self->can("init");
312 17         68 $self;
313             }
314              
315             sub load_module {
316              
317 17     17 0 131 my $self = shift;
318 17         37 my $classname = shift;
319 17         28 my $mod = $classname;
320 17         146 $mod =~ s/::/\//g;
321              
322 17 50       194 if ($main::{"_<$mod.pm"}) {
323             }
324             else {
325 17         5464 require "$mod.pm";
326             }
327             }
328              
329             sub modulemap {
330 0     0 0 0 my $self = shift;
331 0 0       0 $self->{_modulemap} = shift if @_;
332 0         0 return $self->{_modulemap};
333             }
334              
335             sub handler {
336 17     17 1 30 my $self = shift;
337 17 50       68 if (@_) {
338 17         33 my $h = shift;
339 17 50 33     169 if ($h && !ref($h)) {
340 0         0 my $base = "Data::Stag:";
341 0         0 my $mm = $self->modulemap;
342 0 0 0     0 if ($mm && $mm->{$h}) {
343 0         0 $h = $mm->{$h};
344             }
345 0         0 $h =~ s/^xml$/$base:XMLWriter/;
346 0         0 $h =~ s/^perl$/$base:PerlWriter/;
347 0         0 $h =~ s/^sxpr$/$base:SxprWriter/;
348 0         0 $h =~ s/^itext$/$base:ITextWriter/;
349 0         0 $h =~ s/^graph$/$base:GraphWriter/;
350 0         0 $self->load_module($h);
351 0         0 $h = $h->new;
352 0 0       0 if ($h->can("fh")) {
353 0         0 $h->fh(\*STDOUT);
354             }
355             }
356 17         62 $self->{_handler} = $h;
357 17 50 33     135 if (!$h->errhandler && $self->errhandler) {
358 0         0 $h->errhandler($self->errhandler);
359             }
360             }
361             # return $self->{_handler} || Data::Stag::null->new();
362 17         54 return $self->{_handler};
363             }
364              
365             sub cache_errors {
366 0     0 1 0 my $self = shift;
367 0         0 return $self->errhandler(Data::Stag->makehandler);
368             }
369              
370             sub errhandler {
371 34     34 1 58 my $self = shift;
372 34 100       98 if (@_) {
373 17         44 $self->{errhandler} = shift;
374             }
375 34         125 return $self->{errhandler};
376             }
377              
378             sub err_event {
379 0     0 0 0 my $self = shift;
380 0 0       0 if (!$self->errhandler) {
381 0         0 $self->errhandler(Data::Stag->getformathandler('xml'));
382 0         0 $self->errhandler->fh(\*STDERR);
383            
384             # my $estag = Data::Stag->new(@_);
385             # eval {
386             # confess;
387             # };
388             # $estag->set_stacktrace($@);
389             # print STDERR $estag->xml;
390             # exit 1;
391             }
392 0 0       0 if (!$self->errhandler->depth) {
393 0         0 $self->errhandler->start_event("error_eventset");
394             }
395 0         0 $self->errhandler->event(@_);
396 0         0 return;
397             }
398              
399             sub err {
400 0     0 0 0 my $self = shift;
401 0         0 my $err = shift;
402 0 0       0 if (ref($err)) {
403 0         0 $self->throw("Bad error msg $err - must not by ref");
404             }
405 0         0 $self->err_event(message=>$err);
406 0         0 return;
407             }
408              
409             sub parse_err {
410 0     0 0 0 my $self = shift;
411 0   0     0 my $err = shift || '';
412 0 0       0 if (ref($err)) {
413 0         0 $self->throw("Bad error msg $err - must not by ref");
414             }
415 0         0 my @tags = ([message=>$err],[file=>$self->file]);
416 0         0 my $line = $self->line;
417 0 0       0 push(@tags, [line=>$line]) if defined $line;
418 0         0 my $line_no = $self->line_no;
419 0 0       0 push(@tags, [line_no=>$line_no]) if $line_no;
420 0         0 my $pclass = ref($self);
421 0         0 push(@tags, [parse_class=>"$pclass"]);
422 0         0 $self->err_event(error=>[@tags]);
423 0         0 return;
424             }
425              
426              
427             sub line_no {
428 0     0 0 0 my $self = shift;
429 0 0       0 $self->{_line_no} = shift if @_;
430 0         0 return $self->{_line_no};
431             }
432              
433             sub line {
434 0     0 0 0 my $self = shift;
435 0 0       0 $self->{_line} = shift if @_;
436 0         0 return $self->{_line};
437             }
438             sub file {
439 26     26 0 44 my $self = shift;
440 26 50       147 $self->{_file} = shift if @_;
441 26         73 return $self->{_file};
442             }
443              
444             sub finish {
445 0     0 0 0 my $self = shift;
446 0 0 0     0 if ($self->errhandler && $self->errhandler->depth) {
447 0         0 $self->errhandler->end_event;
448             }
449             }
450              
451              
452             sub parse {
453 17     17 1 36 my $self = shift;
454 17         98 my ($file, $str, $fh) =
455             rearrange([qw(file str fh)], @_);
456              
457 17 100       104 $self->file($file) if $file;
458 17 100       82 if ($str) {
    50          
459 4         28 $self->load_module("IO::String");
460 4   33     47 $fh = IO::String->new($str) || confess($str);
461             }
462             elsif ($file) {
463 13 50       50 if ($file eq '-') {
464 0         0 $fh = \*STDIN;
465             }
466             else {
467 13         89 $self->load_module("FileHandle");
468 13   33     121 $fh = FileHandle->new($file) || confess("cannot open file: $file");
469             }
470             }
471             else {
472             }
473 17 50       1786 if (!$fh) {
474 0         0 confess("no filehandle");
475             }
476 17         207 $self->parse_fh($fh);
477             # problem with IO::String closing in perl5.6.1
478 17 100       62 unless ($str) {
479             #$fh->close || confess("cannot close file: $file"); // problem stdout
480 13         60 $fh->close;
481             }
482 17         273 return;
483             }
484              
485             sub handler_err {
486 0     0 0 0 my $self = shift;
487 0         0 $self->err_event(error=>[[message=>'handler problem'],
488             [stack=>shift]]);
489             }
490              
491             sub errlist {
492 0     0 0 0 my $self = shift;
493 0         0 $self->finish;
494 0         0 my $eh = $self->errhandler;
495 0 0 0     0 if ($eh && $eh->stag && $eh->stag->data) {
      0        
496 0         0 return ($eh->stag->get_error);
497             }
498 0         0 return ();
499             }
500              
501              
502             # MAIN EVENT HANDLING
503             # start/end/event/body
504              
505             sub start_event {
506 480     480 1 615 my $self = shift;
507 480         503 push(@{$self->{_stack}}, $_[0]);
  480         1332  
508 480         2104 $self->{_handler}->start_event(@_);
509 480         920 return;
510             }
511             sub end_event {
512 480     480 1 576 my $self = shift;
513 480   66     1238 my $ev = shift || $self->{_stack}->[-1];
514 480         1413 $self->{_handler}->end_event($ev);
515              
516 480         545 my $out = pop(@{$self->{_stack}});
  480         1012  
517 480         1239 return;
518             }
519              
520             sub event {
521 0     0 1 0 my $self = shift;
522 0         0 $self->{_handler}->event(@_);
523 0         0 return;
524             }
525             sub evbody {
526 306     306 1 370 my $self = shift;
527 306         956 $self->{_handler}->evbody(@_);
528             # my $lc = $self->last_evcall_type;
529             # if ($lc && $lc eq 'evbody') {
530             # confess("attempting to event_body illegally (body already defined)");
531             # }
532              
533             # eval {
534             # $self->handler->evbody(@_);
535             # };
536             # if ($@) {
537             # $self->handler_err($@);
538             # }
539             # $self->last_evcall_type('evbody');
540 306         658 return;
541             }
542              
543             1;