# $Id: Phylo.pm 2196 2006-09-07 21:35:47Z rvosa $
package Bio::Phylo;
use strict;
# old ugly hack for PAR, see
# http://groups.google.com/group/perl.par/browse_thread/thread/4cfec27c14cc8f60/3549d0ce5355a5ea
# delete $INC{'Scalar/Util.pm'};
# eval 'use Scalar::Util 1.14 qw(weaken blessed);';
use Scalar::Util qw(weaken blessed);
use Bio::Phylo::Util::CONSTANT qw(looks_like_number);
use Bio::Phylo::Util::IDPool;
use Bio::Phylo::Util::Exceptions;
# The bit of voodoo is for including CVS keywords in the main source file.
# $Id is the subversion revision number. The way I set it up here allows
# 'make dist' to build a *.tar.gz without the "_rev#" in the package name, while
# it still shows up otherwise (e.g. during 'make test') as a developer release,
# with the "_rev#".
my $rev = '$Id: Phylo.pm 2196 2006-09-07 21:35:47Z rvosa $';
$rev =~ s/^[^\d]+(\d+)\b.*$/$1/;
our $VERSION = 0.15;
$VERSION .= "_$rev";
my $VERBOSE = 0;
use vars qw($VERSION);
{
# inside out class arrays
my @name;
my @desc;
my @score;
my @generic;
my @cache;
my @container;
# $fields hashref necessary for object destruction
my $fields = {
'-name' => \@name,
'-desc' => \@desc,
'-score' => \@score,
'-generic' => \@generic,
'-cache' => \@cache,
'-container' => \@container,
};
# global container for Forest, Matrix and Taxa objects (a la Mesquite
# project)
my $super = {};
=head1 NAME
Bio::Phylo - Phylogenetic analysis using perl.
=head1 DESCRIPTION
This is the base class for the Bio::Phylo package. All other modules inherit
from it, the methods defined here are applicable to all. Consult the manual
for usage examples: L<Bio::Phylo::Manual>.
=head1 METHODS
=head2 CONSTRUCTOR
=over
=item new()
The Bio::Phylo object itself, and thus its constructor, is rarely, if ever, used
directly. Rather, all other objects in this package inherit its methods, and call
its constructor internally.
Type : Constructor
Title : new
Usage : my $phylo = Bio::Phylo->new;
Function: Instantiates Bio::Phylo object
Returns : a Bio::Phylo object
Args : -name => (object name)
-desc => (object description)
-score => (numerical score)
-generic => (generic key/value pair)
=cut
sub new {
my $class = shift;
my $self = Bio::Phylo::Util::IDPool->_initialize();
bless $self, __PACKAGE__;
if (@_) {
my %opt;
eval { %opt = @_; };
if ($@) {
Bio::Phylo::Util::Exceptions::OddHash->throw( error => $@ );
}
else {
while ( my ( $key, $value ) = each %opt ) {
if ( $fields->{$key} ) {
$fields->{$key}->[ $self->get_id ] = $value;
delete $opt{$key};
}
}
@_ = %opt;
}
}
return $self;
}
=back
=head2 MUTATORS
=over
=item set_name()
Type : Mutator
Title : set_name
Usage : $obj->set_name($name);
Function: Assigns an object's name.
Returns : Modified object.
Args : Argument must be a string, single
quoted if it contains [;|,|:\(|\)]
or spaces.
=cut
sub set_name {
my ( $self, $name ) = @_;
my $ref = ref $self;
if ( $name && $name !~ m/^['"][^'"]*['"]$/ && $name =~ m/(?:;|,|:|\(|\)|\s)/ ) {
Bio::Phylo::Util::Exceptions::BadString->throw(
error => "\"$name\" is a bad name format for $ref names"
);
}
else {
$name[ $self->get_id ] = $name =~ s/^\s*(.*?)\s*$/$1/;
}
return $self;
}
=item set_desc()
Type : Mutator
Title : set_desc
Usage : $obj->set_desc($desc);
Function: Assigns an object's description.
Returns : Modified object.
Args : Argument must be a string.
=cut
sub set_desc {
my ( $self, $desc ) = @_;
$desc[ $self->get_id ] = $desc;
return $self;
}
=item set_score()
Type : Mutator
Title : set_score
Usage : $obj->set_score($score);
Function: Assigns an object's numerical score.
Returns : Modified object.
Args : Argument must be any of
perl's number formats.
=cut
sub set_score {
my $self = $_[0];
if ( defined $_[1] ) {
my $score = $_[1];
if ( looks_like_number $score ) {
$score[ $self->get_id ] = $score;
}
else {
Bio::Phylo::Util::Exceptions::BadNumber->throw(
error => "Score \"$score\" is a bad number" );
}
}
else {
$score[ $self->get_id ] = undef;
}
return $self;
}
=item set_generic()
Type : Mutator
Title : set_generic
Usage : $obj->set_generic(%generic);
Function: Assigns generic key/value pairs to the invocant.
Returns : Modified object.
Args : Valid arguments constitute
key/value pairs, for example:
$node->set_generic(
'-posterior' => 0.87565,
);
=cut
sub set_generic {
my $self = shift;
if (@_) {
my %args;
eval { %args = @_ };
if ($@) {
Bio::Phylo::Util::Exceptions::OddHash->throw( error => $@ );
}
else {
foreach my $key ( keys %args ) {
$generic[ $self->get_id ]->{$key} = $args{$key};
}
}
}
else {
$generic[ $self->get_id ] = {};
}
return $self;
}
=back
=head2 ACCESSORS
=over
=item get_name()
Type : Accessor
Title : get_name
Usage : my $name = $obj->get_name;
Function: Returns the object's name (if any).
Returns : A string
Args : None
=cut
sub get_name {
my $self = shift;
return $name[ $self->get_id ];
}
=item get_desc()
Type : Accessor
Title : get_desc
Usage : my $desc = $obj->get_desc;
Function: Returns the object's description (if any).
Returns : A string
Args : None
=cut
sub get_desc {
my $self = shift;
return $desc[ $self->get_id ];
}
=item get_score()
Type : Accessor
Title : get_score
Usage : my $score = $obj->get_score;
Function: Returns the object's numerical score (if any).
Returns : A number
Args : None
=cut
sub get_score {
my $self = shift;
return $score[ $self->get_id ];
}
=item get_generic()
Type : Accessor
Title : get_generic
Usage : my $value = $obj->get_generic($key);
or
my %hash = %{ $obj->get_generic() };
Function: Returns the object's generic data. If an
argument is used, it is considered a key
for which the associated value is return.
Without arguments, a reference to the whole
hash is returned.
Returns : A string or hash reference.
Args : None
=cut
sub get_generic {
my ( $self, $key ) = @_;
if ( defined $key ) {
return $generic[ $self->get_id ]->{$key};
}
else {
return $generic[ $self->get_id ];
}
}
=item get_id()
Type : Accessor
Title : get_id
Usage : my $id = $obj->get_id;
Function: Returns the object's unique ID
Returns : INT
Args : None
=cut
sub get_id {
my $self = shift;
if ( UNIVERSAL::isa( $self, 'SCALAR' ) ) {
return $$self;
}
# for tied Bio::Phylo::Listable arrays
elsif ( UNIVERSAL::isa( $self, 'ARRAY' ) ) {
my $tied = tied @{ $self };
if ( $tied and UNIVERSAL::isa( $tied, 'SCALAR' ) ) {
return $$tied;
}
elsif ( not $tied ) {
# die "No object tied to \"$self\"\n";
}
}
else {
# die "Object \"$self\" neither a tied ARRAY nor a SCALAR\n";
}
}
=back
=head2 PACKAGE METHODS
=over
=item get()
All objects in the package subclass the Bio::Phylo object, and so, for example,
you can do C<$node-E<gt>get('get_branch_length');> instead of C<$node-E<gt>get_branch_length>.
This is a useful feature for listable objects especially, as they have the
get_by_value method, which allows you to retrieve, for instance, a list of nodes
whose branch length exceeds a certain value. That method (and
get_by_regular_expression) uses this C<$obj-E<gt>get method>.
Type : Accessor
Title : get
Usage : my $treename = $tree->get('get_name');
Function: Alternative syntax for safely accessing
any of the object data; useful for
interpolating runtime $vars.
Returns : (context dependent)
Args : a SCALAR variable, e.g. $var = 'get_name';
=cut
sub get {
my ( $self, $var ) = @_;
if ( $self->can($var) ) {
return $self->$var;
}
else {
my $ref = ref $self;
Bio::Phylo::Util::Exceptions::UnknownMethod->throw(
error => "sorry, a \"$ref\" can't \"$var\"" );
}
}
=item clone()
Type : Utility method
Title : clone
Usage : my $clone = $object->clone;
Function: Creates a copy of the invocant object.
Returns : A copy of the invocant.
Args : none.
=cut
sub clone {
my $self = shift;
#my $clone = dclone($self);
#return $clone;
}
=item VERBOSE()
Getter and setter for the verbose level. Currently it's just 0 = no messages,
1 = messages, but perhaps there could be more levels? For caller diagnostics
and so on?
Type : Accessor
Title : VERBOSE(0|1)
Usage : Phylo->VERBOSE(0|1)
Function: Sets/gets verbose level
Returns : Verbose level
Args : 0 = no messages; 1 = error messages
Comments:
=cut
sub VERBOSE {
my $class = shift;
if (@_) {
my %opt;
eval { %opt = @_; };
if ($@) {
Bio::Phylo::Util::Exceptions::OddHash->throw( error => $@ );
}
$VERBOSE = $opt{'-level'};
}
return $VERBOSE;
}
=item CITATION()
Type : Accessor
Title : CITATION
Usage : $phylo->CITATION;
Function: Returns suggested citation.
Returns : Returns suggested citation.
Args : None
Comments:
=cut
sub CITATION {
my $self = shift;
my $name = __PACKAGE__;
my $version = __PACKAGE__->VERSION;
my $string = qq{Rutger A. Vos, 2006. $name: };
$string .= qq{Phylogenetic analysis using Perl, version $version};
return $string;
}
=item VERSION()
Type : Accessor
Title : VERSION
Usage : $phylo->VERSION;
Function: Returns version number
(including CVS revision number).
Alias :
Returns : SCALAR
Args : NONE
Comments:
=cut
sub VERSION { $VERSION; }
=item to_xml()
Type : Format converter
Title : to_xml
Usage : my $xml = $obj->to_xml;
Function: Returns an XML representation of the invocant object.
Returns : SCALAR
Args : NONE
=cut
sub to_xml {
my $self = shift;
my $class = ref $self;
$class =~ s/^.*:([^:]+)$/$1/g;
$class = lc($class);
my $xml = '<' . $class . ' id="' . $class . $self->get_id . '">';
my $generic = $self->get_generic;
my ( $name, $score, $desc ) =
( $self->get_name, $self->get_score, $self->get_desc );
$xml .= '<name>' . $name . '</name>' if $name;
$xml .= '<score>' . $score . '</score>' if $score;
$xml .= '<desc>' . $desc . '</desc>' if $desc;
if ( $generic and ref $generic eq 'HASH' ) {
$xml .= "<generic>\n";
$xml .= "<opt><key>$_</key><val>$generic->{$_}</val></opt>" for keys %$generic;
$xml .= "</generic>";
}
if ( $self->isa('Bio::Phylo::Listable') ) {
foreach my $ent ( @{ $self->get_entities } ) {
$xml .= $ent->to_xml;
}
}
$xml .= '</' . $class . '>';
return $xml;
}
=back
=head2 DESTRUCTOR
=over
=item DESTROY()
Type : Destructor
Title : DESTROY
Usage : $phylo->DESTROY
Function: Destroys Phylo object
Alias :
Returns : TRUE
Args : none
Comments: You don't really need this,
it is called automatically when
the object goes out of scope.
=cut
sub DESTROY {
my $self = shift;
if ( my $i = $self->get_id ) {
foreach ( keys %{$fields} ) {
delete $fields->{$_}->[$i];
}
}
Bio::Phylo::Util::IDPool->_reclaim($self);
return 1;
}
=begin comment
Type : Internal method
Title : _check_cache
Usage : $node->_check_cache;
Function: Retrieves intermediate calculation results.
Returns : SCALAR
Args :
=end comment
=cut
sub _check_cache {
my $self = shift;
my @caller = caller(1);
if ( exists $cache[ $self->get_id ]->{ $caller[3] } ) {
return 1, $cache[ $self->get_id ]->{ $caller[3] };
}
}
=begin comment
Type : Internal method
Title : _store_cache
Usage : $node->_store_cache($value);
Function: Stores intermediate calculation results.
Returns : VOID
Args :
=end comment
=cut
sub _store_cache {
my ( $self, $result ) = @_;
my @caller = caller(1);
$cache[ $self->get_id ]->{ $caller[3] } = $result;
}
=begin comment
Type : Internal method
Title : _flush_cache
Usage : $node->_flush_cache;
Function: Stores intermediate calculation results.
Returns : VOID
Args :
=end comment
=cut
sub _flush_cache {
my $self = shift;
$cache[ $self->get_id ] = {};
}
=begin comment
Type : Internal method
Title : _get_container
Usage : $phylo->_get_container;
Function: Retrieves the object that contains the invocant (e.g. for a node,
returns the tree it is in).
Returns : Bio::Phylo::* object
Args : None
=end comment
=cut
sub _get_container {
my $self = shift;
return $container[ $self->get_id ];
}
=begin comment
Type : Internal method
Title : _set_container
Usage : $phylo->_set_container($obj);
Function: Creates a reference from the invocant to the object that contains
it (e.g. for a node, creates a reference to the tree it is in).
Returns : Bio::Phylo::* object
Args : A Bio::Phylo::Listable object
=end comment
=cut
sub _set_container {
my ( $self, $container ) = @_;
if ( blessed $container ) {
if ( $container->can('_type') && $self->can('_container') ) {
if ( $container->_type == $self->_container ) {
if ( $container->contains($self) ) {
$container[ $self->get_id ] = $container;
weaken( $container[ $self->get_id ] );
return $self;
}
else {
Bio::Phylo::Util::Exceptions::ObjectMismatch->throw(
error => "\"$self\" not in \"$container\"", );
}
}
else {
Bio::Phylo::Util::Exceptions::ObjectMismatch->throw(
error => "\"$container\" cannot contain \"$self\"", );
}
}
else {
Bio::Phylo::Util::Exceptions::ObjectMismatch->throw(
error => "Invalid objects", );
}
}
else {
Bio::Phylo::Util::Exceptions::BadArgs->throw(
error => "Argument not an object", );
}
}
=begin comment
Type : Internal method
Title : _set_super
Usage : $phylo->_set_super;
Function: Creates a reference to the invocant in the static $super hashref
Returns : Bio::Phylo::* object
Args : None;
=end comment
=cut
sub _set_super {
my $self = shift;
$super->{$self} = $self;
weaken( $super->{$self} );
return $self;
}
=begin comment
Type : Internal method
Title : _get_super
Usage : Bio::Phylo->_get_super;
Function: Returns all references in the static $super hashref
Returns : Bio::Phylo::* objects in an array ref
Args : None;
=end comment
=cut
sub _get_super {
my @tmp = values %{$super};
return \@tmp;
}
=begin comment
Type : Internal method
Title : _del_from_super;
Usage : $phylo->_del_from_super;
Function: Deletes invocant from $super hashref
Returns : VOID
Args : None;
=end comment
=cut
sub _del_from_super {
my $self = shift;
delete $super->{$self};
return;
}
=back
=head1 SEE ALSO
Also see the manual: L<Bio::Phylo::Manual>.
=head1 FORUM
CPAN hosts a discussion forum for Bio::Phylo. If you have trouble using this
module the discussion forum is a good place to start posting questions (NOT bug
reports, see below): L<http://www.cpanforum.com/dist/Bio-Phylo>
=head1 BUGS
Please report any bugs or feature requests to C<< bug-bio-phylo@rt.cpan.org >>,
or through the web interface at
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Bio-Phylo>. I will be notified,
and then you'll automatically be notified of progress on your bug as I make
changes. Be sure to include the following in your request or comment, so that
I know what version you're using:
$Id: Phylo.pm 2196 2006-09-07 21:35:47Z rvosa $
=head1 AUTHOR
Rutger Vos,
=over
=item email: L<mailto://rvosa@sfu.ca>
=item web page: L<http://www.sfu.ca/~rvosa/>
=back
=head1 ACKNOWLEDGEMENTS
The author would like to thank Jason Stajich for many ideas borrowed from
BioPerl L<http://www.bioperl.org>, and CIPRES L<http://www.phylo.org> and
FAB* L<http://www.sfu.ca/~fabstar> for comments and requests.
=head1 COPYRIGHT & LICENSE
Copyright 2005 Rutger Vos, All Rights Reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=cut
}
package Bio::Phylo::Deprecated;
package Bio::Phylo::Parsers::Fastnewick;
push @Bio::Phylo::Parsers::Fastnewick::ISA, 'Bio::Phylo::Deprecated';
package Bio::Phylo::Parsers::Fastnexus;
push @Bio::Phylo::Parsers::Fastnexus::ISA, 'Bio::Phylo::Deprecated';
package Bio::Phylo::Matrices::Sequence;
push @Bio::Phylo::Matrices::Sequence::ISA, 'Bio::Phylo::Deprecated';
package Bio::Phylo::Matrices::Alignment;
push @Bio::Phylo::Matrices::Alignment::ISA, 'Bio::Phylo::Deprecated';
package Bio::Phylo::Parsers;
push @Bio::Phylo::Parsers::ISA, 'Bio::Phylo::Deprecated';
package Bio::Phylo::Unparsers;
push @Bio::Phylo::Unparsers::ISA, 'Bio::Phylo::Deprecated';
1;