| 0 | 1 # $Id: SimpleOntologyEngine.pm,v 1.3.2.5 2003/07/03 00:41:40 lapp Exp $ | 
|  | 2 # | 
|  | 3 # BioPerl module for SimpleOntologyEngine | 
|  | 4 # | 
|  | 5 # Cared for by Peter Dimitrov <dimitrov@gnf.org> | 
|  | 6 # | 
|  | 7 # Copyright Peter Dimitrov | 
|  | 8 # (c) Peter Dimitrov, dimitrov@gnf.org, 2002. | 
|  | 9 # (c) GNF, Genomics Institute of the Novartis Research Foundation, 2002. | 
|  | 10 # | 
|  | 11 # You may distribute this module under the same terms as perl itself. | 
|  | 12 # Refer to the Perl Artistic License (see the license accompanying this | 
|  | 13 # software package, or see http://www.perl.com/language/misc/Artistic.html) | 
|  | 14 # for the terms under which you may use, modify, and redistribute this module. | 
|  | 15 # | 
|  | 16 # THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED | 
|  | 17 # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF | 
|  | 18 # MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | 
|  | 19 # | 
|  | 20 # POD documentation - main docs before the code | 
|  | 21 | 
|  | 22 =head1 NAME | 
|  | 23 | 
|  | 24 SimpleOntologyEngine - Implementation of OntologyEngineI interface | 
|  | 25 | 
|  | 26 =head1 SYNOPSIS | 
|  | 27 | 
|  | 28   my $soe = Bio::Ontology::SimpleOntologyEngine->new; | 
|  | 29 | 
|  | 30 | 
|  | 31 =head1 DESCRIPTION | 
|  | 32 | 
|  | 33 This is a "simple" implementation of Bio::Ontology::OntologyEngineI. | 
|  | 34 | 
|  | 35 =head1 FEEDBACK | 
|  | 36 | 
|  | 37 =head2 Mailing Lists | 
|  | 38 | 
|  | 39 User feedback is an integral part of the evolution of this and other | 
|  | 40 Bioperl modules. Send your comments and suggestions preferably to | 
|  | 41 the Bioperl mailing list.  Your participation is much appreciated. | 
|  | 42 | 
|  | 43   bioperl-l@bioperl.org              - General discussion | 
|  | 44   http://bioperl.org/MailList.shtml  - About the mailing lists | 
|  | 45 | 
|  | 46 =head2 Reporting Bugs | 
|  | 47 | 
|  | 48 Report bugs to the Bioperl bug tracking system to help us keep track | 
|  | 49 of the bugs and their resolution. Bug reports can be submitted via | 
|  | 50 email or the web: | 
|  | 51 | 
|  | 52   bioperl-bugs@bioperl.org | 
|  | 53   http://bioperl.org/bioperl-bugs/ | 
|  | 54 | 
|  | 55 =head1 AUTHOR - Peter Dimitrov | 
|  | 56 | 
|  | 57 Email dimitrov@gnf.org | 
|  | 58 | 
|  | 59 =head1 CONTRIBUTORS | 
|  | 60 | 
|  | 61 Hilmar Lapp, hlapp at gmx.net | 
|  | 62 | 
|  | 63 =head1 APPENDIX | 
|  | 64 | 
|  | 65 The rest of the documentation details each of the object methods. | 
|  | 66 Internal methods are usually preceded with a _ | 
|  | 67 | 
|  | 68 =cut | 
|  | 69 | 
|  | 70 | 
|  | 71 # Let the code begin... | 
|  | 72 | 
|  | 73 | 
|  | 74 package Bio::Ontology::SimpleOntologyEngine; | 
|  | 75 use vars qw(@ISA); | 
|  | 76 use strict; | 
|  | 77 use Carp; | 
|  | 78 use Bio::Root::Root; | 
|  | 79 use Bio::Ontology::RelationshipFactory; | 
|  | 80 use Bio::Ontology::OntologyEngineI; | 
|  | 81 use Data::Dumper; | 
|  | 82 | 
|  | 83 @ISA = qw( Bio::Root::Root Bio::Ontology::OntologyEngineI ); | 
|  | 84 | 
|  | 85 =head2 new | 
|  | 86 | 
|  | 87  Title   : new | 
|  | 88  Usage   : $soe = Bio::Ontology::SimpleOntologyEngine->new; | 
|  | 89  Function: Initializes the ontology engine. | 
|  | 90  Example : $soe = Bio::Ontology::SimpleOntologyEngine->new; | 
|  | 91  Returns : Object of class SimpleOntologyEngine. | 
|  | 92  Args    : | 
|  | 93 | 
|  | 94 | 
|  | 95 =cut | 
|  | 96 | 
|  | 97 sub new{ | 
|  | 98   my ($class, @args) = @_; | 
|  | 99   my $self = $class->SUPER::new(@args); | 
|  | 100 #   my %param = @args; | 
|  | 101 | 
|  | 102   $self->_term_store( {} ); | 
|  | 103   $self->_relationship_store( {} ); | 
|  | 104   $self->_inverted_relationship_store( {} ); | 
|  | 105   $self->_relationship_type_store( {} ); | 
|  | 106   $self->_instantiated_terms_store( {} ); | 
|  | 107 | 
|  | 108   # set defaults for the factories | 
|  | 109   $self->relationship_factory(Bio::Ontology::RelationshipFactory->new( | 
|  | 110 				     -type => "Bio::Ontology::Relationship")); | 
|  | 111   return $self; | 
|  | 112 } | 
|  | 113 | 
|  | 114 =head2 _instantiated_terms_store | 
|  | 115 | 
|  | 116  Title   : _instantiated_terms_store | 
|  | 117  Usage   : $obj->_instantiated_terms_store($newval) | 
|  | 118  Function: | 
|  | 119  Example : | 
|  | 120  Returns : hash | 
|  | 121  Args    : empty hash | 
|  | 122 | 
|  | 123 | 
|  | 124 =cut | 
|  | 125 | 
|  | 126 sub _instantiated_terms_store{ | 
|  | 127   my ($self, $value) = @_; | 
|  | 128 | 
|  | 129   if( defined $value) { | 
|  | 130     $self->{'_instantiated_terms_store'} = $value; | 
|  | 131   } | 
|  | 132   return $self->{'_instantiated_terms_store'}; | 
|  | 133 } | 
|  | 134 | 
|  | 135 =head2 mark_instantiated | 
|  | 136 | 
|  | 137  Title   : mark_instantiated | 
|  | 138  Usage   : $self->mark_instantiated(TermI[] terms): TermI[] | 
|  | 139  Function: Marks TermI objects as fully instantiated, | 
|  | 140   allowing for proper counting of the number of terms in the term store. | 
|  | 141 The TermI objects has to be already stored in the term store in order to | 
|  | 142  be marked. | 
|  | 143  Example : $self->mark_instantiated($term); | 
|  | 144  Returns : its argument or throws an exception if a term is not | 
|  | 145   in the term store. | 
|  | 146  Args    : array of objects of class TermI. | 
|  | 147 | 
|  | 148 =cut | 
|  | 149 | 
|  | 150 sub mark_instantiated{ | 
|  | 151   my ($self, @terms) = @_; | 
|  | 152 | 
|  | 153   foreach my $term (@terms) { | 
|  | 154     $self->throw( "term ".$term->identifier." not in the term store\n" ) | 
|  | 155       if !defined $self->_term_store->{$term->identifier}; | 
|  | 156     $self->_instantiated_terms_store->{$term->identifier} = 1; | 
|  | 157   } | 
|  | 158 | 
|  | 159   return @terms; | 
|  | 160 } | 
|  | 161 | 
|  | 162 =head2 mark_uninstantiated | 
|  | 163 | 
|  | 164  Title   : mark_uninstantiated | 
|  | 165  Usage   : $self->mark_uninstantiated(TermI[] terms): TermI[] | 
|  | 166  Function: Marks TermI objects as not fully instantiated, | 
|  | 167  Example : $self->mark_uninstantiated($term); | 
|  | 168  Returns : its argument or throws an exception if a term is not | 
|  | 169   in the term store(if the term is not marked it does nothing). | 
|  | 170  Args    : array of objects of class TermI. | 
|  | 171 | 
|  | 172 | 
|  | 173 =cut | 
|  | 174 | 
|  | 175 sub mark_uninstantiated{ | 
|  | 176   my ($self, @terms) = @_; | 
|  | 177 | 
|  | 178   foreach my $term (@terms) { | 
|  | 179     $self->throw( "term ".$term->identifier." not in the term store\n" ) | 
|  | 180       if !defined $self->_term_store->{$term->identifier}; | 
|  | 181     delete $self->_instantiated_terms_store->{$term->identifier} | 
|  | 182       if defined $self->_instantiated_terms_store->{$term->identifier}; | 
|  | 183   } | 
|  | 184 | 
|  | 185   return @terms; | 
|  | 186 } | 
|  | 187 | 
|  | 188 =head2 _term_store | 
|  | 189 | 
|  | 190  Title   : term_store | 
|  | 191  Usage   : $obj->_term_store($newval) | 
|  | 192  Function: | 
|  | 193  Example : | 
|  | 194  Returns : reference to an array of Bio::Ontology::TermI objects | 
|  | 195  Args    : reference to an array of Bio::Ontology::TermI objects | 
|  | 196 | 
|  | 197 =cut | 
|  | 198 | 
|  | 199 sub _term_store{ | 
|  | 200   my ($self, $value) = @_; | 
|  | 201 | 
|  | 202   if( defined $value) { | 
|  | 203     if ( defined $self->{'_term_store'}) { | 
|  | 204       $self->throw("_term_store already defined\n"); | 
|  | 205     } | 
|  | 206     else { | 
|  | 207       $self->{'_term_store'} = $value; | 
|  | 208     } | 
|  | 209   } | 
|  | 210 | 
|  | 211   return $self->{'_term_store'}; | 
|  | 212 } | 
|  | 213 | 
|  | 214 =head2 add_term | 
|  | 215 | 
|  | 216  Title   : add_term | 
|  | 217  Usage   : add_term(TermI term): TermI | 
|  | 218  Function: Adds TermI object to the ontology engine term store. | 
|  | 219  Marks the term fully instantiated by default. | 
|  | 220  Example : $soe->add_term($term) | 
|  | 221  Returns : its argument. | 
|  | 222  Args    : object of class TermI. | 
|  | 223 | 
|  | 224 =cut | 
|  | 225 | 
|  | 226 sub add_term{ | 
|  | 227   my ($self, $term) = @_; | 
|  | 228   my $term_store = $self->_term_store; | 
|  | 229 | 
|  | 230   if ( defined $term_store -> {$term->identifier}) { | 
|  | 231     $self->throw( "term ".$term->identifier." already defined\n" ); | 
|  | 232   } | 
|  | 233   else { | 
|  | 234     $term_store->{$term->identifier} = $term; | 
|  | 235     $self->_instantiated_terms_store->{$term->identifier} = 1; | 
|  | 236   } | 
|  | 237 | 
|  | 238   return $term; | 
|  | 239 } | 
|  | 240 | 
|  | 241 =head2 get_term_by_identifier | 
|  | 242 | 
|  | 243  Title   : get_term_by_identifier | 
|  | 244  Usage   : get_term_by_identifier(String[] id): TermI[] | 
|  | 245  Function: Retrieves terms from the term store by their identifier | 
|  | 246            field, or undef if not there. | 
|  | 247  Example : $term = $soe->get_term_by_identifier("IPR000001"); | 
|  | 248  Returns : An array of zero or more Bio::Ontology::TermI objects. | 
|  | 249  Args    : An array of identifier strings | 
|  | 250 | 
|  | 251 | 
|  | 252 =cut | 
|  | 253 | 
|  | 254 sub get_term_by_identifier{ | 
|  | 255   my ($self, @ids) = @_; | 
|  | 256   my @ans = (); | 
|  | 257 | 
|  | 258   foreach my $id (@ids) { | 
|  | 259       my $term = $self->_term_store->{$id}; | 
|  | 260       push @ans, $term if defined $term; | 
|  | 261   } | 
|  | 262 | 
|  | 263   return @ans; | 
|  | 264 } | 
|  | 265 | 
|  | 266 =head2 _get_number_rels | 
|  | 267 | 
|  | 268  Title   : get_number_rels | 
|  | 269  Usage   : | 
|  | 270  Function: | 
|  | 271  Example : | 
|  | 272  Returns : | 
|  | 273  Args    : | 
|  | 274 | 
|  | 275 | 
|  | 276 =cut | 
|  | 277 | 
|  | 278 sub _get_number_rels{ | 
|  | 279   my ($self) = @_; | 
|  | 280   my $num_rels = 0; | 
|  | 281 | 
|  | 282   foreach my $entry ($self->_relationship_store) { | 
|  | 283     $num_rels += scalar keys %$entry; | 
|  | 284   } | 
|  | 285   return $num_rels; | 
|  | 286 } | 
|  | 287 | 
|  | 288 =head2 _get_number_terms | 
|  | 289 | 
|  | 290  Title   : _get_number_terms | 
|  | 291  Usage   : | 
|  | 292  Function: | 
|  | 293  Example : | 
|  | 294  Returns : | 
|  | 295  Args    : | 
|  | 296 | 
|  | 297 | 
|  | 298 =cut | 
|  | 299 | 
|  | 300 sub _get_number_terms{ | 
|  | 301   my ($self) = @_; | 
|  | 302 | 
|  | 303   return scalar $self->_filter_unmarked( values %{$self->_term_store} ); | 
|  | 304 | 
|  | 305 } | 
|  | 306 | 
|  | 307 =head2 _relationship_store | 
|  | 308 | 
|  | 309  Title   : _storerelationship_store | 
|  | 310  Usage   : $obj->relationship_store($newval) | 
|  | 311  Function: | 
|  | 312  Example : | 
|  | 313  Returns : reference to an array of Bio::Ontology::TermI objects | 
|  | 314  Args    : reference to an array of Bio::Ontology::TermI objects | 
|  | 315 | 
|  | 316 | 
|  | 317 =cut | 
|  | 318 | 
|  | 319 sub _relationship_store{ | 
|  | 320   my ($self, $value) = @_; | 
|  | 321 | 
|  | 322   if( defined $value) { | 
|  | 323     if ( defined $self->{'_relationship_store'}) { | 
|  | 324       $self->throw("_relationship_store already defined\n"); | 
|  | 325     } | 
|  | 326     else { | 
|  | 327       $self->{'_relationship_store'} = $value; | 
|  | 328     } | 
|  | 329   } | 
|  | 330 | 
|  | 331   return $self->{'_relationship_store'}; | 
|  | 332 } | 
|  | 333 | 
|  | 334 =head2 _inverted_relationship_store | 
|  | 335 | 
|  | 336  Title   : _inverted_relationship_store | 
|  | 337  Usage   : | 
|  | 338  Function: | 
|  | 339  Example : | 
|  | 340  Returns : reference to an array of Bio::Ontology::TermI objects | 
|  | 341  Args    : reference to an array of Bio::Ontology::TermI objects | 
|  | 342 | 
|  | 343 | 
|  | 344 =cut | 
|  | 345 | 
|  | 346 sub _inverted_relationship_store{ | 
|  | 347   my ($self, $value) = @_; | 
|  | 348 | 
|  | 349   if( defined $value) { | 
|  | 350     if ( defined $self->{'_inverted_relationship_store'}) { | 
|  | 351       $self->throw("_inverted_relationship_store already defined\n"); | 
|  | 352     } | 
|  | 353     else { | 
|  | 354       $self->{'_inverted_relationship_store'} = $value; | 
|  | 355     } | 
|  | 356   } | 
|  | 357 | 
|  | 358   return $self->{'_inverted_relationship_store'}; | 
|  | 359 } | 
|  | 360 | 
|  | 361 =head2 _relationship_type_store | 
|  | 362 | 
|  | 363  Title   : _relationship_type_store | 
|  | 364  Usage   : $obj->_relationship_type_store($newval) | 
|  | 365  Function: | 
|  | 366  Example : | 
|  | 367  Returns : reference to an array of Bio::Ontology::RelationshipType objects | 
|  | 368  Args    : reference to an array of Bio::Ontology::RelationshipType objects | 
|  | 369 | 
|  | 370 | 
|  | 371 =cut | 
|  | 372 | 
|  | 373 sub _relationship_type_store{ | 
|  | 374   my ($self, $value) = @_; | 
|  | 375 | 
|  | 376   if( defined $value) { | 
|  | 377     if ( defined $self->{'_relationship_type_store'}) { | 
|  | 378       $self->throw("_relationship_type_store already defined\n"); | 
|  | 379     } | 
|  | 380     else { | 
|  | 381       $self->{'_relationship_type_store'} = $value; | 
|  | 382     } | 
|  | 383   } | 
|  | 384 | 
|  | 385   return $self->{'_relationship_type_store'}; | 
|  | 386 } | 
|  | 387 | 
|  | 388 =head2 _add_relationship_simple | 
|  | 389 | 
|  | 390  Title   : _add_relationship_simple | 
|  | 391  Usage   : | 
|  | 392  Function: | 
|  | 393  Example : | 
|  | 394  Returns : | 
|  | 395  Args    : | 
|  | 396 | 
|  | 397 | 
|  | 398 =cut | 
|  | 399 | 
|  | 400 sub _add_relationship_simple{ | 
|  | 401    my ($self, $store, $rel, $inverted) = @_; | 
|  | 402    my $parent_id; | 
|  | 403    my $child_id; | 
|  | 404 | 
|  | 405    if ($inverted) { | 
|  | 406      $parent_id = $rel->subject_term->identifier; | 
|  | 407      $child_id = $rel->object_term->identifier; | 
|  | 408    } | 
|  | 409    else { | 
|  | 410      $parent_id = $rel->object_term->identifier; | 
|  | 411      $child_id = $rel->subject_term->identifier; | 
|  | 412    } | 
|  | 413    if((defined $store->{$parent_id}->{$child_id}) && | 
|  | 414       ($store->{$parent_id}->{$child_id}->name != $rel->predicate_term->name)){ | 
|  | 415        $self->throw("relationship ".Dumper($rel->predicate_term). | 
|  | 416 		    " between ".$parent_id." and ".$child_id. | 
|  | 417 		    " already defined as ". | 
|  | 418 		    Dumper($store->{$parent_id}->{$child_id})."\n"); | 
|  | 419    } | 
|  | 420    else { | 
|  | 421      $store->{$parent_id}->{$child_id} = $rel->predicate_term; | 
|  | 422    } | 
|  | 423 } | 
|  | 424 | 
|  | 425 =head2 add_relationship | 
|  | 426 | 
|  | 427  Title   : add_relationship | 
|  | 428  Usage   : add_relationship(RelationshipI relationship): RelationshipI | 
|  | 429  Function: Adds a relationship object to the ontology engine. | 
|  | 430  Example : | 
|  | 431  Returns : Its argument. | 
|  | 432  Args    : A RelationshipI object. | 
|  | 433 | 
|  | 434 | 
|  | 435 =cut | 
|  | 436 | 
|  | 437 sub add_relationship{ | 
|  | 438    my ($self, $rel) = @_; | 
|  | 439 | 
|  | 440    $self->_add_relationship_simple($self->_relationship_store, | 
|  | 441 				   $rel, 0); | 
|  | 442    $self->_add_relationship_simple($self->_inverted_relationship_store, | 
|  | 443 				   $rel, 1); | 
|  | 444    $self->_relationship_type_store->{ | 
|  | 445        $self->_unique_termid($rel->predicate_term)} = $rel->predicate_term; | 
|  | 446 | 
|  | 447    return $rel; | 
|  | 448 } | 
|  | 449 | 
|  | 450 =head2 get_relationships | 
|  | 451 | 
|  | 452  Title   : get_relationships | 
|  | 453  Usage   : get_relationships(): RelationshipI[] | 
|  | 454  Function: Retrieves all relationship objects. | 
|  | 455  Example : | 
|  | 456  Returns : Array of RelationshipI objects | 
|  | 457  Args    : | 
|  | 458 | 
|  | 459 | 
|  | 460 =cut | 
|  | 461 | 
|  | 462 sub get_relationships{ | 
|  | 463     my $self = shift; | 
|  | 464     my $term = shift; | 
|  | 465     my @rels; | 
|  | 466     my $store = $self->_relationship_store; | 
|  | 467     my $relfact = $self->relationship_factory(); | 
|  | 468 | 
|  | 469     my @parent_ids = $term ? | 
|  | 470 	# if a term is supplied then only get the term's parents | 
|  | 471 	(map { $_->identifier(); } $self->get_parent_terms($term)) : | 
|  | 472 	# otherwise use all parent ids | 
|  | 473 	(keys %{$store}); | 
|  | 474     # add the term as a parent too if one is supplied | 
|  | 475     push(@parent_ids,$term->identifier) if $term; | 
|  | 476 | 
|  | 477     foreach my $parent_id (@parent_ids) { | 
|  | 478 	my $parent_entry = $store->{$parent_id}; | 
|  | 479 | 
|  | 480 	# if a term is supplied, add a relationship for the parent to the term | 
|  | 481 	# except if the parent is the term itself (we added that one before) | 
|  | 482 	if($term && ($parent_id ne $term->identifier())) { | 
|  | 483 	    my $parent_term = $self->get_term_by_identifier($parent_id); | 
|  | 484 	    push(@rels, | 
|  | 485 		 $relfact->create_object(-object_term    => $parent_term, | 
|  | 486 					 -subject_term   => $term, | 
|  | 487 					 -predicate_term => | 
|  | 488 					    $parent_entry->{$term->identifier}, | 
|  | 489 					 -ontology       => $term->ontology() | 
|  | 490 					 ) | 
|  | 491 		 ); | 
|  | 492 | 
|  | 493 	} else { | 
|  | 494 	    # otherwise, i.e., no term supplied, or the parent equals the | 
|  | 495 	    # supplied term | 
|  | 496 	    my $parent_term = $term ? | 
|  | 497 		$term : $self->get_term_by_identifier($parent_id); | 
|  | 498 	    foreach my $child_id (keys %$parent_entry) { | 
|  | 499 		my $rel_info = $parent_entry->{$child_id}; | 
|  | 500 | 
|  | 501 		push(@rels, | 
|  | 502 		     $relfact->create_object(-object_term    => $parent_term, | 
|  | 503 					     -subject_term   => | 
|  | 504 					         $self->get_term_by_identifier( | 
|  | 505 							            $child_id), | 
|  | 506 					     -predicate_term => $rel_info, | 
|  | 507 					     -ontology =>$parent_term->ontology | 
|  | 508 					     ) | 
|  | 509 		     ); | 
|  | 510 	    } | 
|  | 511 	} | 
|  | 512     } | 
|  | 513 | 
|  | 514     return @rels; | 
|  | 515 } | 
|  | 516 | 
|  | 517 =head2 get_all_relationships | 
|  | 518 | 
|  | 519  Title   : get_all_relationships | 
|  | 520  Usage   : get_all_relationships(): RelationshipI[] | 
|  | 521  Function: Retrieves all relationship objects. | 
|  | 522  Example : | 
|  | 523  Returns : Array of RelationshipI objects | 
|  | 524  Args    : | 
|  | 525 | 
|  | 526 | 
|  | 527 =cut | 
|  | 528 | 
|  | 529 sub get_all_relationships{ | 
|  | 530     return shift->get_relationships(); | 
|  | 531 } | 
|  | 532 | 
|  | 533 =head2 get_predicate_terms | 
|  | 534 | 
|  | 535  Title   : get_predicate_terms | 
|  | 536  Usage   : get_predicate_terms(): TermI[] | 
|  | 537  Function: Retrives all relationship types stored in the engine | 
|  | 538  Example : | 
|  | 539  Returns : reference to an array of Bio::Ontology::RelationshipType objects | 
|  | 540  Args    : | 
|  | 541 | 
|  | 542 | 
|  | 543 =cut | 
|  | 544 | 
|  | 545 sub get_predicate_terms{ | 
|  | 546   my ($self) = @_; | 
|  | 547 | 
|  | 548   return values %{$self->_relationship_type_store}; | 
|  | 549 } | 
|  | 550 | 
|  | 551 =head2 _is_rel_type | 
|  | 552 | 
|  | 553  Title   : _is_rel_type | 
|  | 554  Usage   : | 
|  | 555  Function: | 
|  | 556  Example : | 
|  | 557  Returns : | 
|  | 558  Args    : | 
|  | 559 | 
|  | 560 | 
|  | 561 =cut | 
|  | 562 | 
|  | 563 sub _is_rel_type{ | 
|  | 564   my ($self, $term, @rel_types) = @_; | 
|  | 565 | 
|  | 566   foreach my $rel_type (@rel_types) { | 
|  | 567       if($rel_type->identifier || $term->identifier) { | 
|  | 568 	  return 1 if $rel_type->identifier eq $term->identifier; | 
|  | 569       } else { | 
|  | 570 	  return 1 if $rel_type->name eq $term->name; | 
|  | 571       } | 
|  | 572   } | 
|  | 573 | 
|  | 574   return 0; | 
|  | 575 } | 
|  | 576 | 
|  | 577 =head2 _typed_traversal | 
|  | 578 | 
|  | 579  Title   : _typed_traversal | 
|  | 580  Usage   : | 
|  | 581  Function: | 
|  | 582  Example : | 
|  | 583  Returns : | 
|  | 584  Args    : | 
|  | 585 | 
|  | 586 | 
|  | 587 =cut | 
|  | 588 | 
|  | 589 sub _typed_traversal{ | 
|  | 590   my ($self, $rel_store, $level, $term_id, @rel_types) = @_; | 
|  | 591   return undef if !defined($rel_store->{$term_id}); | 
|  | 592   my %parent_entry = %{$rel_store->{$term_id}}; | 
|  | 593   my @children = keys %parent_entry; | 
|  | 594 | 
|  | 595   my @ans; | 
|  | 596 | 
|  | 597   if (@rel_types > 0) { | 
|  | 598     @ans = (); | 
|  | 599 | 
|  | 600     foreach my $child_id (@children) { | 
|  | 601       push @ans, $child_id | 
|  | 602 	  if $self->_is_rel_type( $rel_store->{$term_id}->{$child_id}, | 
|  | 603 				  @rel_types); | 
|  | 604     } | 
|  | 605   } | 
|  | 606   else { | 
|  | 607     @ans = @children; | 
|  | 608   } | 
|  | 609   if ($level < 1) { | 
|  | 610     my @ans1 = (); | 
|  | 611 | 
|  | 612     foreach my $child_id (@ans) { | 
|  | 613       push @ans1, $self->_typed_traversal($rel_store, | 
|  | 614 					  $level - 1, $child_id, @rel_types) | 
|  | 615 	if defined $rel_store->{$child_id}; | 
|  | 616     } | 
|  | 617     push @ans, @ans1; | 
|  | 618   } | 
|  | 619 | 
|  | 620   return @ans; | 
|  | 621 } | 
|  | 622 | 
|  | 623 =head2 get_child_terms | 
|  | 624 | 
|  | 625  Title   : get_child_terms | 
|  | 626  Usage   : get_child_terms(TermI term, TermI[] predicate_terms): TermI[] | 
|  | 627   get_child_terms(TermI term, RelationshipType[] predicate_terms): TermI[] | 
|  | 628  Function: Retrieves all child terms of a given term, that satisfy a | 
|  | 629            relationship among those that are specified in the second | 
|  | 630            argument or undef otherwise. get_child_terms is a special | 
|  | 631            case of get_descendant_terms, limiting the search to the | 
|  | 632            direct descendants. | 
|  | 633 | 
|  | 634  Example : | 
|  | 635  Returns : Array of TermI objects. | 
|  | 636  Args    : First argument is the term of interest, second is the list of relationship type terms. | 
|  | 637 | 
|  | 638 | 
|  | 639 =cut | 
|  | 640 | 
|  | 641 sub get_child_terms{ | 
|  | 642     my ($self, $term, @relationship_types) = @_; | 
|  | 643 | 
|  | 644     $self->throw("must provide TermI compliant object") | 
|  | 645 	unless defined($term) && $term->isa("Bio::Ontology::TermI"); | 
|  | 646 | 
|  | 647     return $self->_filter_unmarked( | 
|  | 648                $self->get_term_by_identifier( | 
|  | 649 		   $self->_typed_traversal($self->_relationship_store, | 
|  | 650 					   1, | 
|  | 651 					   $term->identifier, | 
|  | 652 					   @relationship_types) ) ); | 
|  | 653 } | 
|  | 654 | 
|  | 655 =head2 get_descendant_terms | 
|  | 656 | 
|  | 657  Title   : get_descendant_terms | 
|  | 658  Usage   : get_descendant_terms(TermI term, TermI[] rel_types): TermI[] | 
|  | 659   get_child_terms(TermI term, RelationshipType[] predicate_terms): TermI[] | 
|  | 660  Function: Retrieves all descendant terms of a given term, that | 
|  | 661            satisfy a relationship among those that are specified in | 
|  | 662            the second argument or undef otherwise. Uses | 
|  | 663            _typed_traversal to find all descendants. | 
|  | 664 | 
|  | 665  Example : | 
|  | 666  Returns : Array of TermI objects. | 
|  | 667  Args    : First argument is the term of interest, second is the list of relationship type terms. | 
|  | 668 | 
|  | 669 | 
|  | 670 =cut | 
|  | 671 | 
|  | 672 sub get_descendant_terms{ | 
|  | 673   my ($self, $term, @relationship_types) = @_; | 
|  | 674 | 
|  | 675   $self->throw("must provide TermI compliant object") | 
|  | 676       unless defined($term) && $term->isa("Bio::Ontology::TermI"); | 
|  | 677 | 
|  | 678   return $self->_filter_unmarked( | 
|  | 679 	     $self->_filter_repeated( | 
|  | 680 	         $self->get_term_by_identifier( | 
|  | 681 		     $self->_typed_traversal($self->_relationship_store, | 
|  | 682 					     0, | 
|  | 683 					     $term->identifier, | 
|  | 684 					     @relationship_types) ) ) ); | 
|  | 685 } | 
|  | 686 | 
|  | 687 =head2 get_parent_terms | 
|  | 688 | 
|  | 689  Title   : get_parent_terms | 
|  | 690  Usage   : get_parent_terms(TermI term, TermI[] predicate_terms): TermI[] | 
|  | 691   get_child_terms(TermI term, RelationshipType[] predicate_terms): TermI[] | 
|  | 692  Function: Retrieves all parent terms of a given term, that satisfy a | 
|  | 693            relationship among those that are specified in the second | 
|  | 694            argument or undef otherwise. get_parent_terms is a special | 
|  | 695            case of get_ancestor_terms, limiting the search to the | 
|  | 696            direct ancestors. | 
|  | 697 | 
|  | 698  Example : | 
|  | 699  Returns : Array of TermI objects. | 
|  | 700  Args    : First argument is the term of interest, second is the list of relationship type terms. | 
|  | 701 | 
|  | 702 | 
|  | 703 =cut | 
|  | 704 | 
|  | 705 sub get_parent_terms{ | 
|  | 706   my ($self, $term, @relationship_types) = @_; | 
|  | 707   $self->throw("term must be a valid object, not undef") unless defined $term; | 
|  | 708 | 
|  | 709   return $self->_filter_unmarked( | 
|  | 710 	    $self->get_term_by_identifier( | 
|  | 711 		$self->_typed_traversal($self->_inverted_relationship_store, | 
|  | 712 					1, | 
|  | 713 					$term->identifier, | 
|  | 714 					@relationship_types) ) ); | 
|  | 715 } | 
|  | 716 | 
|  | 717 =head2 get_ancestor_terms | 
|  | 718 | 
|  | 719  Title   : get_ancestor_terms | 
|  | 720  Usage   : get_ancestor_terms(TermI term, TermI[] predicate_terms): TermI[] | 
|  | 721   get_child_terms(TermI term, RelationshipType[] predicate_terms): TermI[] | 
|  | 722  Function: Retrieves all ancestor terms of a given term, that satisfy | 
|  | 723            a relationship among those that are specified in the second | 
|  | 724            argument or undef otherwise. Uses _typed_traversal to find | 
|  | 725            all ancestors. | 
|  | 726 | 
|  | 727  Example : | 
|  | 728  Returns : Array of TermI objects. | 
|  | 729  Args    : First argument is the term of interest, second is the list | 
|  | 730            of relationship type terms. | 
|  | 731 | 
|  | 732 | 
|  | 733 =cut | 
|  | 734 | 
|  | 735 sub get_ancestor_terms{ | 
|  | 736   my ($self, $term, @relationship_types) = @_; | 
|  | 737   $self->throw("term must be a valid object, not undef") unless defined $term; | 
|  | 738 | 
|  | 739   return $self->_filter_unmarked( | 
|  | 740 	    $self->_filter_repeated( | 
|  | 741                $self->get_term_by_identifier( | 
|  | 742                   $self->_typed_traversal($self->_inverted_relationship_store, | 
|  | 743 					  0, | 
|  | 744 					  $term->identifier, | 
|  | 745 					  @relationship_types) ) ) ); | 
|  | 746 } | 
|  | 747 | 
|  | 748 =head2 get_leaf_terms | 
|  | 749 | 
|  | 750  Title   : get_leaf_terms | 
|  | 751  Usage   : get_leaf_terms(): TermI[] | 
|  | 752  Function: Retrieves all leaf terms from the ontology. Leaf term is a term w/o descendants. | 
|  | 753  Example : @leaf_terms = $obj->get_leaf_terms() | 
|  | 754  Returns : Array of TermI objects. | 
|  | 755  Args    : | 
|  | 756 | 
|  | 757 | 
|  | 758 =cut | 
|  | 759 | 
|  | 760 sub get_leaf_terms{ | 
|  | 761   my ($self) = @_; | 
|  | 762   my @leaf_terms; | 
|  | 763 | 
|  | 764   foreach my $term (values %{$self->_term_store}) { | 
|  | 765     push @leaf_terms, $term | 
|  | 766       if !defined $self->_relationship_store->{$term->identifier} && | 
|  | 767 	defined $self->_instantiated_terms_store->{$term->identifier}; | 
|  | 768   } | 
|  | 769 | 
|  | 770   return @leaf_terms; | 
|  | 771 } | 
|  | 772 | 
|  | 773 =head2 get_root_terms | 
|  | 774 | 
|  | 775  Title   : get_root_terms | 
|  | 776  Usage   : get_root_terms(): TermI[] | 
|  | 777  Function: Retrieves all root terms from the ontology. Root term is a term w/o descendants. | 
|  | 778  Example : @root_terms = $obj->get_root_terms() | 
|  | 779  Returns : Array of TermI objects. | 
|  | 780  Args    : | 
|  | 781 | 
|  | 782 | 
|  | 783 =cut | 
|  | 784 | 
|  | 785 sub get_root_terms{ | 
|  | 786   my ($self) = @_; | 
|  | 787   my @root_terms; | 
|  | 788 | 
|  | 789   foreach my $term (values %{$self->_term_store}) { | 
|  | 790     push @root_terms, $term | 
|  | 791       if !defined $self->_inverted_relationship_store->{$term->identifier} && | 
|  | 792 	defined $self->_instantiated_terms_store->{$term->identifier}; | 
|  | 793   } | 
|  | 794 | 
|  | 795   return @root_terms; | 
|  | 796 } | 
|  | 797 | 
|  | 798 =head2 _filter_repeated | 
|  | 799 | 
|  | 800  Title   : _filter_repeated | 
|  | 801  Usage   : @lst = $self->_filter_repeated(@old_lst); | 
|  | 802  Function: Removes repeated terms | 
|  | 803  Example : | 
|  | 804  Returns : List of unique TermI objects | 
|  | 805  Args    : List of TermI objects | 
|  | 806 | 
|  | 807 | 
|  | 808 =cut | 
|  | 809 | 
|  | 810 sub _filter_repeated{ | 
|  | 811   my ($self, @args) = @_; | 
|  | 812   my %h; | 
|  | 813 | 
|  | 814   foreach my $element (@args) { | 
|  | 815     $h{$element->identifier} = $element if !defined $h{$element->identifier}; | 
|  | 816   } | 
|  | 817 | 
|  | 818   return values %h; | 
|  | 819 } | 
|  | 820 | 
|  | 821 =head2 get_all_terms | 
|  | 822 | 
|  | 823  Title   : get_all_terms | 
|  | 824  Usage   : get_all_terms(): TermI[] | 
|  | 825  Function: Retrieves all terms currently stored in the ontology. | 
|  | 826  Example : @all_terms = $obj->get_all_terms() | 
|  | 827  Returns : Array of TermI objects. | 
|  | 828  Args    : | 
|  | 829 | 
|  | 830 | 
|  | 831 =cut | 
|  | 832 | 
|  | 833 sub get_all_terms{ | 
|  | 834   my ($self) = @_; | 
|  | 835 | 
|  | 836   return $self->_filter_unmarked( values %{$self->_term_store} ); | 
|  | 837 } | 
|  | 838 | 
|  | 839 =head2 find_terms | 
|  | 840 | 
|  | 841  Title   : find_terms | 
|  | 842  Usage   : ($term) = $oe->find_terms(-identifier => "SO:0000263"); | 
|  | 843  Function: Find term instances matching queries for their attributes. | 
|  | 844 | 
|  | 845            This implementation can efficiently resolve queries by | 
|  | 846            identifier. | 
|  | 847 | 
|  | 848  Example : | 
|  | 849  Returns : an array of zero or more Bio::Ontology::TermI objects | 
|  | 850  Args    : Named parameters. The following parameters should be recognized | 
|  | 851            by any implementations: | 
|  | 852 | 
|  | 853               -identifier    query by the given identifier | 
|  | 854               -name          query by the given name | 
|  | 855 | 
|  | 856 | 
|  | 857 =cut | 
|  | 858 | 
|  | 859 sub find_terms{ | 
|  | 860     my ($self,@args) = @_; | 
|  | 861     my @terms; | 
|  | 862 | 
|  | 863     my ($id,$name) = $self->_rearrange([qw(IDENTIFIER NAME)],@args); | 
|  | 864 | 
|  | 865     if(defined($id)) { | 
|  | 866 	@terms = $self->get_term_by_identifier($id); | 
|  | 867     } else { | 
|  | 868 	@terms = $self->get_all_terms(); | 
|  | 869     } | 
|  | 870     if(defined($name)) { | 
|  | 871 	@terms = grep { $_->name() eq $name; } @terms; | 
|  | 872     } | 
|  | 873     return @terms; | 
|  | 874 } | 
|  | 875 | 
|  | 876 | 
|  | 877 =head2 relationship_factory | 
|  | 878 | 
|  | 879  Title   : relationship_factory | 
|  | 880  Usage   : $fact = $obj->relationship_factory() | 
|  | 881  Function: Get/set the object factory to be used when relationship | 
|  | 882            objects are created by the implementation on-the-fly. | 
|  | 883 | 
|  | 884  Example : | 
|  | 885  Returns : value of relationship_factory (a Bio::Factory::ObjectFactoryI | 
|  | 886            compliant object) | 
|  | 887  Args    : on set, a Bio::Factory::ObjectFactoryI compliant object | 
|  | 888 | 
|  | 889 | 
|  | 890 =cut | 
|  | 891 | 
|  | 892 sub relationship_factory{ | 
|  | 893     my $self = shift; | 
|  | 894 | 
|  | 895     return $self->{'relationship_factory'} = shift if @_; | 
|  | 896     return $self->{'relationship_factory'}; | 
|  | 897 } | 
|  | 898 | 
|  | 899 =head2 term_factory | 
|  | 900 | 
|  | 901  Title   : term_factory | 
|  | 902  Usage   : $fact = $obj->term_factory() | 
|  | 903  Function: Get/set the object factory to be used when term objects are | 
|  | 904            created by the implementation on-the-fly. | 
|  | 905 | 
|  | 906            Note that this ontology engine implementation does not | 
|  | 907            create term objects on the fly, and therefore setting this | 
|  | 908            attribute is meaningless. | 
|  | 909 | 
|  | 910  Example : | 
|  | 911  Returns : value of term_factory (a Bio::Factory::ObjectFactoryI | 
|  | 912            compliant object) | 
|  | 913  Args    : on set, a Bio::Factory::ObjectFactoryI compliant object | 
|  | 914 | 
|  | 915 | 
|  | 916 =cut | 
|  | 917 | 
|  | 918 sub term_factory{ | 
|  | 919     my $self = shift; | 
|  | 920 | 
|  | 921     if(@_) { | 
|  | 922 	$self->warn("setting term factory, but ".ref($self). | 
|  | 923 		    " does not create terms on-the-fly"); | 
|  | 924 	return $self->{'term_factory'} = shift; | 
|  | 925     } | 
|  | 926     return $self->{'term_factory'}; | 
|  | 927 } | 
|  | 928 | 
|  | 929 =head2 _filter_unmarked | 
|  | 930 | 
|  | 931  Title   : _filter_unmarked | 
|  | 932  Usage   : _filter_unmarked(TermI[] terms): TermI[] | 
|  | 933  Function: Removes the uninstantiated terms from the list of terms | 
|  | 934  Example : | 
|  | 935  Returns : array of fully instantiated TermI objects | 
|  | 936  Args    : array of TermI objects | 
|  | 937 | 
|  | 938 | 
|  | 939 =cut | 
|  | 940 | 
|  | 941 sub _filter_unmarked{ | 
|  | 942   my ($self, @terms) = @_; | 
|  | 943   my @filtered_terms = (); | 
|  | 944 | 
|  | 945   if ( scalar(@terms) >= 1) { | 
|  | 946     foreach my $term (@terms) { | 
|  | 947       push @filtered_terms, $term | 
|  | 948 	if defined $self->_instantiated_terms_store->{$term->identifier}; | 
|  | 949     } | 
|  | 950   } | 
|  | 951 | 
|  | 952   return @filtered_terms; | 
|  | 953 } | 
|  | 954 | 
|  | 955 =head2 remove_term_by_id | 
|  | 956 | 
|  | 957  Title   : remove_term_by_id | 
|  | 958  Usage   : remove_term_by_id(String id): TermI | 
|  | 959  Function: Removes TermI object from the ontology engine using the | 
|  | 960            string id as an identifier. Current implementation does not | 
|  | 961            enforce consistency of the relationships using that term. | 
|  | 962  Example : $term = $soe->remove_term_by_id($id); | 
|  | 963  Returns : Object of class TermI or undef if not found. | 
|  | 964  Args    : The string identifier of a term. | 
|  | 965 | 
|  | 966 | 
|  | 967 =cut | 
|  | 968 | 
|  | 969 sub remove_term_by_id{ | 
|  | 970   my ($self, $id) = @_; | 
|  | 971 | 
|  | 972   if ( $self->get_term_by_identifier($id) ) { | 
|  | 973     my $term = $self->{_term_store}->{$id}; | 
|  | 974     delete $self->{_term_store}->{$id}; | 
|  | 975     return $term; | 
|  | 976   } | 
|  | 977   else { | 
|  | 978     $self->warn("Term with id '$id' is not in the term store"); | 
|  | 979     return undef; | 
|  | 980   } | 
|  | 981 } | 
|  | 982 | 
|  | 983 =head2 to_string | 
|  | 984 | 
|  | 985  Title   : to_string | 
|  | 986  Usage   : print $sv->to_string(); | 
|  | 987  Function: Currently returns formatted string containing the number of | 
|  | 988            terms and number of relationships from the ontology engine. | 
|  | 989  Example : print $sv->to_string(); | 
|  | 990  Returns : | 
|  | 991  Args    : | 
|  | 992 | 
|  | 993 | 
|  | 994 =cut | 
|  | 995 | 
|  | 996 sub to_string{ | 
|  | 997   my ($self) = @_; | 
|  | 998   my $s = ""; | 
|  | 999 | 
|  | 1000   $s .= "-- # Terms:\n"; | 
|  | 1001   $s .= scalar($self->get_all_terms)."\n"; | 
|  | 1002   $s .= "-- # Relationships:\n"; | 
|  | 1003   $s .= $self->_get_number_rels."\n"; | 
|  | 1004 | 
|  | 1005   return $s; | 
|  | 1006 } | 
|  | 1007 | 
|  | 1008 =head2 _unique_termid | 
|  | 1009 | 
|  | 1010  Title   : _unique_termid | 
|  | 1011  Usage   : | 
|  | 1012  Function: Returns a string that can be used as ID using fail-over | 
|  | 1013            approaches. | 
|  | 1014 | 
|  | 1015            If the identifier attribute is not set, it uses the | 
|  | 1016            combination of name and ontology name, provided both are | 
|  | 1017            set. If they aren't, it returns the name alone. | 
|  | 1018 | 
|  | 1019            Note that this is a private method. Call from inheriting | 
|  | 1020            classes but not from outside. | 
|  | 1021 | 
|  | 1022  Example : | 
|  | 1023  Returns : a string | 
|  | 1024  Args    : a Bio::Ontology::TermI compliant object | 
|  | 1025 | 
|  | 1026 | 
|  | 1027 =cut | 
|  | 1028 | 
|  | 1029 sub _unique_termid{ | 
|  | 1030     my $self = shift; | 
|  | 1031     my $term = shift; | 
|  | 1032 | 
|  | 1033     return $term->identifier() if $term->identifier(); | 
|  | 1034     my $id = $term->ontology->name() if $term->ontology(); | 
|  | 1035     if($id) { | 
|  | 1036 	$id .= '|'; | 
|  | 1037     } else { | 
|  | 1038 	$id = ''; | 
|  | 1039     } | 
|  | 1040     $id .= $term->name(); | 
|  | 1041 } | 
|  | 1042 | 
|  | 1043 | 
|  | 1044 ################################################################# | 
|  | 1045 # aliases | 
|  | 1046 ################################################################# | 
|  | 1047 | 
|  | 1048 *get_relationship_types = \&get_predicate_terms; | 
|  | 1049 | 
|  | 1050 1; |