File Coverage

blib/lib/PException.pm
Criterion Covered Total %
statement 84 97 86.6
branch 22 28 78.5
condition 6 8 75.0
subroutine 15 17 88.2
pod 10 11 90.9
total 137 161 85.0


line stmt bran cond sub pod time code
1             # -*- cperl -*-
2             #
3             # Copyright (c) 1997-2003 Samuel MOUNIEE
4             #
5             # This file is part of PException.
6             #
7             # PException is free software; you can redistribute it and/or modify
8             # it under the terms of the GNU General Public License as published by
9             # the Free Software Foundation; either version 2 of the License, or
10             # (at your option) any later version.
11             #
12             # PException is distributed in the hope that it will be useful,
13             # but WITHOUT ANY WARRANTY; without even the implied warranty of
14             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15             # GNU General Public License for more details.
16             #
17             # You should have received a copy of the GNU General Public License
18             # along with PException; if not, write to the Free Software
19             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20             #
21             package PException;
22              
23 1     1   549 use strict;
  1         2  
  1         55  
24 1     1   5 use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK $_XCPHDL_ );
  1         2  
  1         203  
25              
26             #sub DEBUG { 5 }
27             #sub debug { print STDERR join( " ! ", @_ ) if $_[0] >= DEBUG }
28 10     10 0 9 sub debug { }
29              
30             require Exporter;
31              
32             $VERSION= "2.4";
33              
34             =pod
35              
36             =head1 NAME
37              
38             PException - Exception manager
39              
40             =head1 SYNOPSIS
41              
42             use PException;
43              
44              
45             try {
46             throw( AnException->new() ) if $something;
47             throw( AnOtherException->new( [] ) )
48             unless $anotherthing;
49             }
50             catch AnException( sub { } ) ,
51             onfly AnOtherException( sub { } ) ;
52              
53              
54             =cut
55              
56             @ISA = qw( Exporter );
57             @EXPORT = qw( throw try );
58             @EXPORT_OK = qw( );
59              
60             #my $_XCPHDL_ = [ {
61             # EXCEPTIONS => 1,
62             # FLYS => 2,
63             # STACKFLY => 3,
64             # ONFLY => 4,
65             # FRESH => 5,
66             # CANDIE => 6
67             # }, [], [], [], 0, 0, 0 ];
68              
69             $_XCPHDL_ = {
70             EXCEPTIONS => [],
71             FLYS => [],
72             STACKFLY => [],
73             ONFLY => 0,
74             FRESH => 0,
75             CANDIE => 0
76             };
77              
78             BEGIN
79             {
80 1     1   4 $SIG{__WARN__} = \&__onflyhandler;
81              
82             $SIG{__DIE__} = sub {
83             # $PException::_XCPHDL_->{CANDIE} = 1;
84              
85             # if( $@ && hadWaittingExceptions() )
86             # { debug( 2, "NN-> $@ <" ) }
87             # elsif ( $@ ) { debug( 2, "NO-> $@ <" ); $@ = undef }
88             # elsif ( @_ ) { debug( 2, "ON->", @_, "<" ) }
89             # else { debug( 2, "OO->", $@, @_, "<" ) }
90             # $@ .= " - ";
91              
92 8         71 die ( $@, @_ );
93             # throw();
94 1         1057 };
95             }
96              
97              
98             =pod
99              
100             =head1 DESCRIPTION
101              
102             =head2 Methods & Functions
103              
104             =over 4
105              
106             =item try
107              
108             execute code until its end or an exception happens.
109              
110             =cut
111              
112             sub try(&@)
113             {
114 7     7 1 33 my $e = shift;
115              
116 7         10 my @catch = grep { $_->isa("PException::CATCH") } @_;
  7         39  
117 7         8 my @onfly = grep { $_->isa("PException::ONFLY") } @_;
  7         32  
118              
119             ## add an new stack of onfly exception
120              
121 7         8 push( @{$PException::_XCPHDL_->{STACKFLY}}, [ @onfly ] );
  7         15  
122              
123             # debug( 1, "try", $e, @_ );
124              
125 7         9 eval { &$e; };
  7         15  
126              
127             ## remove the stack of onfly exception
128              
129 7         18 pop( @{$PException::_XCPHDL_->{STACKFLY}} );
  7         11  
130              
131 7 100 66     24 if( $@ && hadWaittingExceptions() ) {
    50          
132 6         8 chomp( $@ );
133             # debug( 2, "---> $@ <" );
134 6 100       12 throw() if checkException( @catch );
135             }
136             elsif ( $@ ) {
137 0         0 chomp( $@ );
138             # debug( 2, "no-> $@ <" );
139 0         0 $@ = undef;
140             }
141              
142             # debug( 2, "STACKFLY", scalar( @{$PException::_XCPHDL_->{STACKFLY}} ) );
143             }
144              
145             =pod
146              
147             =item throw
148              
149             throw a list of exceptions.
150              
151             =cut
152              
153             sub throw(@)
154             {
155             # debug( 1, "throw", "begin" );
156              
157 14     14 1 19 $PException::_XCPHDL_->{FRESH} = 0;
158 14 100       28 if ( @_ > 0 ) {
159 7         5 push( @{$PException::_XCPHDL_->{EXCEPTIONS}}, @_ );
  7         12  
160 7         15 $PException::_XCPHDL_->{FRESH} = 1;
161             }
162              
163 14         13 my @tmp = @{$PException::_XCPHDL_->{EXCEPTIONS}};
  14         22  
164              
165             # debug( 2, "throw", @tmp );
166              
167 14         17 map { $_ = ref($_) . "($$_)" } @tmp;
  16         63  
168              
169 14 100 66     67 if ( !$PException::_XCPHDL_->{ONFLY} && $PException::_XCPHDL_->{CANDIE} ) {
    50          
170             # debug( 2, "throw", "Die" );
171 6         8 $_XCPHDL_->{CANDIE} = 0;
172 6         29 die( join( "\t- ", "Die", @tmp ) );
173             }
174             elsif( !$PException::_XCPHDL_->{ONFLY} )
175             {
176             # debug( 2, "throw", "Warn" );
177 8         45 warn( join( "\t- ", "Warn", @tmp ) );
178             }
179             }
180              
181              
182             =pod
183              
184             =item new
185              
186             create a new instance of an Exception.
187              
188             =cut
189              
190             sub new($@) {
191 8     8 1 33 my $classe = shift;
192 8         6 my $sc = shift;
193              
194 8 50       24 return bless $sc, $classe if ref( $sc );
195 8         30 return bless \$sc, $classe;
196             }
197              
198              
199             =pod
200              
201             =item catch
202              
203             execute an subroutine if this kind of exception is thrown
204              
205             =cut
206 6     6 1 120 sub catch($&) { return newTypedCatching( $_[0], $_[1], "CATCH" ) }
207              
208             =pod
209              
210             =item onfly
211              
212             execute an subroutine and continue the execution of the last try
213             if this kind of exception is thrown
214              
215             =cut
216 1     1 1 3 sub onfly($&) { return newTypedCatching( $_[0], $_[1], "ONFLY" ) }
217              
218              
219             =pod
220              
221             =item addFlyingHandler
222              
223             add a flying exception handler. it allows to continue the try
224             block where the exception appear.
225              
226             =cut
227             sub addFlyingHandler($&)
228 1     1 1 2 { push( @{$PException::_XCPHDL_->{FLYS}}, newTypedCatching( $_[0], $_[1], "FLYS" ) ) }
  1         3  
