/[suikacvs]/messaging/manakai/lib/Error.pm
Suika

Contents of /messaging/manakai/lib/Error.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (hide annotations) (download)
Sat Jan 17 08:31:25 2004 UTC (20 years, 4 months ago) by wakaba
Branch: MAIN
CVS Tags: before-dis2-200411, manakai-release-0-3-2, manakai-release-0-3-1, manakai-release-0-4-0, manakai-200612, HEAD
Branch point for: experimental-xml-parser-200401
Imported to CVS

1 wakaba 1.1 # Error.pm
2     #
3     # Copyright (c) 1997-8 Graham Barr <gbarr@ti.com>. All rights reserved.
4     # This program is free software; you can redistribute it and/or
5     # modify it under the same terms as Perl itself.
6     #
7     # Based on my original Error.pm, and Exceptions.pm by Peter Seibel
8     # <peter@weblogic.com> and adapted by Jesse Glick <jglick@sig.bsh.com>.
9     #
10     # but modified ***significantly***
11    
12     package Error;
13    
14     use strict;
15     use vars qw($VERSION);
16     use 5.004;
17    
18     $VERSION = "0.15";
19    
20     use overload (
21     '""' => 'stringify',
22     '0+' => 'value',
23     'bool' => sub { return 1; },
24     'fallback' => 1
25     );
26    
27     $Error::Depth = 0; # Depth to pass to caller()
28     $Error::Debug = 0; # Generate verbose stack traces
29     @Error::STACK = (); # Clause stack for try
30     $Error::THROWN = undef; # last error thrown, a workaround until die $ref works
31    
32     my $LAST; # Last error created
33     my %ERROR; # Last error associated with package
34    
35     # Exported subs are defined in Error::subs
36    
37     sub import {
38     shift;
39     local $Exporter::ExportLevel = $Exporter::ExportLevel + 1;
40     Error::subs->import(@_);
41     }
42    
43     # I really want to use last for the name of this method, but it is a keyword
44     # which prevent the syntax last Error
45    
46     sub prior {
47     shift; # ignore
48    
49     return $LAST unless @_;
50    
51     my $pkg = shift;
52     return exists $ERROR{$pkg} ? $ERROR{$pkg} : undef
53     unless ref($pkg);
54    
55     my $obj = $pkg;
56     my $err = undef;
57     if($obj->isa('HASH')) {
58     $err = $obj->{'__Error__'}
59     if exists $obj->{'__Error__'};
60     }
61     elsif($obj->isa('GLOB')) {
62     $err = ${*$obj}{'__Error__'}
63     if exists ${*$obj}{'__Error__'};
64     }
65    
66     $err;
67     }
68    
69     # Return as much information as possible about where the error
70     # happened. The -stacktrace element only exists if $Error::DEBUG
71     # was set when the error was created
72    
73     sub stacktrace {
74     my $self = shift;
75    
76     return $self->{'-stacktrace'}
77     if exists $self->{'-stacktrace'};
78    
79     my $text = exists $self->{'-text'} ? $self->{'-text'} : "Died";
80    
81     $text .= sprintf(" at %s line %d.\n", $self->file, $self->line)
82     unless($text =~ /\n$/s);
83    
84     $text;
85     }
86    
87     # Allow error propagation, ie
88     #
89     # $ber->encode(...) or
90     # return Error->prior($ber)->associate($ldap);
91    
92     sub associate {
93     my $err = shift;
94     my $obj = shift;
95    
96     return unless ref($obj);
97    
98     if($obj->isa('HASH')) {
99     $obj->{'__Error__'} = $err;
100     }
101     elsif($obj->isa('GLOB')) {
102     ${*$obj}{'__Error__'} = $err;
103     }
104     $obj = ref($obj);
105     $ERROR{ ref($obj) } = $err;
106    
107     return;
108     }
109    
110     sub new {
111     my $self = shift;
112     my($pkg,$file,$line) = caller($Error::Depth);
113    
114     my $err = bless {
115     '-package' => $pkg,
116     '-file' => $file,
117     '-line' => $line,
118     @_
119     }, $self;
120    
121     $err->associate($err->{'-object'})
122     if(exists $err->{'-object'});
123    
124     # To always create a stacktrace would be very inefficient, so
125     # we only do it if $Error::Debug is set
126    
127     if($Error::Debug) {
128     require Carp;
129     local $Carp::CarpLevel = $Error::Depth;
130     my $text = defined($err->{'-text'}) ? $err->{'-text'} : "Error";
131     my $trace = Carp::longmess($text);
132     # Remove try calls from the trace
133     $trace =~ s/(\n\s+\S+__ANON__[^\n]+)?\n\s+eval[^\n]+\n\s+Error::subs::try[^\n]+(?=\n)//sog;
134     $trace =~ s/(\n\s+\S+__ANON__[^\n]+)?\n\s+eval[^\n]+\n\s+Error::subs::run_clauses[^\n]+\n\s+Error::subs::try[^\n]+(?=\n)//sog;
135     $err->{'-stacktrace'} = $trace
136     }
137    
138     $@ = $LAST = $ERROR{$pkg} = $err;
139     }
140    
141     # Throw an error. this contains some very gory code.
142    
143     sub throw {
144     my $self = shift;
145     local $Error::Depth = $Error::Depth + 1;
146    
147     # if we are not rethrow-ing then create the object to throw
148     $self = $self->new(@_) unless ref($self);
149    
150     die $Error::THROWN = $self;
151     }
152    
153     # syntactic sugar for
154     #
155     # die with Error( ... );
156    
157     sub with {
158     my $self = shift;
159     local $Error::Depth = $Error::Depth + 1;
160    
161     $self->new(@_);
162     }
163    
164     # syntactic sugar for
165     #
166     # record Error( ... ) and return;
167    
168     sub record {
169     my $self = shift;
170     local $Error::Depth = $Error::Depth + 1;
171    
172     $self->new(@_);
173     }
174    
175     # catch clause for
176     #
177     # try { ... } catch CLASS with { ... }
178    
179     sub catch {
180     my $pkg = shift;
181     my $code = shift;
182     my $clauses = shift || {};
183     my $catch = $clauses->{'catch'} ||= [];
184    
185     unshift @$catch, $pkg, $code;
186    
187     $clauses;
188     }
189    
190     # Object query methods
191    
192     sub object {
193     my $self = shift;
194     exists $self->{'-object'} ? $self->{'-object'} : undef;
195     }
196    
197     sub file {
198     my $self = shift;
199     exists $self->{'-file'} ? $self->{'-file'} : undef;
200     }
201    
202     sub line {
203     my $self = shift;
204     exists $self->{'-line'} ? $self->{'-line'} : undef;
205     }
206    
207     sub text {
208     my $self = shift;
209     exists $self->{'-text'} ? $self->{'-text'} : undef;
210     }
211    
212     # overload methods
213    
214     sub stringify {
215     my $self = shift;
216     defined $self->{'-text'} ? $self->{'-text'} : "Died";
217     }
218    
219     sub value {
220     my $self = shift;
221     exists $self->{'-value'} ? $self->{'-value'} : undef;
222     }
223    
224     package Error::Simple;
225    
226     @Error::Simple::ISA = qw(Error);
227    
228     sub new {
229     my $self = shift;
230     my $text = "" . shift;
231     my $value = shift;
232     my(@args) = ();
233    
234     local $Error::Depth = $Error::Depth + 1;
235    
236     @args = ( -file => $1, -line => $2)
237     if($text =~ s/ at (\S+) line (\d+)(\.\n)?$//s);
238    
239     push(@args, '-value', 0 + $value)
240     if defined($value);
241    
242     $self->SUPER::new(-text => $text, @args);
243     }
244    
245     sub stringify {
246     my $self = shift;
247     my $text = $self->SUPER::stringify;
248     $text .= sprintf(" at %s line %d.\n", $self->file, $self->line)
249     unless($text =~ /\n$/s);
250     $text;
251     }
252    
253     ##########################################################################
254     ##########################################################################
255    
256     # Inspired by code from Jesse Glick <jglick@sig.bsh.com> and
257     # Peter Seibel <peter@weblogic.com>
258    
259     package Error::subs;
260    
261     use Exporter ();
262     use vars qw(@EXPORT_OK @ISA %EXPORT_TAGS);
263    
264     @EXPORT_OK = qw(try with finally except otherwise);
265     %EXPORT_TAGS = (try => \@EXPORT_OK);
266    
267     @ISA = qw(Exporter);
268    
269     sub run_clauses ($$$\@) {
270     my($clauses,$err,$wantarray,$result) = @_;
271     my $code = undef;
272    
273     $err = new Error::Simple($err) unless ref($err);
274    
275     CATCH: {
276    
277     # catch
278     my $catch;
279     if(defined($catch = $clauses->{'catch'})) {
280     my $i = 0;
281    
282     CATCHLOOP:
283     for( ; $i < @$catch ; $i += 2) {
284     my $pkg = $catch->[$i];
285     unless(defined $pkg) {
286     #except
287     splice(@$catch,$i,2,$catch->[$i+1]->());
288     $i -= 2;
289     next CATCHLOOP;
290     }
291     elsif($err->isa($pkg)) {
292     $code = $catch->[$i+1];
293     while(1) {
294     my $more = 0;
295     local($Error::THROWN);
296     my $ok = eval {
297     if($wantarray) {
298     @{$result} = $code->($err,\$more);
299     }
300     elsif(defined($wantarray)) {
301     @{$result} = ();
302     $result->[0] = $code->($err,\$more);
303     }
304     else {
305     $code->($err,\$more);
306     }
307     1;
308     };
309     if( $ok ) {
310     next CATCHLOOP if $more;
311     undef $err;
312     }
313     else {
314     $err = defined($Error::THROWN)
315     ? $Error::THROWN : $@;
316     $err = new Error::Simple($err)
317     unless ref($err);
318     }
319     last CATCH;
320     };
321     }
322     }
323     }
324    
325     # otherwise
326     my $owise;
327     if(defined($owise = $clauses->{'otherwise'})) {
328     my $code = $clauses->{'otherwise'};
329     my $more = 0;
330     my $ok = eval {
331     if($wantarray) {
332     @{$result} = $code->($err,\$more);
333     }
334     elsif(defined($wantarray)) {
335     @{$result} = ();
336     $result->[0] = $code->($err,\$more);
337     }
338     else {
339     $code->($err,\$more);
340     }
341     1;
342     };
343     if( $ok ) {
344     undef $err;
345     }
346     else {
347     $err = defined($Error::THROWN)
348     ? $Error::THROWN : $@;
349     $err = new Error::Simple($err)
350     unless ref($err);
351     }
352     }
353     }
354     $err;
355     }
356    
357     sub try (&;$) {
358     my $try = shift;
359     my $clauses = @_ ? shift : {};
360     my $ok = 0;
361     my $err = undef;
362     my @result = ();
363    
364     unshift @Error::STACK, $clauses;
365    
366     my $wantarray = wantarray();
367    
368     do {
369     local $Error::THROWN = undef;
370    
371     $ok = eval {
372     if($wantarray) {
373     @result = $try->();
374     }
375     elsif(defined $wantarray) {
376     $result[0] = $try->();
377     }
378     else {
379     $try->();
380     }
381     1;
382     };
383    
384     $err = defined($Error::THROWN) ? $Error::THROWN : $@
385     unless $ok;
386     };
387    
388     shift @Error::STACK;
389    
390     $err = run_clauses($clauses,$err,wantarray,@result)
391     unless($ok);
392    
393     $clauses->{'finally'}->()
394     if(defined($clauses->{'finally'}));
395    
396     throw $err if defined($err);
397    
398     wantarray ? @result : $result[0];
399     }
400    
401     # Each clause adds a sub to the list of clauses. The finally clause is
402     # always the last, and the otherwise clause is always added just before
403     # the finally clause.
404     #
405     # All clauses, except the finally clause, add a sub which takes one argument
406     # this argument will be the error being thrown. The sub will return a code ref
407     # if that clause can handle that error, otherwise undef is returned.
408     #
409     # The otherwise clause adds a sub which unconditionally returns the users
410     # code reference, this is why it is forced to be last.
411     #
412     # The catch clause is defined in Error.pm, as the syntax causes it to
413     # be called as a method
414    
415     sub with (&;$) {
416     @_
417     }
418    
419     sub finally (&) {
420     my $code = shift;
421     my $clauses = { 'finally' => $code };
422     $clauses;
423     }
424    
425     # The except clause is a block which returns a hashref or a list of
426     # key-value pairs, where the keys are the classes and the values are subs.
427    
428     sub except (&;$) {
429     my $code = shift;
430     my $clauses = shift || {};
431     my $catch = $clauses->{'catch'} ||= [];
432    
433     my $sub = sub {
434     my $ref;
435     my(@array) = $code->($_[0]);
436     if(@array == 1 && ref($array[0])) {
437     $ref = $array[0];
438     $ref = [ %$ref ]
439     if(UNIVERSAL::isa($ref,'HASH'));
440     }
441     else {
442     $ref = \@array;
443     }
444     @$ref
445     };
446    
447     unshift @{$catch}, undef, $sub;
448    
449     $clauses;
450     }
451    
452     sub otherwise (&;$) {
453     my $code = shift;
454     my $clauses = shift || {};
455    
456     if(exists $clauses->{'otherwise'}) {
457     require Carp;
458     Carp::croak("Multiple otherwise clauses");
459     }
460    
461     $clauses->{'otherwise'} = $code;
462    
463     $clauses;
464     }
465    
466     1;
467     __END__
468    
469     =head1 NAME
470    
471     Error - Error/exception handling in an OO-ish way
472    
473     =head1 SYNOPSIS
474    
475     use Error qw(:try);
476    
477     throw Error::Simple( "A simple error");
478    
479     sub xyz {
480     ...
481     record Error::Simple("A simple error")
482     and return;
483     }
484    
485     unlink($file) or throw Error::Simple("$file: $!",$!);
486    
487     try {
488     do_some_stuff();
489     die "error!" if $condition;
490     throw Error::Simple -text => "Oops!" if $other_condition;
491     }
492     catch Error::IO with {
493     my $E = shift;
494     print STDERR "File ", $E->{'-file'}, " had a problem\n";
495     }
496     except {
497     my $E = shift;
498     my $general_handler=sub {send_message $E->{-description}};
499     return {
500     UserException1 => $general_handler,
501     UserException2 => $general_handler
502     };
503     }
504     otherwise {
505     print STDERR "Well I don't know what to say\n";
506     }
507     finally {
508     close_the_garage_door_already(); # Should be reliable
509     }; # Don't forget the trailing ; or you might be surprised
510    
511     =head1 DESCRIPTION
512    
513     The C<Error> package provides two interfaces. Firstly C<Error> provides
514     a procedural interface to exception handling. Secondly C<Error> is a
515     base class for errors/exceptions that can either be thrown, for
516     subsequent catch, or can simply be recorded.
517    
518     Errors in the class C<Error> should not be thrown directly, but the
519     user should throw errors from a sub-class of C<Error>.
520    
521     =head1 PROCEDURAL INTERFACE
522    
523     C<Error> exports subroutines to perform exception handling. These will
524     be exported if the C<:try> tag is used in the C<use> line.
525    
526     =over 4
527    
528     =item try BLOCK CLAUSES
529    
530     C<try> is the main subroutine called by the user. All other subroutines
531     exported are clauses to the try subroutine.
532    
533     The BLOCK will be evaluated and, if no error is throw, try will return
534     the result of the block.
535    
536     C<CLAUSES> are the subroutines below, which describe what to do in the
537     event of an error being thrown within BLOCK.
538    
539     =item catch CLASS with BLOCK
540    
541     This clauses will cause all errors that satisfy C<$err-E<gt>isa(CLASS)>
542     to be caught and handled by evaluating C<BLOCK>.
543    
544     C<BLOCK> will be passed two arguments. The first will be the error
545     being thrown. The second is a reference to a scalar variable. If this
546     variable is set by the catch block then, on return from the catch
547     block, try will continue processing as if the catch block was never
548     found.
549    
550     To propagate the error the catch block may call C<$err-E<gt>throw>
551    
552     If the scalar reference by the second argument is not set, and the
553     error is not thrown. Then the current try block will return with the
554     result from the catch block.
555    
556     =item except BLOCK
557    
558     When C<try> is looking for a handler, if an except clause is found
559     C<BLOCK> is evaluated. The return value from this block should be a
560     HASHREF or a list of key-value pairs, where the keys are class names
561     and the values are CODE references for the handler of errors of that
562     type.
563    
564     =item otherwise BLOCK
565    
566     Catch any error by executing the code in C<BLOCK>
567    
568     When evaluated C<BLOCK> will be passed one argument, which will be the
569     error being processed.
570    
571     Only one otherwise block may be specified per try block
572    
573     =item finally BLOCK
574    
575     Execute the code in C<BLOCK> either after the code in the try block has
576     successfully completed, or if the try block throws an error then
577     C<BLOCK> will be executed after the handler has completed.
578    
579     If the handler throws an error then the error will be caught, the
580     finally block will be executed and the error will be re-thrown.
581    
582     Only one finally block may be specified per try block
583    
584     =back
585    
586     =head1 CLASS INTERFACE
587    
588     =head2 CONSTRUCTORS
589    
590     The C<Error> object is implemented as a HASH. This HASH is initialized
591     with the arguments that are passed to it's constructor. The elements
592     that are used by, or are retrievable by the C<Error> class are listed
593     below, other classes may add to these.
594    
595     -file
596     -line
597     -text
598     -value
599     -object
600    
601     If C<-file> or C<-line> are not specified in the constructor arguments
602     then these will be initialized with the file name and line number where
603     the constructor was called from.
604    
605     If the error is associated with an object then the object should be
606     passed as the C<-object> argument. This will allow the C<Error> package
607     to associate the error with the object.
608    
609     The C<Error> package remembers the last error created, and also the
610     last error associated with a package. This could either be the last
611     error created by a sub in that package, or the last error which passed
612     an object blessed into that package as the C<-object> argument.
613    
614     =over 4
615    
616     =item throw ( [ ARGS ] )
617    
618     Create a new C<Error> object and throw an error, which will be caught
619     by a surrounding C<try> block, if there is one. Otherwise it will cause
620     the program to exit.
621    
622     C<throw> may also be called on an existing error to re-throw it.
623    
624     =item with ( [ ARGS ] )
625    
626     Create a new C<Error> object and returns it. This is defined for
627     syntactic sugar, eg
628    
629     die with Some::Error ( ... );
630    
631     =item record ( [ ARGS ] )
632    
633     Create a new C<Error> object and returns it. This is defined for
634     syntactic sugar, eg
635    
636     record Some::Error ( ... )
637     and return;
638    
639     =back
640    
641     =head2 STATIC METHODS
642    
643     =over 4
644    
645     =item prior ( [ PACKAGE ] )
646    
647     Return the last error created, or the last error associated with
648     C<PACKAGE>
649    
650     =back
651    
652     =head2 OBJECT METHODS
653    
654     =over 4
655    
656     =item stacktrace
657    
658     If the variable C<$Error::Debug> was non-zero when the error was
659     created, then C<stacktrace> returns a string created by calling
660     C<Carp::longmess>. If the variable was zero the C<stacktrace> returns
661     the text of the error appended with the filename and line number of
662     where the error was created, providing the text does not end with a
663     newline.
664    
665     =item object
666    
667     The object this error was associated with
668    
669     =item file
670    
671     The file where the constructor of this error was called from
672    
673     =item line
674    
675     The line where the constructor of this error was called from
676    
677     =item text
678    
679     The text of the error
680    
681     =back
682    
683     =head2 OVERLOAD METHODS
684    
685     =over 4
686    
687     =item stringify
688    
689     A method that converts the object into a string. This method may simply
690     return the same as the C<text> method, or it may append more
691     information. For example the file name and line number.
692    
693     By default this method returns the C<-text> argument that was passed to
694     the constructor, or the string C<"Died"> if none was given.
695    
696     =item value
697    
698     A method that will return a value that can be associated with the
699     error. For example if an error was created due to the failure of a
700     system call, then this may return the numeric value of C<$!> at the
701     time.
702    
703     By default this method returns the C<-value> argument that was passed
704     to the constructor.
705    
706     =back
707    
708     =head1 PRE-DEFINED ERROR CLASSES
709    
710     =over 4
711    
712     =item Error::Simple
713    
714     This class can be used to hold simple error strings and values. It's
715     constructor takes two arguments. The first is a text value, the second
716     is a numeric value. These values are what will be returned by the
717     overload methods.
718    
719     If the text value ends with C<at file line 1> as $@ strings do, then
720     this infomation will be used to set the C<-file> and C<-line> arguments
721     of the error object.
722    
723     This class is used internally if an eval'd block die's with an error
724     that is a plain string.
725    
726     =back
727    
728     =head1 KNOWN BUGS
729    
730     None, but that does not mean there are not any.
731    
732     =head1 AUTHORS
733    
734     Graham Barr <gbarr@pobox.com>
735    
736     The code that inspired me to write this was originally written by
737     Peter Seibel <peter@weblogic.com> and adapted by Jesse Glick
738     <jglick@sig.bsh.com>.
739    
740     =head1 MAINTAINER
741    
742     Arun Kumar U <u_arunkumar@yahoo.com>
743    
744     =cut

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24