Raw content of Bio::Root::Object#-----------------------------------------------------------------------------
# PACKAGE : Bio::Root::Object.pm
# AUTHOR : Steve Chervitz (sac@bioperl.org)
# CREATED : 23 July 1996
# REVISION: $Id: Object.pm,v 1.23 2002/10/22 07:38:37 lapp Exp $
# STATUS : Alpha
#
# For documentation, run this module through pod2html
# (preferably from Perl v5.004 or better).
#
# MODIFICATION NOTES: See bottom of file.
#
# Copyright (c) 1996-2000 Steve Chervitz. All Rights Reserved.
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
# Retain this notice and note any modifications made.
#-----------------------------------------------------------------------------
package Bio::Root::Object;
use strict;
require 5.002;
use Bio::Root::Global qw(:devel $AUTHORITY $CGI);
use Bio::Root::Root;
use Exporter ();
#use AutoLoader;
#*AUTOLOAD = \&AutoLoader::AUTOLOAD;
use vars qw(@EXPORT_OK %EXPORT_TAGS);
@EXPORT_OK = qw($VERSION &find_object &stack_trace &containment &_rearrange);
%EXPORT_TAGS = ( std => [qw(&stack_trace &containment)] );
use vars qw($ID $VERSION %Objects_created $Revision @ISA);
@ISA = qw(Bio::Root::Root);
# %Objects_created can be used for tracking all objects created.
# See _initialize() for details.
$ID = 'Bio::Root::Object';
$VERSION = 0.041;
$Revision = '$Id: Object.pm,v 1.23 2002/10/22 07:38:37 lapp Exp $'; #'
### POD Documentation:
=head1 NAME
Bio::Root::Object - A core Perl 5 object.
=head1 SYNOPSIS
Use this module as the root of your inheritance tree.
=head2 Object Creation
require Bio::Root::Object;
$dad = new Bio::Root::Object();
$son = new Bio::Root::Object(-name => 'Junior',
-parent => $dad,
-make => 'full');
See the L method for a complete description of parameters.
See also L.
=head1 DESCRIPTION
B attempts to encapsulate the "core" Perl5
object: What are the key data and behaviors ALL (or at least most) Perl5
objects should have?
=head2 Rationale
Use of B within the Bioperl framework facilitates
operational consistency across the different modules defined within
the B namespace. Not all objects need to derive from
B. However, when generating lots of different types
of potentially complex objects which should all conform to a set of
basic expectations, this module may be handy.
At the very least, this module saves you from re-writing the L
method for each module you develop. It also permits consistent and
robust handling of C<-tag =E value> method arguments via the
L method and provides a
object-oriented way handle exceptions and warnings via the L and L methods.
See L for some other handy methods.
=head2 Fault-Tolerant Objects
A major motivation for this module was to promote the creation of robust,
fault-tolerant Perl5 objects. The L method relies on Perl's built-in
C exception mechanism to generate fatal exceptions.
The data comprising an exception is managed by the B
module, which essentially allows the data thrown by a C event to be
wrapped into an object that can be easily examined and possibly re-thrown.
The intent here is three-fold:
=over 4
=item 1 Detailed error reporting.
Allow objects to report detailed information about the error condition
(who, what, where, why, how).
=item 2 Handle complex errors in objects.
The goal is to make it relatively painless to detect and handle the wide
variety of errors possible with a complex Perl object.
Perl's error handling mechanism is a might clunky when it comes to
handling complex errors within complex objects, but it is improving.
=item 3 Efficient & easy exception handling.
To enable robust exception handling without incurring a significant
performance penalty in the resulting code. Ideally, exception handling
code should be transparent to the cpu until and unless an exception
arises.
=back
These goals may at times be at odds and we are not claiming
to have achieved the perfect balance. Ultimately, we want self-
sufficient object-oriented systems able to deal with their own errors.
This area should improve as the module, and Perl, evolve.
One possible modification might be to utilize Graham Barr's B
module or Torsten Ekedahl's B module
(see L).
Technologies such as these may eventually be
incorporated into future releases of Perl. The exception handling
used by B can be expected to change as Perl's
exception handling mechanism evolves.
B In this discussion and elsewhere in this module,
the terms "Exception" and "Error" are used interchangeably to mean
"something unexpected occurred" either as a result of incorrect user
input or faulty internal processing.
=head1 USAGE
=head2 Basic Exception handling
Object construction is a common place for exceptions to occur. By wrapping
the construction in an C block, we can prevent the exception from
crashing the script and attempt to recover gracefully:
# Package Foo.pm IS-A Bio::Root::Object.pm
$obj = eval { new Foo(@data) }; # ending semicolon required.
if($@) {
print STDERR "\nTrouble creating Foo object: $@\n";
recover_gracefully($@);
}
A common strategy when generating lots of objects is to collect
data about which objects failed to build but still permit
the successfully created ones get processed:
@errs = ();
foreach $thing ( @stuff ) {
my $obj = eval { new Foo($thing) };
if($@) {
push @err, [$thing, $@];
}
else {
process_obj($obj);
}
}
Post-mortem reporting, logging, or analysis of the problems ensues:
if(@errs) {
printf "\n%d things failed:\n", scalar(@errs);
foreach(@errs) { print "$err->[0], ";}
print "\n\nTrapped exceptions:\n";
foreach(@errs) { print "$err->[1]\n";}
}
New with B is the ability to C with an object
reference in C<$@> instead of just a string. This feature is not yet
exploited in Bio::Root::Object.pm but may be in future versions.
Bio::Root::Err.pm objects can be reconstructed from the contents of C<$@>:
eval{ # exception-prone code here... };
if($@) {
$err = new Bio::Root::Err($@);
printf "Trouble: %s\n". $err->msg;
printf "Stack trace: %s\n". $err->stack;
}
=head2 Demo Scripts
Some demo script that illustrate working with Bio::Root::Objects
are included with the distribution in the examples/root_object directory.
=head1 STRICTNESS & VERBOSITY
There are two global variables that can be used to control sensitivity to
exceptions/warnings and the amount of reporting for all objects within a process.
These are accessed via functions B and B exported by
Bio::Root::Global (see L).
$STRICTNESS - Regulates the sensitivity of the object to exceptions and warnings.
$VERBOSITY - Regulates the amount of reporting by an object.
The L and L methods of B
originally operated at the the object level, to permit individual
strictness and verbosity levels for different objects. This level of
control is not usually required and can often be inconvenient; one
typically wants to set these properties globally for a given
script. While this sacrifices some flexibility, it saves time and
memory when working with lots of objects. For instance, child objects
don't have to worry about checking their parents to determine their
strictness/verbosity levels. Strictness and verbosity are
globally-defined values, but different classes of objects can be
differentially sensitive to these values depending on design criteria.
Strictness and verbosity can be positive or negative. Negative
verbosity equals terseness; negative strictness equals permissiveness.
In B only the Bio::Root::Root::throw() and
Bio::Root::Root::warn() methods (see L) are sensitive to
these values as indicated in the tables below:
+---------+
| throw() | v e r b o s i t y
+---------+ -------------------------------------
-1 0 1
s ---------- ----------- ----------
t
r -2 -- throw() converted into warn()
i
c -1 | Exception Exception Exception
t 0 |_ printed printed printed
n 1 | without with with stack
e 2 | stack trace stack trace trace and
s | sysbeep
s
+---------+
| warn() | v e r b o s i t y
+---------+ --------------------------------------
-1 0 1
s ---------- ----------- -----------
t
r -2 | Warning Warning Warning
i -1 |_ not printed printed
c 0 | printed without with stack
t 1 | but stack trace trace and
n | attached* sysbeep
e
s 2 -- warn() converted into throw()
s
(*) Warnings will be attached to an object if the
-record_err =>1 flag is set when constructing the object
or if $object->record_err(1) is called subsequent to creation.
See the methods L, L, L,
Bio::Root::Root::throw(), and Bio::Root::Root::warn() in
L for more details.
=head1 DEPENDENCIES
As the B does not inherit from any modules
but wraps (i.e., provides an interface and delegates
functionality to) other modules in the Bio::Root:: hierarchy:
Module Purpose
-------------------- ------------------------------------
Bio::Root::Err.pm Exception handling
Bio::Root::IOManager.pm Input/output of object data or error data
Bio::Root::Xref.pm Arbitrary links between objects
All of these modules are loaded only when necessary.
B is an object representing an exception.
B and B are more experimental. They are
utilized via delegation, which permits them to be developed and utilized
independently of B.
Since this module is at the root of potentially many different objects
in a particular application, efficiency is important. Bio::Root::Object.pm is
intended to be a lightweight, lean and mean module.
=head1 FEEDBACK
=head2 Mailing Lists
User feedback is an integral part of the evolution of this and other Bioperl modules.
Send your comments and suggestions preferably to one of the Bioperl mailing lists.
Your participation is much appreciated.
bioperl-l@bioperl.org - General discussion
http://bioperl.org/MailList.shtml - About the mailing lists
=head2 Reporting Bugs
Report bugs to the Bioperl bug tracking system to help us keep track the bugs and
their resolution. Bug reports can be submitted via email or the web:
bioperl-bugs@bio.perl.org
http://bugzilla.bioperl.org/
=head1 AUTHOR
Steve Chervitz Esac@bioperl.orgE
See L for where to send bug reports and comments.
=head1 VERSION
Bio::Root::Object.pm, 0.041
=head1 TODO
=over 0
=item * Experiment with other Exception classes.
Consider incorporating a more widely-used Error/Exception module
(see L).
=item * Think about integration with Data::Dumper.pm for persisting objects.
=back
=head1 SEE ALSO
L - Error/Exception object
L - Input/Output manager object
L - Manages dynamic lists of objects
L - Cross-reference object
L - Manages global variables/constants
http://bio.perl.org/Projects/modules.html - Online module documentation
http://bio.perl.org/ - Bioperl Project Homepage
=head2 Other Exception Modules
Experimental::Exception.pm - ftp://ftp.matematik.su.se/pub/teke/
Error.pm - http://www.cpan.org/authors/id/GBARR/
Throwable.pm - mailto:kstevens@globeandmail.ca
http://genome-www.stanford.edu/perlOOP/exceptions.html
=head1 ACKNOWLEDGEMENTS
This module was developed under the auspices of the Saccharomyces Genome
Database:
http://genome-www.stanford.edu/Saccharomyces
Other Bioperl developers contributed ideas including Ewan Birney, Ian Korf,
Chris Dagdigian, Georg Fuellen, and Steven Brenner.
=head1 COPYRIGHT
Copyright (c) 1996-98 Steve Chervitz. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=cut
#
##
###
#### END of main POD documentation. '
###
##
#
=head1 APPENDIX
Methods beginning with a leading underscore are considered private
and are intended for internal use by this module. They are
B considered part of the public interface and are described here
for documentation purposes only.
=cut
#
# This object is deprecated as the root of the inheritance tree, but some
# modules depend on it as a legacy. We issue a deprecation warning for all
# other modules.
#
my @inheriting_modules = ('Bio::Tools::Blast', 'Bio::Root::Object',
'Bio::Root::IOManager');
#######################################################
# CONSTRUCTOR/DESTRUCTOR #
#######################################################
=head2 new
Purpose : Creates a blessed object reference (hash) for the indicated class
: and calls _initialize() for the class passing it all parameters.
Usage : new CLASS_NAME [ %named_parameters];
Example : $obj = new Bio::Root::Object 'george';
: $obj = Bio::Root::Object->new(-name => 'L56163',
: -parent => $obj2 );
: $obj = Bio::Root::Object->new();
Returns : Blessed hash reference.
Argument : Named parameters: (PARAMETER TAGS CAN BE UPPER OR LOWERCASE).
: (all are optional)
: -NAME => arbitrary string to identify an object;
: should be unique within its class.
: -PARENT => blessed reference for an object that
: is responsible for the present object
: (e.g., a container).
: -MAKE => string to specify special constructor option.
: -OBJ => object reference for an object to be cloned.
: -RECORD_ERR => boolean (if true, attach all Err.pm objects generated by
: warn() or throw() calls to the present object;
: default = false).
:
: The use of STRICT and VERBOSE in constructors is no longer
: necessary since there is no object-specific strict or verbose setting.
: Use the strictness() and verbosity() functions exported by
: Bio::Root::Global.pm. These options are still provided
: in the constructor but the will affect *all* objects within a
: given process.
:
: -STRICT => integer (level of strictness: -2, -1, 0, 1, 2).
: -VERBOSE => integer (level of verbosity: -1, 0, 1)
: Verbosity can be used to control how much reporting
: an object should do generally. In this module,
: verbosity affects the behavior of throw() and warn()
: only.
:
:
Comments : This method creates blessed HASH references.
: An object is free to define its own strict, and verbose
: behavior as well as its own make (constructor) options.
See Also : L<_initialize()|_initialize>, L, L, L, L, L, L, and Bio::Root::Root::throw() and Bio::Root::Root::warn() in L
=cut
#----------
sub new {
#----------
my($class, @param) = @_;
my $self = {};
bless $self, ref($class) || $class;
$DEBUG==2 && print STDERR "CREATING $self";
$self->_initialize(@param);
$self;
}
=head2 _initialize
Purpose : Initializes key Bio::Root::Object.pm data (name, parent, make, strict).
: Called by new().
Usage : n/a; automatically called by Bio::Root::Object::new()
Returns : String containing the -MAKE constructor option or 'default'
: if none defined (if a -MAKE parameter is defined, the value
: returned will be that obtained from the make() method.)
: This return value saves any subclass from having to call
: $self->make() during construction. For example, within a
: subclass _initialize() method, invoke the Bio::Root::Object::
: initialize() method as follows:
: my $make = $self->SUPER::_initialize(@param);
Argument : Named parameters passed from new()
: (PARAMETER TAGS CAN BE ALL UPPER OR ALL LOWER CASE).
Comments : This method calls name(), make(), parent(), strict(), index()
: and thus enables polymorphism on these methods. To save on method
: call overhead, these methods are called only if the data need
: to be set.
:
: The _set_clone() method is called if the -MAKE option includes
: the string 'clone' (e.g., -MAKE => 'clone').
:
: The index() method is called if the -MAKE option includes
: the string 'index'. (This is an experimental feature)
: (Example: -MAKE => 'full_index').
:
: NOTE ON USING _rearrange():
:
: _rearrange() is a handy method for working with tagged (named)
: parameters and it permits case-insensitive in tag names
: as well as handling tagged or un-tagged parameters.
: _initialize() does not currently call _rearrange() since
: there is a concern about performance when setting many objects.
: One issue is that _rearrange() could be called with many elements
: yet the caller is interested in only a few. Also, derived objects
: typically invoke _rearrange() in their constructors as well.
: This could particularly degrade performance when creating lots
: of objects with extended inheritance hierarchies and lots of tagged
: parameters which are passes along the inheritance hierarchy.
:
: One thing that may help is if _rearrange() deleted all parameters
: it extracted. This would require passing a reference to the param list
: and may add excessive dereferencing overhead.
: It also would cause problems if the same parameters are used by
: different methods or objects.
See Also : L, L, L, L, L, L, L
=cut
#----------------
sub _initialize {
#----------------
local($^W) = 0;
my($self, %param) = @_;
if(! grep { ref($self) =~ /$_/; } @inheriting_modules) {
$self->warn("Class " . ref($self) .
" inherits from Bio::Root::Object, which is deprecated. ".
"Try changing your inheritance to Bio::Root::Root.");
}
my($name, $parent, $make, $strict, $verbose, $obj, $record_err) = (
($param{-NAME}||$param{'-name'}), ($param{-PARENT}||$param{'-parent'}),
($param{-MAKE}||$param{'-make'}), ($param{-STRICT}||$param{'-strict'}),
($param{-VERBOSE}||$param{'-verbose'}),
($param{-OBJ}||$param{'-obj'}, $param{-RECORD_ERR}||$param{'-record_err'})
);
## See "Comments" above regarding use of _rearrange().
# $self->_rearrange([qw(NAME PARENT MAKE STRICT VERBOSE OBJ)], %param);
$DEBUG and do{ print STDERR ">>>> Initializing $ID (${\ref($self)}) ",$name||'anon';};
if(defined($make) and $make =~ /clone/i) {
$self->_set_clone($obj);
} else {
$name ||= ($#_ == 1 ? $_[1] : ''); # If a single arg is given, use as name.
## Another performance issue: calling name(), parent(), strict(), make()
## Any speed diff with conditionals to avoid method calls?
$self->name($name) if $name;
$self->parent($parent) if $parent;
$self->{'_strict'} = $strict || undef;
$self->{'_verbose'} = $verbose || undef;
$self->{'_record_err'} = $record_err || undef;
if($make) {
$make = $self->make($make);
# Index the Object in the global object hash only if requested.
# This feature is not used much. If desired, an object can always
# call Bio::Root::Object::index() any time after construction.
$self->index() if $make =~ /index/;
}
}
$DEBUG and print STDERR "---> Initialized $ID (${\ref($self)}) ",$name,"\n";
## Return data of potential use to subclass constructors.
# return (($make || 'default'), $strict); # maybe (?)
return $make || 'default';
}
=head2 DESTROY
Purpose : Provides indication that the object is being reclaimed
: by the GC for debugging purposes only.
Usage : n/a; automatically called by Perl when the ref count
: on the object drops to zero.
Argument : n/a
Comments : Setting the global $DEBUG to 2 will print messages upon
: object destruction.
: Subclasses should override this method to
: clean up any resources (open file handles, etc.)
: The overridden method should end with a call to
: SUPER::DESTROY;
See Also : L
=cut
#-----------
sub DESTROY {
#-----------
my $self=shift;
$DEBUG==2 && print STDERR "DESTROY called in $ID for ${\$self->to_string} ($self)\n";
}
=head2 destroy
Purpose : Clean up any resources allocated by the object and
: remove links to all objects connected to the present
: object with the ultimate aim of signaling the GC to
: reclaim all memory allocated for the object.
: This method breaks links to any Err, IOManager, and Xref objects
: and drops the present object as a child from any parent objects.
Usage : $object->destroy(); undef $object;
: undef-ing the object reference signals the GC to reclaim
: the object's memory.
Returns : undef
Argument : n/a
Comments : Circular reference structures are problematic for garbage
: collection schemes such as Perl's which are based on reference
: counting. If you create such structures outside of
: the parent-child relationship, be sure to properly break
: the circularity when destroying the object.
: Subclasses should override this method to call destroy()
: on any contained child objects. The overridden method
: should end with a call to SUPER::destroy().
Bugs : Bio::Root::Xref.pm objects have not been tested and
: may not be handled properly here.
: Bio::Root::Vector.pm objects are also not yet handled
: properly so beware of crunching lots of Vector objects.
=cut
#-------------'
sub destroy {
#-------------
## Note: Cannot delete parent and xref object refs since they are not
## owned by this object, merely associated with it.
my $self = shift;
if(ref($self->{'_parent'})) {
$self->{'_parent'}->_drop_child($self);
undef $self->{'_parent'};
}
if(ref($self->{'_io'})) {
$self->{'_io'}->destroy;
undef $self->{'_io'};
}
if(ref($self->{'_err'})) {
$self->{'_err'}->remove_all;
undef $self->{'_err'};
}
if(ref($self->{'_xref'})) {
$self->{'_xref'}->remove_all;
undef $self->{'_xref'};
}
$self->_remove_from_index if scalar %Objects_created;
}
=head2 _drop_child
Usage : $object->_drop_child(object_ref)
: Used internally by destroy().
Purpose : To remove a parent-to-child inter-object relationship.
: The aim here is to break cyclical object refs to permit Perl's
: GC to reclaim the object's memory. The expectation is that
: a child object requests of its parent that the parent drop the
: child object making the request. Parents do not drop children
: unless requested by the child in question.
Example : $self->parent->_drop_child($self);
Returns : undef
Argument : Object reference for the child object to be dropped
Throws : Exception if an object ref is not provided as an argument.
Comments : This is a simplistic version that systematically checks every
: data member, searching all top-level array, hash, and scalar
: data members.
: It does not recurse through all levels of complex data members.
: Subclasses could override this method to handle complex child
: data members for more optimal child searching. However, the
: version here is probably sufficient for most situations.
:
: _drop_child() is called by Bio::Root::Object::destroy() for
: all objects with parents.
Status : Experimental
See Also : L
=cut
#---------------'
sub _drop_child {
#---------------
my ($self, $child) = @_;
my ($member, $found);
$self->throw("Child not defined or not an object ($child).") unless ref $child;
local($^W = 0);
foreach $member (keys %{$self}) {
next unless ref($self->{$member});
# compare references.
if (ref($self->{$member}) eq 'ARRAY') {
my ($i);
for($i=0; $i < @{$self->{$member}}; $i++) {
if ($self->{$member}->[$i] eq $child) {
$DEBUG==2 && print STDERR "Removing array child $child\n";
undef $self->{$member}->[$i];
$found = 1; last;
}
}
} elsif(ref($self->{$member}) eq 'HASH') {
foreach(keys %{$self->{$member}}) {
if ($self->{$member}->{$_} eq $child) {
$DEBUG==2 && print STDERR "Removing hash child $child\n";
undef $self->{$member}->{$_};
$found = 1; last;
}
}
} else {
if ($self->{$member} eq $child) {
$DEBUG==2 && print STDERR "Removing child $child\n";
undef $self->{$member};
$found = 1; last;
}
}
}
# Child not found:
# It is possible that a child object has a parent but has not yet been added to
# the parent due to a failure during construction of the child. Not warning.
#$self->warn(sprintf "Child %s not found in Parent %s.", $child->to_string, $self->to_string) unless $found;
undef;
}
#################################################################
# ACCESSORS & INSTANCE METHODS
#################################################################
=head2 name
Usage : $object->name([string]);
Purpose : Set/Get an object's common name.
Example : $myName = $myObj->name;
: $myObj->name('fred');
Returns : String consisting of the object's name or
: "anonymous " if name is not set.
: Thus, this method ALWAYS returns some string.
Argument : String to be used as the common name of the object.
: Should be unique within its class.
See also : L
=cut
#---------
sub name {
#---------
my $self = shift;
# $DEBUG and do{ print STDERR "\n$ID: name(@_) called.";; };
if (@_) { $self->{'_name'} = shift }
return defined $self->{'_name'} ? $self->{'_name'} : 'anonymous '.ref($self);
}
=head2 to_string
Usage : $object->to_string();
Purpose : Get an object as a simple string useful for debugging purposes.
Example : print $myObj->to_string; # prints: Object "