229              
230              
231             =pod
232              
233             =back
234              
235             =head2 Internal Calls
236              
237             =over 4
238              
239             =item hadWaittingExceptions
240              
241             return true if there is waitting exceptions, false elsewhere
242              
243             =cut
244 28     28 1 25 sub hadWaittingExceptions { return scalar(@{$PException::_XCPHDL_->{EXCEPTIONS}})>0 }
  28         180  
245              
246              
247             =pod
248              
249             =item newTypedCatching
250              
251             create an object for catching methods.
252             when a catch happens,
253              
254             =cut
255             sub newTypedCatching($$$) {
256 8     8 1 17 my ( $s, $c, $sig ) = @_;
257              
258 8 100       14 if ( !defined( $c ) )
259 3 50   0   20 { $c = sub {} unless ( $c = $s->can( "handleException" ) ) }
  0         0  
260              
261             return bless sub() {
262 10     10   36 debug( 1, $sig, $s, $c, @{$PException::_XCPHDL_->{EXCEPTIONS}} );
  10         23  
263              
264 10 100       9 if ( my @tmp = grep { $_->isa($s) } @{$PException::_XCPHDL_->{EXCEPTIONS}} )
  11         69  
  10         15  
265             {
266 8         13 @{$PException::_XCPHDL_->{EXCEPTIONS}} =
  9         30  
267 8         16 grep { !($_->isa($s)) } @{$PException::_XCPHDL_->{EXCEPTIONS}};
  8         14  
268 8         22 &$c(@tmp);
269             # @{$PException::_XCPHDL_->{EXCEPTIONS}} = ( @{$PException::_XCPHDL_->{EXCEPTIONS}}, @tmp );
270 8         192 return 1;
271             }
272 2         4 return 0;
273 8         71 }, "PException::$sig";
274             }
275              
276             =pod
277              
278             =item checkException
279              
280             control if there is some exceptions to catch & treat in an stack
281              
282             =cut
283             sub checkException(@) {
284 14     14 1 17 foreach(@_){&{$_}()}
  10         10  
  10         14  
285 14         24 return hadWaittingExceptions();
286             }
287              
288              
289             =pod
290              
291             =item handleException
292              
293             code for handling a exception. it's an empty sub actually.
294             you overload it if you want a default handler for an exception.
295              
296             =cut
297 0     0 1 0 sub handleException { }
298              
299              
300             =pod
301              
302             =item __onflyhandler
303              
304             the handler which intercept __WARN__ or __DIE__ signal
305              
306             =cut
307             sub __onflyhandler {
308 8 50   8   14 if ( hadWaittingExceptions() ) {
309 8         9 my ( @tmp );
310             # debug( 1, "FLYS HANDLER", "Begin" );
311              
312             ## get the current stack of onfly exception
313 8 50       17 if ( scalar( @{$PException::_XCPHDL_->{STACKFLY}} ) ) {
  8         14  
314 8         9 @tmp = @{$PException::_XCPHDL_->{STACKFLY}->[
  8         22  
315 8         9 $#{$PException::_XCPHDL_->{STACKFLY}}]};
316             } else {
317 0         0 my ( $i, @tmp ) = ( 0 );
318              
319 0         0 while( @tmp = caller( $i++ ) )
320 0         0 { print STDERR "$i\t> " . join( " + ", grep { defined $_ } @tmp, "\n" ) }
  0         0  
321              
322 0         0 for ( @{$PException::_XCPHDL_->{EXCEPTIONS}} )
  0         0  
323 0         0 { print STDERR "\t>$_ - ", $$_ , "\n" }
324 0         0 die( "ya1kouille! PException thrown with no try block" )
325             }
326              
327             # debug( 2, "FLYS HANDLER", @tmp );
328              
329             ## add flying handlers if fresh exception
330 2         4 push( @tmp, @{$PException::_XCPHDL_->{FLYS}} )
  7         16  
331             if ( ( $PException::_XCPHDL_->{FRESH} ) &&
332 8 100 100     21 scalar( @{$PException::_XCPHDL_->{FLYS}} ) );
333              
334 8         10 $PException::_XCPHDL_->{ONFLY} = 1;
335              
336 8 100       14 if ( checkException( @tmp ) ) {
337             # debug( 2, "FLYS HANDLER", "Following" );
338 6         7 $PException::_XCPHDL_->{ONFLY} = 0;
339 6         6 $PException::_XCPHDL_->{CANDIE}= 1;
340 6         16 throw();
341             }
342             # debug( 2, "FLYS HANDLER", "End" );
343 2         4 undef $@;
344 2         9 $PException::_XCPHDL_->{ONFLY} = 0;
345 0           } else { print STDERR $@ }
346             }
347              
348             __END__