PageRenderTime 25ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/Bio/Phylo/ListableRole.pm

http://github.com/rvosa/bio-phylo
Perl | 609 lines | 522 code | 85 blank | 2 comment | 56 complexity | 43496f0af32181db5da02c13f5a3e409 MD5 | raw file
Possible License(s): LGPL-2.0, GPL-3.0
  1. package Bio::Phylo::ListableRole;
  2. use strict;
  3. use warnings;
  4. use Bio::Phylo::Util::MOP;
  5. use base 'Bio::Phylo::NeXML::Writable';
  6. use Scalar::Util qw'blessed';
  7. use Bio::Phylo::Util::Exceptions 'throw';
  8. use Bio::Phylo::Util::CONSTANT qw':all';
  9. {
  10. my $logger = __PACKAGE__->get_logger;
  11. my ( $DATUM, $NODE, $MATRIX, $TREE ) =
  12. ( _DATUM_, _NODE_, _MATRIX_, _TREE_ );
  13. =head1 NAME
  14. Bio::Phylo::ListableRole - Extra functionality for things that are lists
  15. =head1 SYNOPSIS
  16. No direct usage, parent class. Methods documented here
  17. are available for all objects that inherit from it.
  18. =head1 DESCRIPTION
  19. A listable object is an object that contains multiple smaller objects of the
  20. same type. For example: a tree contains nodes, so it's a listable object.
  21. This class contains methods that are useful for all listable objects: Matrices
  22. (i.e. sets of matrix objects), individual Matrix objects, Datum objects (i.e.
  23. character state sequences), Taxa, Forest, Tree and Node objects.
  24. =head1 METHODS
  25. =head2 ARRAY METHODS
  26. =over
  27. =item prune_entities()
  28. Prunes the container's contents specified by an array reference of indices.
  29. Type : Mutator
  30. Title : prune_entities
  31. Usage : $list->prune_entities([9,7,7,6]);
  32. Function: Prunes a subset of contents
  33. Returns : A Bio::Phylo::Listable object.
  34. Args : An array reference of indices
  35. =cut
  36. sub prune_entities {
  37. my ( $self, @indices ) = @_;
  38. my %indices = map { $_ => 1 } @indices;
  39. my $last_index = $self->last_index;
  40. my @keep;
  41. for my $i ( 0 .. $last_index ) {
  42. push @keep, $i if not exists $indices{$i};
  43. }
  44. return $self->keep_entities( \@keep );
  45. }
  46. =item get_index_of()
  47. Returns the index of the argument in the list,
  48. or undef if the list doesn't contain the argument
  49. Type : Accessor
  50. Title : get_index_of
  51. Usage : my $i = $listable->get_index_of($obj)
  52. Function: Returns the index of the argument in the list,
  53. or undef if the list doesn't contain the argument
  54. Returns : An index or undef
  55. Args : A contained object
  56. =cut
  57. sub get_index_of {
  58. my ( $self, $obj ) = @_;
  59. my $id = $obj->get_id;
  60. my $i = 0;
  61. for my $ent ( @{ $self->get_entities } ) {
  62. return $i if $ent->get_id == $id;
  63. $i++;
  64. }
  65. return;
  66. }
  67. =item get_by_index()
  68. Gets element at index from container.
  69. Type : Accessor
  70. Title : get_by_index
  71. Usage : my $contained_obj = $obj->get_by_index($i);
  72. Function: Retrieves the i'th entity
  73. from a listable object.
  74. Returns : An entity stored by a listable
  75. object (or array ref for slices).
  76. Args : An index or range. This works
  77. the way you dereference any perl
  78. array including through slices,
  79. i.e. $obj->get_by_index(0 .. 10)>
  80. $obj->get_by_index(0, -1)
  81. and so on.
  82. Comments: Throws if out-of-bounds
  83. =cut
  84. sub get_by_index {
  85. my $self = shift;
  86. my $entities = $self->get_entities;
  87. my @range = @_;
  88. if ( scalar @range > 1 ) {
  89. my @returnvalue;
  90. eval { @returnvalue = @{$entities}[@range] };
  91. if ($@) {
  92. throw 'OutOfBounds' => 'index out of bounds';
  93. }
  94. return \@returnvalue;
  95. }
  96. else {
  97. my $returnvalue;
  98. eval { $returnvalue = $entities->[ $range[0] ] };
  99. if ($@) {
  100. throw 'OutOfBounds' => 'index out of bounds';
  101. }
  102. return $returnvalue;
  103. }
  104. }
  105. =item get_by_regular_expression()
  106. Gets elements that match regular expression from container.
  107. Type : Accessor
  108. Title : get_by_regular_expression
  109. Usage : my @objects = @{
  110. $obj->get_by_regular_expression(
  111. -value => $method,
  112. -match => $re
  113. ) };
  114. Function: Retrieves the data in the
  115. current Bio::Phylo::Listable
  116. object whose $method output
  117. matches $re
  118. Returns : A list of Bio::Phylo::* objects.
  119. Args : -value => any of the string
  120. datum props (e.g. 'get_type')
  121. -match => a compiled regular
  122. expression (e.g. qr/^[D|R]NA$/)
  123. =cut
  124. sub get_by_regular_expression {
  125. my $self = shift;
  126. my %o = looks_like_hash @_;
  127. my @matches;
  128. for my $e ( @{ $self->get_entities } ) {
  129. if ( $o{-match} && looks_like_instance( $o{-match}, 'Regexp' ) ) {
  130. if ( $e->get( $o{-value} )
  131. && $e->get( $o{-value} ) =~ $o{-match} )
  132. {
  133. push @matches, $e;
  134. }
  135. }
  136. else {
  137. throw 'BadArgs' => 'need a regular expression to evaluate';
  138. }
  139. }
  140. return \@matches;
  141. }
  142. =item get_by_value()
  143. Gets elements that meet numerical rule from container.
  144. Type : Accessor
  145. Title : get_by_value
  146. Usage : my @objects = @{ $obj->get_by_value(
  147. -value => $method,
  148. -ge => $number
  149. ) };
  150. Function: Iterates through all objects
  151. contained by $obj and returns
  152. those for which the output of
  153. $method (e.g. get_tree_length)
  154. is less than (-lt), less than
  155. or equal to (-le), equal to
  156. (-eq), greater than or equal to
  157. (-ge), or greater than (-gt) $number.
  158. Returns : A reference to an array of objects
  159. Args : -value => any of the numerical
  160. obj data (e.g. tree length)
  161. -lt => less than
  162. -le => less than or equals
  163. -eq => equals
  164. -ge => greater than or equals
  165. -gt => greater than
  166. =cut
  167. sub get_by_value {
  168. my $self = shift;
  169. my %o = looks_like_hash @_;
  170. my @results;
  171. for my $e ( @{ $self->get_entities } ) {
  172. if ( $o{-eq} ) {
  173. if ( $e->get( $o{-value} )
  174. && $e->get( $o{-value} ) == $o{-eq} )
  175. {
  176. push @results, $e;
  177. }
  178. }
  179. if ( $o{-le} ) {
  180. if ( $e->get( $o{-value} )
  181. && $e->get( $o{-value} ) <= $o{-le} )
  182. {
  183. push @results, $e;
  184. }
  185. }
  186. if ( $o{-lt} ) {
  187. if ( $e->get( $o{-value} )
  188. && $e->get( $o{-value} ) < $o{-lt} )
  189. {
  190. push @results, $e;
  191. }
  192. }
  193. if ( $o{-ge} ) {
  194. if ( $e->get( $o{-value} )
  195. && $e->get( $o{-value} ) >= $o{-ge} )
  196. {
  197. push @results, $e;
  198. }
  199. }
  200. if ( $o{-gt} ) {
  201. if ( $e->get( $o{-value} )
  202. && $e->get( $o{-value} ) > $o{-gt} )
  203. {
  204. push @results, $e;
  205. }
  206. }
  207. }
  208. return \@results;
  209. }
  210. =item get_by_name()
  211. Gets first element that has argument name
  212. Type : Accessor
  213. Title : get_by_name
  214. Usage : my $found = $obj->get_by_name('foo');
  215. Function: Retrieves the first contained object
  216. in the current Bio::Phylo::Listable
  217. object whose name is 'foo'
  218. Returns : A Bio::Phylo::* object.
  219. Args : A name (string)
  220. =cut
  221. sub get_by_name {
  222. my ( $self, $name ) = @_;
  223. if ( not defined $name or ref $name ) {
  224. throw 'BadString' => "Can't search on name '$name'";
  225. }
  226. for my $obj ( @{ $self->get_entities } ) {
  227. my $obj_name = $obj->get_name;
  228. if ( $obj_name and $name eq $obj_name ) {
  229. return $obj;
  230. }
  231. }
  232. return;
  233. }
  234. =back
  235. =head2 VISITOR METHODS
  236. =over
  237. =item visit()
  238. Iterates over objects contained by container, executes argument
  239. code reference on each.
  240. Type : Visitor predicate
  241. Title : visit
  242. Usage : $obj->visit(
  243. sub{ print $_[0]->get_name, "\n" }
  244. );
  245. Function: Implements visitor pattern
  246. using code reference.
  247. Returns : The container, possibly modified.
  248. Args : a CODE reference.
  249. =cut
  250. sub visit {
  251. my ( $self, $code ) = @_;
  252. if ( looks_like_instance( $code, 'CODE' ) ) {
  253. for ( @{ $self->get_entities } ) {
  254. $code->($_);
  255. }
  256. }
  257. else {
  258. throw 'BadArgs' => "\"$code\" is not a CODE reference!";
  259. }
  260. return $self;
  261. }
  262. =back
  263. =head2 TESTS
  264. =over
  265. =item contains()
  266. Tests whether the container object contains the argument object.
  267. Type : Test
  268. Title : contains
  269. Usage : if ( $obj->contains( $other_obj ) ) {
  270. # do something
  271. }
  272. Function: Tests whether the container object
  273. contains the argument object
  274. Returns : BOOLEAN
  275. Args : A Bio::Phylo::* object
  276. =cut
  277. sub contains {
  278. my ( $self, $obj ) = @_;
  279. if ( blessed $obj ) {
  280. my $id = $obj->get_id;
  281. for my $ent ( @{ $self->get_entities } ) {
  282. next if not $ent;
  283. return 1 if $ent->get_id == $id;
  284. }
  285. return 0;
  286. }
  287. else {
  288. for my $ent ( @{ $self->get_entities } ) {
  289. next if not $ent;
  290. return 1 if $ent eq $obj;
  291. }
  292. }
  293. }
  294. =item can_contain()
  295. Tests if argument can be inserted in container.
  296. Type : Test
  297. Title : can_contain
  298. Usage : &do_something if $listable->can_contain( $obj );
  299. Function: Tests if $obj can be inserted in $listable
  300. Returns : BOOL
  301. Args : An $obj to test
  302. =cut
  303. sub can_contain {
  304. my ( $self, @obj ) = @_;
  305. for my $obj (@obj) {
  306. my ( $self_type, $obj_container );
  307. eval {
  308. $self_type = $self->_type;
  309. $obj_container = $obj->_container;
  310. };
  311. if ( $@ or $self_type != $obj_container ) {
  312. if ( not $@ ) {
  313. $logger->info(" $self $self_type != $obj $obj_container");
  314. }
  315. else {
  316. $logger->info($@);
  317. }
  318. return 0;
  319. }
  320. }
  321. return 1;
  322. }
  323. =back
  324. =head2 UTILITY METHODS
  325. =over
  326. =item cross_reference()
  327. The cross_reference method links node and datum objects to the taxa they apply
  328. to. After crossreferencing a matrix with a taxa object, every datum object has
  329. a reference to a taxon object stored in its C<$datum-E<gt>get_taxon> field, and
  330. every taxon object has a list of references to datum objects stored in its
  331. C<$taxon-E<gt>get_data> field.
  332. Type : Generic method
  333. Title : cross_reference
  334. Usage : $obj->cross_reference($taxa);
  335. Function: Crossreferences the entities
  336. in the container with names
  337. in $taxa
  338. Returns : string
  339. Args : A Bio::Phylo::Taxa object
  340. Comments:
  341. =cut
  342. sub cross_reference {
  343. my ( $self, $taxa ) = @_;
  344. my ( $selfref, $taxref ) = ( ref $self, ref $taxa );
  345. if ( looks_like_implementor( $taxa, 'get_entities' ) ) {
  346. my $ents = $self->get_entities;
  347. if ( $ents && @{$ents} ) {
  348. foreach ( @{$ents} ) {
  349. if ( looks_like_implementor( $_, 'get_name' )
  350. && looks_like_implementor( $_, 'set_taxon' ) )
  351. {
  352. my $tax = $taxa->get_entities;
  353. if ( $tax && @{$tax} ) {
  354. foreach my $taxon ( @{$tax} ) {
  355. if ( not $taxon->get_name or not $_->get_name )
  356. {
  357. next;
  358. }
  359. if ( $taxon->get_name eq $_->get_name ) {
  360. $_->set_taxon($taxon);
  361. if ( $_->_type == $DATUM ) {
  362. $taxon->set_data($_);
  363. }
  364. if ( $_->_type == $NODE ) {
  365. $taxon->set_nodes($_);
  366. }
  367. }
  368. }
  369. }
  370. }
  371. else {
  372. throw 'ObjectMismatch' =>
  373. "$selfref can't link to $taxref";
  374. }
  375. }
  376. }
  377. if ( $self->_type == $TREE ) {
  378. $self->_get_container->set_taxa($taxa);
  379. }
  380. elsif ( $self->_type == $MATRIX ) {
  381. $self->set_taxa($taxa);
  382. }
  383. return $self;
  384. }
  385. else {
  386. throw 'ObjectMismatch' => "$taxref does not contain taxa";
  387. }
  388. }
  389. =item alphabetize()
  390. Sorts the contents alphabetically by their name.
  391. Type : Generic method
  392. Title : alphabetize
  393. Usage : $obj->alphabetize;
  394. Function: Sorts the contents alphabetically by their name.
  395. Returns : $self
  396. Args : None
  397. Comments:
  398. =cut
  399. sub alphabetize {
  400. my $self = shift;
  401. my @sorted = map { $_->[0] }
  402. sort { $_->[1] cmp $_->[1] }
  403. map { [ $_, $_->get_internal_name ] }
  404. @{ $self->get_entities };
  405. $self->clear;
  406. $self->insert($_) for @sorted;
  407. return $self;
  408. }
  409. =back
  410. =head2 SETS MANAGEMENT
  411. Many Bio::Phylo objects are segmented, i.e. they contain one or more subparts
  412. of the same type. For example, a matrix contains multiple rows; each row
  413. contains multiple cells; a tree contains nodes, and so on. (Segmented objects
  414. all inherit from Bio::Phylo::Listable, i.e. the class whose documentation you're
  415. reading here.) In many cases it is useful to be able to define subsets of the
  416. contents of segmented objects, for example sets of taxon objects inside a taxa
  417. block. The Bio::Phylo::Listable object allows this through a number of methods
  418. (add_set, remove_set, add_to_set, remove_from_set etc.). Those methods delegate
  419. the actual management of the set contents to the L<Bio::Phylo::Set> object.
  420. Consult the documentation for L<Bio::Phylo::Set> for a code sample.
  421. =over
  422. =item sets_to_xml()
  423. Returns string representation of sets
  424. Type : Accessor
  425. Title : sets_to_xml
  426. Usage : my $str = $obj->sets_to_xml;
  427. Function: Gets xml string
  428. Returns : Scalar
  429. Args : None
  430. =cut
  431. sub sets_to_xml {
  432. my $self = shift;
  433. my $xml = '';
  434. if ( $self->can('get_sets') ) {
  435. for my $set ( @{ $self->get_sets } ) {
  436. my %contents;
  437. for my $ent ( @{ $self->get_entities } ) {
  438. if ( $self->is_in_set($ent,$set) ) {
  439. my $tag = $ent->get_tag;
  440. $contents{$tag} = [] if not $contents{$tag};
  441. push @{ $contents{$tag} }, $ent->get_xml_id;
  442. }
  443. }
  444. for my $key ( keys %contents ) {
  445. my @ids = @{ $contents{$key} };
  446. $contents{$key} = join ' ', @ids;
  447. }
  448. $set->set_attributes(%contents);
  449. $xml .= "\n" . $set->to_xml;
  450. }
  451. }
  452. return $xml;
  453. }
  454. =back
  455. =cut
  456. # podinherit_insert_token
  457. =head1 SEE ALSO
  458. There is a mailing list at L<https://groups.google.com/forum/#!forum/bio-phylo>
  459. for any user or developer questions and discussions.
  460. Also see the manual: L<Bio::Phylo::Manual> and L<http://rutgervos.blogspot.com>.
  461. =head2 Objects inheriting from Bio::Phylo::Listable
  462. =over
  463. =item L<Bio::Phylo::Forest>
  464. Iterate over a set of trees.
  465. =item L<Bio::Phylo::Forest::Tree>
  466. Iterate over nodes in a tree.
  467. =item L<Bio::Phylo::Forest::Node>
  468. Iterate of children of a node.
  469. =item L<Bio::Phylo::Matrices>
  470. Iterate over a set of matrices.
  471. =item L<Bio::Phylo::Matrices::Matrix>
  472. Iterate over the datum objects in a matrix.
  473. =item L<Bio::Phylo::Matrices::Datum>
  474. Iterate over the characters in a datum.
  475. =item L<Bio::Phylo::Taxa>
  476. Iterate over a set of taxa.
  477. =back
  478. =head2 Superclasses
  479. =over
  480. =item L<Bio::Phylo::NeXML::Writable>
  481. This object inherits from L<Bio::Phylo::NeXML::Writable>, so methods
  482. defined there are also applicable here.
  483. =back
  484. =head1 CITATION
  485. If you use Bio::Phylo in published research, please cite it:
  486. B<Rutger A Vos>, B<Jason Caravas>, B<Klaas Hartmann>, B<Mark A Jensen>
  487. and B<Chase Miller>, 2011. Bio::Phylo - phyloinformatic analysis using Perl.
  488. I<BMC Bioinformatics> B<12>:63.
  489. L<http://dx.doi.org/10.1186/1471-2105-12-63>
  490. =cut
  491. }
  492. 1;