Bring base classes from BioPerl-run back to BioPerl.
[bioperl-live.git] / Bio / Root / Root.pm
blobce1a10063d0d2c671d4c97a75d60bbea94710497
1 package Bio::Root::Root;
2 use strict;
3 use Bio::Root::IO;
4 use Bio::Root::Version;
5 use Scalar::Util qw(blessed reftype);
6 use base qw(Bio::Root::RootI);
8 our $VERSION = eval "$VERSION";
10 =head1 SYNOPSIS
12 # Any Bioperl-compliant object is a RootI compliant object
14 # Here's how to throw and catch an exception using the eval-based syntax.
16 $obj->throw("This is an exception");
18 eval {
19 $obj->throw("This is catching an exception");
22 if( $@ ) {
23 print "Caught exception";
24 } else {
25 print "no exception";
28 # Alternatively, using the new typed exception syntax in the throw() call:
30 $obj->throw( -class => 'Bio::Root::BadParameter',
31 -text => "Can not open file $file",
32 -value => $file );
34 # Want to see debug() outputs for this object
36 my $obj = Bio::Object->new(-verbose=>1);
38 my $obj = Bio::Object->new(%args);
39 $obj->verbose(2);
41 # Print debug messages which honour current verbosity setting
43 $obj->debug("Boring output only to be seen if verbose > 0\n");
45 # Deep-object copy
47 my $clone = $obj->clone;
49 =head1 DESCRIPTION
51 This is a hashref-based implementation of the Bio::Root::RootI
52 interface. Most Bioperl objects should inherit from this.
54 See the documentation for L<Bio::Root::RootI> for most of the methods
55 implemented by this module. Only overridden methods are described
56 here.
58 =head2 Throwing Exceptions
60 One of the functionalities that L<Bio::Root::RootI> provides is the
61 ability to L<throw>() exceptions with pretty stack traces. Bio::Root::Root
62 enhances this with the ability to use L<Error> (available from CPAN)
63 if it has also been installed.
65 If L<Error> has been installed, L<throw>() will use it. This causes an
66 Error.pm-derived object to be thrown. This can be caught within a
67 C<catch{}> block, from which you can extract useful bits of
68 information. If L<Error> is not installed, it will use the
69 L<Bio::Root::RootI>-based exception throwing facilty.
71 =head2 Typed Exception Syntax
73 The typed exception syntax of L<throw>() has the advantage of plainly
74 indicating the nature of the trouble, since the name of the class
75 is included in the title of the exception output.
77 To take advantage of this capability, you must specify arguments
78 as named parameters in the L<throw>() call. Here are the parameters:
80 =over 4
82 =item -class
84 name of the class of the exception.
85 This should be one of the classes defined in L<Bio::Root::Exception>,
86 or a custom error of yours that extends one of the exceptions
87 defined in L<Bio::Root::Exception>.
89 =item -text
91 a sensible message for the exception
93 =item -value
95 the value causing the exception or $!, if appropriate.
97 =back
99 Note that Bio::Root::Exception does not need to be imported into
100 your module (or script) namespace in order to throw exceptions
101 via Bio::Root::Root::throw(), since Bio::Root::Root imports it.
103 =head2 Try-Catch-Finally Support
105 In addition to using an eval{} block to handle exceptions, you can
106 also use a try-catch-finally block structure if L<Error> has been
107 installed in your system (available from CPAN). See the documentation
108 for Error for more details.
110 Here's an example. See the L<Bio::Root::Exception> module for
111 other pre-defined exception types:
113 my $IN;
114 try {
115 open $IN, '<', $file or $obj->throw( -class => 'Bio::Root::FileOpenException',
116 -text => "Cannot read file '$file'",
117 -value => $!);
119 catch Bio::Root::BadParameter with {
120 my $err = shift; # get the Error object
121 # Perform specific exception handling code for the FileOpenException
123 catch Bio::Root::Exception with {
124 my $err = shift; # get the Error object
125 # Perform general exception handling code for any Bioperl exception.
127 otherwise {
128 # A catch-all for any other type of exception
130 finally {
131 # Any code that you want to execute regardless of whether or not
132 # an exception occurred.
134 # the ending semicolon is essential!
136 =head1 AUTHOR Steve Chervitz
138 Ewan Birney, Lincoln Stein
140 =cut
142 our ($DEBUG, $ID, $VERBOSITY, $ERRORLOADED, $CLONE_CLASS);
144 BEGIN {
145 $ID = 'Bio::Root::Root';
146 $DEBUG = 0;
147 $VERBOSITY = 0;
148 $ERRORLOADED = 0;
150 # Check whether or not Error.pm is available.
152 # $main::DONT_USE_ERROR is intended for testing purposes and also
153 # when you don't want to use the Error module, even if it is installed.
154 # Just put a INIT { $DONT_USE_ERROR = 1; } at the top of your script.
155 if( not $main::DONT_USE_ERROR ) {
156 if ( eval "require Error; 1;" ) {
157 import Error qw(:try);
158 require Bio::Root::Exception;
159 $ERRORLOADED = 1;
160 $Error::Debug = 1; # enable verbose stack trace
163 if( !$ERRORLOADED ) {
164 require Carp; import Carp qw( confess );
167 # set up _dclone()
168 for my $class (qw(Clone Storable)) {
169 eval "require $class; 1;";
170 if (!$@) {
171 $CLONE_CLASS = $class;
172 if ($class eq 'Clone') {
173 *Bio::Root::Root::_dclone = sub {shift; return Clone::clone(shift)};
174 } else {
175 *Bio::Root::Root::_dclone = sub {
176 shift;
177 local $Storable::Deparse = 1;
178 local $Storable::Eval = 1;
179 return Storable::dclone(shift);
182 last;
185 if (!defined $CLONE_CLASS) {
186 *Bio::Root::Root::_dclone = sub {
187 my ($self, $orig, $level) = @_;
188 my $class = Scalar::Util::blessed($orig) || '';
189 my $reftype = Scalar::Util::reftype($orig) || '';
190 my $data;
191 if (!$reftype) {
192 $data = $orig
193 } elsif ($reftype eq "ARRAY") {
194 $data = [map $self->_dclone($_), @$orig];
195 } elsif ($reftype eq "HASH") {
196 $data = { map { $_ => $self->_dclone($orig->{$_}) } keys %$orig };
197 } elsif ($reftype eq 'CODE') { # nothing, maybe shallow copy?
198 $self->throw("Code reference cloning not supported; install Clone or Storable from CPAN");
199 } else { $self->throw("What type is $_?")}
200 if ($class) {
201 bless $data, $class;
203 $data;
207 $main::DONT_USE_ERROR; # so that perl -w won't warn "used only once"
210 =head2 new
212 Purpose : generic instantiation function can be overridden if
213 special needs of a module cannot be done in _initialize
215 =cut
217 sub new {
218 # my ($class, %param) = @_;
219 my $class = shift;
220 my $self = {};
221 bless $self, ref($class) || $class;
223 if(@_ > 1) {
224 # if the number of arguments is odd but at least 3, we'll give
225 # it a try to find -verbose
226 shift if @_ % 2;
227 my %param = @_;
228 ## See "Comments" above regarding use of _rearrange().
229 $self->verbose($param{'-VERBOSE'} || $param{'-verbose'});
231 return $self;
235 =head2 clone
237 Title : clone
238 Usage : my $clone = $obj->clone();
240 my $clone = $obj->clone( -start => 110 );
241 Function: Deep recursion copying of any object via Storable dclone()
242 Returns : A cloned object.
243 Args : Any named parameters provided will be set on the new object.
244 Unnamed parameters are ignored.
245 Comments: Where possible, faster clone methods are used, in order:
246 Clone::Fast::clone(), Clone::clone(), Storable::dclone. If neither
247 is present, a pure perl fallback (not very well tested) is used
248 instead. Storable dclone() cannot clone CODE references. Therefore,
249 any CODE reference in your original object will remain, but will not
250 exist in the cloned object. This should not be used for anything
251 other than cloning of simple objects. Developers of subclasses are
252 encouraged to override this method with one of their own.
254 =cut
256 sub clone {
257 my ($orig, %named_params) = @_;
259 __PACKAGE__->throw("Can't call clone() as a class method") unless
260 ref $orig && $orig->isa('Bio::Root::Root');
262 # Can't dclone CODE references...
263 # Should we shallow copy these? Should be harmless for these specific
264 # methods...
266 my %put_these_back = (
267 _root_cleanup_methods => $orig->{'_root_cleanup_methods'},
269 delete $orig->{_root_cleanup_methods};
271 # call the proper clone method, set lazily above
272 my $clone = __PACKAGE__->_dclone($orig);
274 $orig->{_root_cleanup_methods} = $put_these_back{_root_cleanup_methods};
276 foreach my $key (grep { /^-/ } keys %named_params) {
277 my $method = $key;
278 $method =~ s/^-//;
279 if ($clone->can($method)) {
280 $clone->$method($named_params{$key})
281 } else {
282 $orig->warn("Parameter $method is not a method for ".ref($clone));
285 return $clone;
288 =head2 _dclone
290 Title : clone
291 Usage : my $clone = $obj->_dclone($ref);
293 my $clone = $obj->_dclone($ref);
294 Function: Returns a copy of the object passed to it (a deep clone)
295 Returns : clone of passed argument
296 Args : Anything
297 NOTE : This differs from clone significantly in that it does not clone
298 self, but the data passed to it. This code may need to be optimized
299 or overridden as needed.
300 Comments: This is set in the BEGIN block to take advantage of optimized
301 cloning methods if Clone or Storable is present, falling back to a
302 pure perl kludge. May be moved into a set of modules if the need
303 arises. At the moment, code ref cloning is not supported.
305 =cut
307 =head2 verbose
309 Title : verbose
310 Usage : $self->verbose(1)
311 Function: Sets verbose level for how ->warn behaves
312 -1 = no warning
313 0 = standard, small warning
314 1 = warning with stack trace
315 2 = warning becomes throw
316 Returns : The current verbosity setting (integer between -1 to 2)
317 Args : -1,0,1 or 2
320 =cut
322 sub verbose {
323 my ($self,$value) = @_;
324 # allow one to set global verbosity flag
325 return $DEBUG if $DEBUG;
326 return $VERBOSITY unless ref $self;
328 if (defined $value || ! defined $self->{'_root_verbose'}) {
329 $self->{'_root_verbose'} = $value || 0;
331 return $self->{'_root_verbose'};
334 =head2 _register_for_cleanup
336 =cut
338 sub _register_for_cleanup {
339 my ($self,$method) = @_;
340 if ($method) {
341 if(! exists($self->{'_root_cleanup_methods'})) {
342 $self->{'_root_cleanup_methods'} = [];
344 push(@{$self->{'_root_cleanup_methods'}},$method);
348 =head2 _unregister_for_cleanup
350 =cut
352 sub _unregister_for_cleanup {
353 my ($self,$method) = @_;
354 my @methods = grep {$_ ne $method} $self->_cleanup_methods;
355 $self->{'_root_cleanup_methods'} = \@methods;
358 =head2 _cleanup_methods
360 =cut
362 sub _cleanup_methods {
363 my $self = shift;
364 return unless ref $self && $self->isa('HASH');
365 my $methods = $self->{'_root_cleanup_methods'} or return;
366 @$methods;
369 =head2 throw
371 Title : throw
372 Usage : $obj->throw("throwing exception message");
374 $obj->throw( -class => 'Bio::Root::Exception',
375 -text => "throwing exception message",
376 -value => $bad_value );
377 Function: Throws an exception, which, if not caught with an eval or
378 a try block will provide a nice stack trace to STDERR
379 with the message.
380 If Error.pm is installed, and if a -class parameter is
381 provided, Error::throw will be used, throwing an error
382 of the type specified by -class.
383 If Error.pm is installed and no -class parameter is provided
384 (i.e., a simple string is given), A Bio::Root::Exception
385 is thrown.
386 Returns : n/a
387 Args : A string giving a descriptive error message, optional
388 Named parameters:
389 '-class' a string for the name of a class that derives
390 from Error.pm, such as any of the exceptions
391 defined in Bio::Root::Exception.
392 Default class: Bio::Root::Exception
393 '-text' a string giving a descriptive error message
394 '-value' the value causing the exception, or $! (optional)
396 Thus, if only a string argument is given, and Error.pm is available,
397 this is equivalent to the arguments:
398 -text => "message",
399 -class => Bio::Root::Exception
400 Comments : If Error.pm is installed, and you don't want to use it
401 for some reason, you can block the use of Error.pm by
402 Bio::Root::Root::throw() by defining a scalar named
403 $main::DONT_USE_ERROR (define it in your main script
404 and you don't need the main:: part) and setting it to
405 a true value; you must do this within a BEGIN subroutine.
407 =cut
409 sub throw {
410 my ($self, @args) = @_;
412 my ($text, $class, $value) = $self->_rearrange( [qw(TEXT
413 CLASS
414 VALUE)], @args);
415 $text ||= $args[0] if @args == 1;
417 if ($ERRORLOADED) {
418 # Enable re-throwing of Error objects.
419 # If the error is not derived from Bio::Root::Exception,
420 # we can't guarantee that the Error's value was set properly
421 # and, ipso facto, that it will be catchable from an eval{}.
422 # But chances are, if you're re-throwing non-Bio::Root::Exceptions,
423 # you're probably using Error::try(), not eval{}.
424 # TODO: Fix the MSG: line of the re-thrown error. Has an extra line
425 # containing the '----- EXCEPTION -----' banner.
426 if (ref($args[0])) {
427 if( $args[0]->isa('Error')) {
428 my $class = ref $args[0];
429 $class->throw( @args );
431 else {
432 my $text .= "\nWARNING: Attempt to throw a non-Error.pm object: " . ref$args[0];
433 my $class = "Bio::Root::Exception";
434 $class->throw( '-text' => $text, '-value' => $args[0] );
437 else {
438 $class ||= "Bio::Root::Exception";
440 my %args;
441 if( @args % 2 == 0 && $args[0] =~ /^-/ ) {
442 %args = @args;
443 $args{-text} = $text;
444 $args{-object} = $self;
447 $class->throw( scalar keys %args > 0 ? %args : @args ); # (%args || @args) puts %args in scalar context!
450 else {
451 $class ||= '';
452 $class = ': '.$class if $class;
453 my $std = $self->stack_trace_dump();
454 my $title = "------------- EXCEPTION$class -------------";
455 my $footer = ('-' x CORE::length($title))."\n";
456 $text ||= '';
458 die "\n$title\n", "MSG: $text\n", $std, $footer, "\n";
462 =head2 debug
464 Title : debug
465 Usage : $obj->debug("This is debugging output");
466 Function: Prints a debugging message when verbose is > 0
467 Returns : none
468 Args : message string(s) to print to STDERR
470 =cut
472 sub debug {
473 my ($self, @msgs) = @_;
475 # using CORE::warn doesn't give correct backtrace information; we want the
476 # line from the previous call in the call stack, not this call (similar to
477 # cluck). For now, just add a stack trace dump and simple comment under the
478 # correct conditions.
479 if (defined $self->verbose && $self->verbose > 0) {
480 if (!@msgs || $msgs[-1] !~ /\n$/) {
481 push @msgs, "Debugging comment:" if !@msgs;
482 push @msgs, sprintf("%s %s:%s", @{($self->stack_trace)[2]}[3,1,2])."\n";
484 CORE::warn @msgs;
488 =head2 _load_module
490 Title : _load_module
491 Usage : $self->_load_module("Bio::SeqIO::genbank");
492 Function: Loads up (like use) the specified module at run time on demand.
493 Example :
494 Returns : TRUE on success. Throws an exception upon failure.
495 Args : The module to load (_without_ the trailing .pm).
497 =cut
499 sub _load_module {
500 my ($self, $name) = @_;
501 my ($module, $load, $m);
502 $module = "_<$name.pm";
503 return 1 if $main::{$module};
505 # untaint operation for safe web-based running (modified after
506 # a fix by Lincoln) HL
507 if ($name !~ /^([\w:]+)$/) {
508 $self->throw("$name is an illegal perl package name");
509 } else {
510 $name = $1;
513 $load = "$name.pm";
514 my $io = Bio::Root::IO->new();
515 # catfile comes from IO
516 $load = $io->catfile((split(/::/,$load)));
517 eval {
518 require $load;
520 if ( $@ ) {
521 $self->throw("Failed to load module $name. ".$@);
523 return 1;
526 =head2 DESTROY
528 =cut
530 sub DESTROY {
531 my $self = shift;
532 my @cleanup_methods = $self->_cleanup_methods or return;
533 for my $method (@cleanup_methods) {
534 $method->($self);