Allow falling back to any strigified Bio::AnnotationI for 'gene_name'
[bioperl-live.git] / Bio / Root / Root.pm
blob46c082acaa4e164fc882cbd8011ea648ffa13120
1 package Bio::Root::Root;
2 use strict;
3 use Scalar::Util qw(blessed reftype);
6 =head1 NAME
8 Bio::Root::Root - Hash-based implementation of Bio::Root::RootI
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 =head1 DESCRIPTION
47 This is a hashref-based implementation of the Bio::Root::RootI
48 interface. Most Bioperl objects should inherit from this.
50 See the documentation for L<Bio::Root::RootI> for most of the methods
51 implemented by this module. Only overridden methods are described
52 here.
54 =head2 Throwing Exceptions
56 One of the functionalities that L<Bio::Root::RootI> provides is the
57 ability to L<throw>() exceptions with pretty stack traces. Bio::Root::Root
58 enhances this with the ability to use L<Error> (available from CPAN)
59 if it has also been installed.
61 If L<Error> has been installed, L<throw>() will use it. This causes an
62 Error.pm-derived object to be thrown. This can be caught within a
63 C<catch{}> block, from wich you can extract useful bits of
64 information. If L<Error> is not installed, it will use the
65 L<Bio::Root::RootI>-based exception throwing facilty.
67 =head2 Typed Exception Syntax
69 The typed exception syntax of L<throw>() has the advantage of plainly
70 indicating the nature of the trouble, since the name of the class
71 is included in the title of the exception output.
73 To take advantage of this capability, you must specify arguments
74 as named parameters in the L<throw>() call. Here are the parameters:
76 =over 4
78 =item -class
80 name of the class of the exception.
81 This should be one of the classes defined in L<Bio::Root::Exception>,
82 or a custom error of yours that extends one of the exceptions
83 defined in L<Bio::Root::Exception>.
85 =item -text
87 a sensible message for the exception
89 =item -value
91 the value causing the exception or $!, if appropriate.
93 =back
95 Note that Bio::Root::Exception does not need to be imported into
96 your module (or script) namespace in order to throw exceptions
97 via Bio::Root::Root::throw(), since Bio::Root::Root imports it.
99 =head2 Try-Catch-Finally Support
101 In addition to using an eval{} block to handle exceptions, you can
102 also use a try-catch-finally block structure if L<Error> has been
103 installed in your system (available from CPAN). See the documentation
104 for Error for more details.
106 Here's an example. See the L<Bio::Root::Exception> module for
107 other pre-defined exception types:
109 try {
110 open( IN, $file) || $obj->throw( -class => 'Bio::Root::FileOpenException',
111 -text => "Cannot open file $file for reading",
112 -value => $!);
114 catch Bio::Root::BadParameter with {
115 my $err = shift; # get the Error object
116 # Perform specific exception handling code for the FileOpenException
118 catch Bio::Root::Exception with {
119 my $err = shift; # get the Error object
120 # Perform general exception handling code for any Bioperl exception.
122 otherwise {
123 # A catch-all for any other type of exception
125 finally {
126 # Any code that you want to execute regardless of whether or not
127 # an exception occurred.
129 # the ending semicolon is essential!
131 =head1 FEEDBACK
133 =head2 Mailing Lists
135 User feedback is an integral part of the evolution of this
136 and other Bioperl modules. Send your comments and suggestions preferably
137 to one of the Bioperl mailing lists.
139 Your participation is much appreciated.
141 bioperl-l@bioperl.org - General discussion
142 http://bioperl.org/wiki/Mailing_lists - About the mailing lists
144 =head2 Support
146 Please direct usage questions or support issues to the mailing list:
148 I<bioperl-l@bioperl.org>
150 rather than to the module maintainer directly. Many experienced and
151 reponsive experts will be able look at the problem and quickly
152 address it. Please include a thorough description of the problem
153 with code and data examples if at all possible.
155 =head2 Reporting Bugs
157 Report bugs to the Bioperl bug tracking system to help us keep track
158 the bugs and their resolution. Bug reports can be submitted via the
159 web:
161 https://redmine.open-bio.org/projects/bioperl/
163 =head1 AUTHOR
165 Functions originally from Steve Chervitz.
166 Refactored by Ewan Birney.
167 Re-refactored by Lincoln Stein.
169 =head1 APPENDIX
171 The rest of the documentation details each of the object
172 methods. Internal methods are usually preceded with a _
174 =cut
178 use strict;
179 use Bio::Root::IO;
181 use base qw(Bio::Root::RootI);
183 our ($DEBUG, $ID, $VERBOSITY, $ERRORLOADED, $CLONE_CLASS);
185 BEGIN {
186 $ID = 'Bio::Root::Root';
187 $DEBUG = 0;
188 $VERBOSITY = 0;
189 $ERRORLOADED = 0;
191 # Check whether or not Error.pm is available.
193 # $main::DONT_USE_ERROR is intended for testing purposes and also
194 # when you don't want to use the Error module, even if it is installed.
195 # Just put a INIT { $DONT_USE_ERROR = 1; } at the top of your script.
196 if( not $main::DONT_USE_ERROR ) {
197 if ( eval "require Error" ) {
198 import Error qw(:try);
199 require Bio::Root::Exception;
200 $ERRORLOADED = 1;
201 $Error::Debug = 1; # enable verbose stack trace
204 if( !$ERRORLOADED ) {
205 require Carp; import Carp qw( confess );
208 # set up _dclone()
209 for my $class (qw(Clone Storable)) {
210 eval "require $class; 1;";
211 if (!$@) {
212 $CLONE_CLASS = $class;
213 *Bio::Root::Root::_dclone = $class eq 'Clone' ?
214 sub {shift; Clone::clone($_[0])} :
215 sub {shift; Storable::dclone($_[0])} ;
216 last;
219 if (!defined $CLONE_CLASS) {
220 *Bio::Root::Root::_dclone = sub {
221 my ($self, $orig, $level) = @_;
222 my $class = Scalar::Util::blessed($orig) || '';
223 my $reftype = Scalar::Util::reftype($orig) || '';
224 my $data;
225 if (!$reftype) {
226 $data = $orig
227 } elsif ($reftype eq "ARRAY") {
228 $data = [map $self->_dclone($_), @$orig];
229 } elsif ($reftype eq "HASH") {
230 $data = { map { $_ => $self->_dclone($orig->{$_}) } keys %$orig };
231 } elsif ($reftype eq 'CODE') { # nothing, maybe shallow copy?
232 $self->throw("Code reference cloning not supported");
233 } else { $self->throw("What type is $_?")}
234 if ($class) {
235 bless $data, $class;
237 $data;
241 $main::DONT_USE_ERROR; # so that perl -w won't warn "used only once"
244 =head2 new
246 Purpose : generic instantiation function can be overridden if
247 special needs of a module cannot be done in _initialize
249 =cut
251 sub new {
252 # my ($class, %param) = @_;
253 my $class = shift;
254 my $self = {};
255 bless $self, ref($class) || $class;
257 if(@_ > 1) {
258 # if the number of arguments is odd but at least 3, we'll give
259 # it a try to find -verbose
260 shift if @_ % 2;
261 my %param = @_;
262 ## See "Comments" above regarding use of _rearrange().
263 $self->verbose($param{'-VERBOSE'} || $param{'-verbose'});
265 return $self;
269 =head2 clone
271 Title : clone
272 Usage : my $clone = $obj->clone();
274 my $clone = $obj->clone( -start => 110 );
275 Function: Deep recursion copying of any object via Storable dclone()
276 Returns : A cloned object.
277 Args : Any named parameters provided will be set on the new object.
278 Unnamed parameters are ignored.
279 Comments: Where possible, faster clone methods are used, in order:
280 Clone::clone(), Storable::dclone. If neither is present,
281 a pure perl fallback (not very well tested) is used instead.
282 Storable dclone() cannot clone CODE references. Therefore,
283 any CODE reference in your original object will remain, but
284 will not exist in the cloned object.
285 This should not be used for anything other than cloning of simple
286 objects. Developers of subclasses are encouraged to override this
287 method with one of their own.
289 =cut
291 sub clone {
292 my ($orig, %named_params) = @_;
294 __PACKAGE__->throw("Can't call clone() as a class method") unless
295 ref $orig && $orig->isa('Bio::Root::Root');
297 # Can't dclone CODE references...
298 # Should we shallow copy these? Should be harmless for these specific
299 # methods...
301 my %put_these_back = (
302 _root_cleanup_methods => $orig->{'_root_cleanup_methods'},
304 delete $orig->{_root_cleanup_methods};
306 # call the proper clone method, set lazily above
307 my $clone = __PACKAGE__->_dclone($orig);
309 $orig->{_root_cleanup_methods} = $put_these_back{_root_cleanup_methods};
311 foreach my $key (grep { /^-/ } keys %named_params) {
312 my $method = $key;
313 $method =~ s/^-//;
314 if ($clone->can($method)) {
315 $clone->$method($named_params{$key})
316 } else {
317 $orig->warn("Parameter $method is not a method for ".ref($clone));
320 return $clone;
323 =head2 _dclone
325 Title : clone
326 Usage : my $clone = $obj->_dclone($ref);
328 my $clone = $obj->_dclone($ref);
329 Function: Returns a copy of the object passed to it (a deep clone)
330 Returns : clone of passed argument
331 Args : Anything
332 NOTE : This differs from clone significantly in that it does not clone
333 self, but the data passed to it. This code may need to be optimized
334 or overridden as needed.
335 Comments: This is set in the BEGIN block to take advantage of optimized
336 cloning methods if Clone or Storable is present, falling back to a
337 pure perl kludge. May be moved into a set of modules if the need
338 arises. At the moment, code ref cloning is not supported.
340 =cut
342 =head2 verbose
344 Title : verbose
345 Usage : $self->verbose(1)
346 Function: Sets verbose level for how ->warn behaves
347 -1 = no warning
348 0 = standard, small warning
349 1 = warning with stack trace
350 2 = warning becomes throw
351 Returns : The current verbosity setting (integer between -1 to 2)
352 Args : -1,0,1 or 2
355 =cut
357 sub verbose {
358 my ($self,$value) = @_;
359 # allow one to set global verbosity flag
360 return $DEBUG if $DEBUG;
361 return $VERBOSITY unless ref $self;
363 if (defined $value || ! defined $self->{'_root_verbose'}) {
364 $self->{'_root_verbose'} = $value || 0;
366 return $self->{'_root_verbose'};
369 sub _register_for_cleanup {
370 my ($self,$method) = @_;
371 if($method) {
372 if(! exists($self->{'_root_cleanup_methods'})) {
373 $self->{'_root_cleanup_methods'} = [];
375 push(@{$self->{'_root_cleanup_methods'}},$method);
379 sub _unregister_for_cleanup {
380 my ($self,$method) = @_;
381 my @methods = grep {$_ ne $method} $self->_cleanup_methods;
382 $self->{'_root_cleanup_methods'} = \@methods;
386 sub _cleanup_methods {
387 my $self = shift;
388 return unless ref $self && $self->isa('HASH');
389 my $methods = $self->{'_root_cleanup_methods'} or return;
390 @$methods;
394 =head2 throw
396 Title : throw
397 Usage : $obj->throw("throwing exception message");
399 $obj->throw( -class => 'Bio::Root::Exception',
400 -text => "throwing exception message",
401 -value => $bad_value );
402 Function: Throws an exception, which, if not caught with an eval or
403 a try block will provide a nice stack trace to STDERR
404 with the message.
405 If Error.pm is installed, and if a -class parameter is
406 provided, Error::throw will be used, throwing an error
407 of the type specified by -class.
408 If Error.pm is installed and no -class parameter is provided
409 (i.e., a simple string is given), A Bio::Root::Exception
410 is thrown.
411 Returns : n/a
412 Args : A string giving a descriptive error message, optional
413 Named parameters:
414 '-class' a string for the name of a class that derives
415 from Error.pm, such as any of the exceptions
416 defined in Bio::Root::Exception.
417 Default class: Bio::Root::Exception
418 '-text' a string giving a descriptive error message
419 '-value' the value causing the exception, or $! (optional)
421 Thus, if only a string argument is given, and Error.pm is available,
422 this is equivalent to the arguments:
423 -text => "message",
424 -class => Bio::Root::Exception
425 Comments : If Error.pm is installed, and you don't want to use it
426 for some reason, you can block the use of Error.pm by
427 Bio::Root::Root::throw() by defining a scalar named
428 $main::DONT_USE_ERROR (define it in your main script
429 and you don't need the main:: part) and setting it to
430 a true value; you must do this within a BEGIN subroutine.
432 =cut
434 sub throw {
435 my ($self, @args) = @_;
437 my ($text, $class, $value) = $self->_rearrange( [qw(TEXT
438 CLASS
439 VALUE)], @args);
440 $text ||= $args[0] if @args == 1;
442 if ($ERRORLOADED) {
443 # Enable re-throwing of Error objects.
444 # If the error is not derived from Bio::Root::Exception,
445 # we can't guarantee that the Error's value was set properly
446 # and, ipso facto, that it will be catchable from an eval{}.
447 # But chances are, if you're re-throwing non-Bio::Root::Exceptions,
448 # you're probably using Error::try(), not eval{}.
449 # TODO: Fix the MSG: line of the re-thrown error. Has an extra line
450 # containing the '----- EXCEPTION -----' banner.
451 if (ref($args[0])) {
452 if( $args[0]->isa('Error')) {
453 my $class = ref $args[0];
454 $class->throw( @args );
456 else {
457 my $text .= "\nWARNING: Attempt to throw a non-Error.pm object: " . ref$args[0];
458 my $class = "Bio::Root::Exception";
459 $class->throw( '-text' => $text, '-value' => $args[0] );
462 else {
463 $class ||= "Bio::Root::Exception";
465 my %args;
466 if( @args % 2 == 0 && $args[0] =~ /^-/ ) {
467 %args = @args;
468 $args{-text} = $text;
469 $args{-object} = $self;
472 $class->throw( scalar keys %args > 0 ? %args : @args ); # (%args || @args) puts %args in scalar context!
475 else {
476 $class ||= '';
477 $class = ': '.$class if $class;
478 my $std = $self->stack_trace_dump();
479 my $title = "------------- EXCEPTION$class -------------";
480 my $footer = ('-' x CORE::length($title))."\n";
481 $text ||= '';
483 die "\n$title\n", "MSG: $text\n", $std, $footer, "\n";
487 =head2 debug
489 Title : debug
490 Usage : $obj->debug("This is debugging output");
491 Function: Prints a debugging message when verbose is > 0
492 Returns : none
493 Args : message string(s) to print to STDERR
495 =cut
497 sub debug {
498 my ($self, @msgs) = @_;
500 # using CORE::warn doesn't give correct backtrace information; we want the
501 # line from the previous call in the call stack, not this call (similar to
502 # cluck). For now, just add a stack trace dump and simple comment under the
503 # correct conditions.
504 if (defined $self->verbose && $self->verbose > 0) {
505 if (!@msgs || $msgs[-1] !~ /\n$/) {
506 push @msgs, "Debugging comment:" if !@msgs;
507 push @msgs, sprintf("%s %s:%s", @{($self->stack_trace)[2]}[3,1,2])."\n";
509 CORE::warn @msgs;
513 =head2 _load_module
515 Title : _load_module
516 Usage : $self->_load_module("Bio::SeqIO::genbank");
517 Function: Loads up (like use) the specified module at run time on demand.
518 Example :
519 Returns : TRUE on success. Throws an exception upon failure.
520 Args : The module to load (_without_ the trailing .pm).
522 =cut
524 sub _load_module {
525 my ($self, $name) = @_;
526 my ($module, $load, $m);
527 $module = "_<$name.pm";
528 return 1 if $main::{$module};
530 # untaint operation for safe web-based running (modified after
531 # a fix by Lincoln) HL
532 if ($name !~ /^([\w:]+)$/) {
533 $self->throw("$name is an illegal perl package name");
534 } else {
535 $name = $1;
538 $load = "$name.pm";
539 my $io = Bio::Root::IO->new();
540 # catfile comes from IO
541 $load = $io->catfile((split(/::/,$load)));
542 eval {
543 require $load;
545 if ( $@ ) {
546 $self->throw("Failed to load module $name. ".$@);
548 return 1;
551 sub DESTROY {
552 my $self = shift;
553 my @cleanup_methods = $self->_cleanup_methods or return;
554 for my $method (@cleanup_methods) {
555 $method->($self);