File Coverage

Bio/OntologyIO/obo.pm
Criterion Covered Total %
statement 274 301 91.0
branch 91 128 71.0
condition 17 24 70.8
subroutine 37 37 100.0
pod 4 4 100.0
total 423 494 85.6


line stmt bran cond sub pod time code
1             #
2             # BioPerl module for Bio::OntologyIO::obo
3             #
4             # Please direct questions and support issues to
5             #
6             # Cared for by Sohel Merchant, s-merchant at northwestern.edu
7             #
8             # Copyright Sohel Merchant
9             #
10             # You may distribute this module under the same terms as perl itself
11              
12              
13             =head1 NAME
14              
15             Bio::OntologyIO::obo
16              
17             =head1 SYNOPSIS
18              
19             use Bio::OntologyIO;
20              
21             # do not use directly -- use via Bio::OntologyIO
22             my $parser = Bio::OntologyIO->new
23             ( -format => "obo",
24             -file => "gene_ontology.obo");
25              
26             while(my $ont = $parser->next_ontology()) {
27             print "read ontology ",$ont->name()," with ",
28             scalar($ont->get_root_terms), " root terms, and ",
29             scalar($ont->get_all_terms), " total terms, and ",
30             scalar($ont->get_leaf_terms), " leaf terms\n";
31             }
32              
33             =head1 DESCRIPTION
34              
35             Parser for OBO flat-file format. 'obo' example:
36              
37             format-version: 1.2
38             ontology: so/dev/externalDerived
39             property_value: owl:versionInfo "$Revision: 80 $" xsd:string
40             default-namespace: SO
41              
42             [Term]
43             id: SO_0000343
44             name: match
45             def: "A region of sequence, aligned to another sequence." []
46              
47             [Term]
48             id: SO_0000039
49             name: match_part
50             def: "A part of a match." []
51             is_a: SO_0000343
52              
53             Specification: L.
54              
55             =head1 FEEDBACK
56              
57             =head2 Mailing Lists
58              
59             User feedback is an integral part of the evolution of this and other
60             Bioperl modules. Send your comments and suggestions preferably to the
61             Bioperl mailing lists Your participation is much appreciated.
62              
63             bioperl-l@bioperl.org - General discussion
64             http://bioperl.org/wiki/Mailing_lists - About the mailing lists
65              
66             =head2 Support
67              
68             Please direct usage questions or support issues to the mailing list:
69              
70             I
71              
72             rather than to the module maintainer directly. Many experienced and
73             reponsive experts will be able look at the problem and quickly
74             address it. Please include a thorough description of the problem
75             with code and data examples if at all possible.
76              
77             =head2 Reporting Bugs
78              
79             Report bugs to the Bioperl bug tracking system to help us keep track
80             the bugs and their resolution. Bug reports can be submitted via the
81             web:
82              
83             https://github.com/bioperl/bioperl-live/issues
84              
85             =head1 AUTHOR
86              
87             Sohel Merchant
88              
89             Email: s-merchant@northwestern.edu
90              
91             Address:
92              
93             Northwestern University
94             Center for Genetic Medicine (CGM), dictyBase
95             Suite 1206,
96             676 St. Clair st
97             Chicago IL 60611
98              
99             =head2 CONTRIBUTOR
100              
101             Hilmar Lapp, hlapp at gmx.net
102             Chris Mungall, cjm at fruitfly.org
103             Brian Osborne, briano@bioteam.net
104              
105             =head1 APPENDIX
106              
107             The rest of the documentation details each of the object
108             methods. Internal methods are usually preceded with a _
109              
110             =cut
111              
112             package Bio::OntologyIO::obo;
113              
114 1     1   4 use strict;
  1         1  
  1         24  
115              
116 1     1   2 use Bio::Root::IO;
  1         1  
  1         18  
117 1     1   396 use Bio::Ontology::OBOEngine;
  1         2  
  1         20  
118 1     1   4 use Bio::Ontology::Ontology;
  1         1  
  1         19  
119 1     1   3 use Bio::Ontology::OntologyStore;
  1         1  
  1         16  
120 1     1   404 use Bio::Ontology::TermFactory;
  1         1  
  1         20  
121 1     1   389 use Bio::Annotation::Collection;
  1         2  
  1         24  
122 1     1   550 use Text::Balanced qw(extract_quotelike extract_bracketed);
  1         6806  
  1         68  
123              
124 1     1   5 use constant TRUE => 1;
  1         1  
  1         50  
125 1     1   3 use constant FALSE => 0;
  1         2  
  1         34  
126              
127 1     1   3 use base qw(Bio::OntologyIO);
  1         1  
  1         3199  
128              
129             =head2 new
130              
131             Title : new
132             Usage : $parser = Bio::OntologyIO->new(
133             -format => "obo",
134             -file => "gene_ontology.obo");
135             Function: Creates a new dagflat parser.
136             Returns : A new dagflat parser object, implementing Bio::OntologyIO.
137             Args : -file => a single ontology flat file holding the
138             terms, descriptions and relationships
139             -ontology_name => the name of the ontology; if not specified the
140             parser will assign the name of the ontology as the
141             default-namespace header value from the OBO file.
142             -engine => the Bio::Ontology::OntologyEngineI object
143             to be reused (will be created otherwise); note
144             that every Bio::Ontology::OntologyI will
145             qualify as well since that one inherits from the
146             former.
147              
148             See L.
149              
150             =cut
151              
152             # Let OntologyIO::new() do the instantiation, and override
153             # _initialize for all initialization work
154             sub _initialize {
155 3     3   9 my ( $self, %arg ) = @_;
156              
157 3         16 my ( $file, $name, $eng ) = $self->_rearrange(
158             [ qw( FILE ONTOLOGY_NAME ENGINE) ], %arg
159             );
160              
161 3         15 $self->SUPER::_initialize(%arg);
162 3         5 delete $self->{'_ontologies'};
163              
164             # Ontology engine (and possibly name if it's an OntologyI)
165 3 50       23 $eng = Bio::Ontology::OBOEngine->new() unless $eng;
166 3 50       22 if ( $eng->isa("Bio::Ontology::OntologyI") ) {
167 0         0 $self->ontology_name( $eng->name() );
168 0 0       0 $eng = $eng->engine() if $eng->can('engine');
169             }
170 3         6 $self->_ont_engine($eng);
171              
172 3 50       11 $self->ontology_name($name) if $name;
173             }
174              
175             =head2 ontology_name
176              
177             Title : ontology_name
178             Usage : $obj->ontology_name($newval)
179             Function: Get/set the name of the ontology parsed by this module.
180             Example :
181             Returns : value of ontology_name (a scalar)
182             Args : on set, new value (a scalar or undef, optional)
183              
184             =cut
185              
186             sub ontology_name {
187 6     6 1 7 my $self = shift;
188              
189 6 100       17 return $self->{'ontology_name'} = shift if @_;
190 3         9 return $self->{'ontology_name'};
191             }
192              
193             =head2 parse
194              
195             Title : parse()
196             Usage : $parser->parse();
197             Function: Parses the files set with "new" or with methods
198             defs_file and _flat_files.
199              
200             Normally you should not need to call this method as it will
201             be called automatically upon the first call to
202             next_ontology().
203              
204             Returns : Bio::Ontology::OntologyEngineI
205             Args :
206              
207             =cut
208              
209             sub parse {
210 3     3 1 3 my $self = shift;
211              
212             # Setup the default term factory if not done by anyone yet
213 3 50       24 $self->term_factory(Bio::Ontology::TermFactory->new( -type => "Bio::Ontology::OBOterm" ) )
214             unless $self->term_factory();
215              
216             # Parse the file header
217 3         9 my $annotations_collection = $self->_header();
218              
219             # Create the default ontology object itself
220 3         6 my $ont = Bio::Ontology::Ontology->new(
221             -name => $self->ontology_name(),
222             -engine => $self->_ont_engine()
223             );
224              
225             # Assign the file headers
226 3         9 $ont->annotation($annotations_collection);
227              
228             # Set up the ontology of the relationship types
229 3         9 for (
230             $self->_part_of_relationship(),
231             $self->_is_a_relationship(),
232             $self->_related_to_relationship(),
233             $self->_regulates_relationship(),
234             $self->_positively_regulates_relationship(),
235             $self->_negatively_regulates_relationship(),
236             )
237             {
238 18         21 $_->ontology($ont);
239             }
240              
241 3         9 $self->_add_ontology($ont);
242              
243             # Adding new terms
244 3         7 while ( my $term = $self->_next_term() ) {
245              
246             # Check if the terms has a valid ID and NAME otherwise ignore the term
247 1021 50 33     1462 if ( !$term->identifier() || !$term->name() ) {
248             $self->throw( "OBO File Format Error on line "
249 0         0 . $self->{'_current_line_no'}
250             . "\nThe term does not have a id/name tag. This term will be ignored."
251             );
252 0         0 next;
253             }
254              
255 1021         887 my $new_ontology_flag = 1;
256 1021         875 my $ontologies_array_ref = $self->{'_ontologies'};
257              
258 1021         1136 for my $ontology ( @$ontologies_array_ref ) {
259 1037         1721 my ($oname, $t_ns) = ( $ontology->name, $term->namespace );
260 1037 100 66     3715 next unless ( defined($oname) && defined($t_ns) );
261 187 100       267 if ( $oname eq $t_ns ) {
262             # No need to create new ontology
263 169         120 $new_ontology_flag = 0;
264 169         217 $ont = $ontology;
265             }
266             }
267              
268 1021 100 100     2053 if ( $new_ontology_flag && $term->namespace ) {
269 2         6 my $new_ont = Bio::Ontology::Ontology->new(
270             -name => $term->namespace,
271             -engine => $self->_ont_engine
272             );
273 2         6 $new_ont->annotation($annotations_collection);
274 2         4 $self->_add_ontology($new_ont);
275 2         2 $ont = $new_ont;
276             }
277              
278 1021         1412 $self->_add_term( $term, $ont );
279              
280             # Adding the IS_A relationship
281 1021         769 for my $parent_term ( @{$self->{'_isa_parents'}} ) {
  1021         1511  
282             # Check if parent exists, if not then add the term to the graph.
283 990 100       1315 if ( ! $self->_has_term($parent_term) ) {
284 97         117 $self->_add_term( $parent_term, $ont ); # !
285             }
286              
287 990         1605 $self->_add_relationship( $parent_term, $term,
288             $self->_is_a_relationship(), $ont );
289             }
290              
291             # Adding the other relationships like part_of, related_to, develops_from
292 1021         814 for my $relationship ( keys %{$self->{'_relationships'}} ) {
  1021         3227  
293 139         118 my $reltype;
294             # Check if relationship exists, if not add it
295 139 100       189 if ( $self->_ont_engine->get_relationship_type($relationship) ) {
296 130         145 $reltype = $self->_ont_engine->get_relationship_type($relationship);
297             }
298             else {
299 9         16 $self->_ont_engine->add_relationship_type( $relationship, $ont );
300 9         16 $reltype = $self->_ont_engine->get_relationship_type($relationship);
301             }
302              
303             # Check if the id already exists in the graph
304 139         132 for my $id ( @{$self->{'_relationships'}->{$relationship}} ) {
  139         186  
305 241         361 my $parent_term = $self->_create_term_object();
306 241         387 $parent_term->identifier($id);
307 241         315 $parent_term->ontology($ont);
308              
309 241 100       331 if ( ! $self->_has_term($parent_term) ) {
310 66         99 $self->_add_term( $parent_term, $ont );
311             }
312              
313 241         407 $self->_add_relationship( $parent_term, $term, $reltype, $ont );
314             }
315             }
316             }
317              
318 3         5 return $self->_ont_engine();
319             }
320              
321             =head2 next_ontology
322              
323             Title : next_ontology
324             Usage :
325             Function: Get the next available ontology from the parser. This is the
326             method prescribed by Bio::OntologyIO.
327             Example :
328             Returns : An object implementing Bio::Ontology::OntologyI, and nothing if
329             there is no more ontology in the input.
330             Args :
331              
332             =cut
333              
334             sub next_ontology {
335 6     6 1 711 my $self = shift;
336              
337             # Parse if not done already
338 6 100       20 $self->parse() unless exists( $self->{'_ontologies'} );
339              
340             # Return next available ontology
341 6 50       11 if ( exists( $self->{'_ontologies'} ) ) {
342 6         6 my $ont = shift( @{ $self->{'_ontologies'} } );
  6         11  
343 6 100       11 if ($ont) {
344 5         31 my $store = Bio::Ontology::OntologyStore->new();
345 5         15 $store->register_ontology($ont);
346              
347 5         10 return $ont;
348             }
349             }
350 1         2 return;
351             }
352              
353             =head2 close
354              
355             Title : close
356             Usage :
357             Function: Closes this ontology stream and associated file handles.
358              
359             Clients should call this method especially when they write
360             ontologies.
361              
362             We need to override this here in order to close the file
363             handle for the term definitions file.
364              
365             Example :
366             Returns : none
367             Args : none
368              
369             =cut
370              
371             sub close {
372 3     3 1 3 my $self = shift;
373              
374             # first call the inherited implementation
375 3         12 $self->SUPER::close();
376             }
377              
378             # INTERNAL METHODS
379              
380             sub _add_ontology {
381 5     5   5 my $self = shift;
382 5 100       11 $self->{'_ontologies'} = [] unless exists( $self->{'_ontologies'} );
383 5         9 for my $ont (@_) {
384 5 50 33     21 $self->throw( ref($ont) . " does not implement Bio::Ontology::OntologyI" )
385             unless ref($ont) && $ont->isa("Bio::Ontology::OntologyI");
386             # The ontology name may have been auto-discovered while parsing
387             # the file
388 5 50       8 $ont->name( $self->ontology_name ) unless $ont->name();
389 5         3 push( @{ $self->{'_ontologies'} }, $ont );
  5         10  
390             }
391             }
392              
393             # This simply delegates. See Ontology::OBOEngine::add_term.
394             sub _add_term {
395 1184     1184   1047 my ( $self, $term, $ont ) = @_;
396 1184 100 66     2193 $term->ontology($ont) if $ont && ( !$term->ontology );
397 1184         1665 $self->_ont_engine()->add_term($term);
398             }
399              
400             # This simply delegates. See OBOEngine
401             sub _part_of_relationship {
402 3     3   3 my $self = shift;
403              
404 3         6 return $self->_ont_engine()->part_of_relationship(@_);
405             }
406              
407             # This simply delegates. See OBOEngine
408             sub _is_a_relationship {
409 993     993   754 my $self = shift;
410              
411 993         1036 return $self->_ont_engine()->is_a_relationship(@_);
412             }
413              
414             # This simply delegates. See OBOEngine
415             sub _related_to_relationship {
416 3     3   3 my $self = shift;
417              
418 3         3 return $self->_ont_engine()->related_to_relationship(@_);
419             }
420              
421             # This simply delegates. See OBOEngine
422             sub _regulates_relationship {
423 3     3   4 my $self = shift;
424              
425 3         6 return $self->_ont_engine()->regulates_relationship(@_);
426             }
427              
428             # This simply delegates. See OBOEngine
429             sub _positively_regulates_relationship {
430 3     3   4 my $self = shift;
431              
432 3         4 return $self->_ont_engine()->positively_regulates_relationship(@_);
433             }
434              
435             # This simply delegates. See OBOEngine
436             sub _negatively_regulates_relationship {
437 3     3   3 my $self = shift;
438              
439 3         6 return $self->_ont_engine()->negatively_regulates_relationship(@_);
440             }
441              
442             # This simply delegates. See OBOEngine
443             sub _add_relationship {
444 1231     1231   1062 my ( $self, $parent, $child, $type, $ont ) = @_;
445             # Note the triple terminology (subject,predicate,object) corresponds to
446             # (child,type,parent)
447 1231         1184 $self->_ont_engine()->add_relationship( $child, $type, $parent, $ont );
448             }
449              
450             # This simply delegates. See OBOEngine
451             sub _has_term {
452 2252     2252   1828 my $self = shift;
453              
454 2252         2535 return $self->_ont_engine()->has_term(@_);
455             }
456              
457             # Holds the OBO engine to be parsed into
458             sub _ont_engine {
459 6136     6136   4382 my ( $self, $value ) = @_;
460              
461 6136 100       7371 if ( defined $value ) {
462 3         5 $self->{"_ont_engine"} = $value;
463             }
464              
465 6136         11052 $self->{"_ont_engine"};
466             }
467              
468             # Removes the escape chracters from the file
469             sub _filter_line {
470 6614     6614   5721 my ( $self, $line ) = @_;
471              
472 6614         6427 chomp($line);
473 6614         5333 $line =~ tr [\200-\377] [\000-\177];
474             # see 'man perlop', section on tr/
475             # weird ascii characters should be excluded
476 6614         6374 $line =~ tr/\0-\10//d; # remove weird characters; ascii 0-8
477             # preserve \11 (9 - tab) and \12 (10-linefeed)
478 6614         5262 $line =~ tr/\13\14//d; # remove weird characters; 11,12
479             # preserve \15 (13 - carriage return)
480 6614         4522 $line =~ tr/\16-\37//d; # remove 14-31 (all rest before space)
481 6614         4404 $line =~ tr/\177//d; # remove DEL character
482              
483 6614         4925 $line =~ s/^\!.*//;
484 6614         7314 $line =~ s/[^\\]\!.*//;
485 6614         5859 $line =~ s/[^\\]\#.*//;
486 6614         7893 $line =~ s/^\s+//;
487 6614         8203 $line =~ s/\s+$//;
488              
489 6614         6978 return $line;
490             }
491              
492             # Parses the header
493             sub _header {
494 3     3   5 my $self = shift;
495 3         19 my $annotation_collection = Bio::Annotation::Collection->new();
496 3         5 my ( $tag, $value );
497 3         2 my $line_counter = 0;
498 3         8 $self->{'_current_line_no'} = 0;
499 3         3 my $format_version_header_flag = 0;
500 3         3 my $default_namespace_header_flag = 0;
501              
502 3         12 while ( my $line = $self->_readline() ) {
503 24         20 ++$line_counter;
504 24         30 my $line = $self->_filter_line($line);
505              
506 24 100       35 if ( !$line ) {
507 3 50       5 if ( !$format_version_header_flag ) {
508 0         0 $self->throw("Format Error - Cannot find tag format-version." .
509             "This is required in header" );
510             }
511              
512 3         5 $self->{'_current_line_no'} = $line_counter;
513 3         5 return $annotation_collection;
514             }
515              
516             # Check if there is a header
517 21 50       28 if ( $line =~ /\[\w*\]/ ) {
518 0         0 $self->throw("Format Error - Cannot find tag format-version." .
519             "This is required in header." );
520             }
521              
522             # If the line is not null, check it contains at least one colon
523 21         31 $self->_check_colon( $line, $line_counter );
524              
525             # These are the allowed headers. Any other headers will be ignored
526 21 50       52 if ( $line =~ /^(\[|format-version:
527             |data-version:
528             |typeref:
529             |version:
530             |date:
531             |saved-by:
532             |auto-generated-by:
533             |default-namespace:
534             |remark:
535             |subsetdef:
536             |import:
537             |synonymtypedef:
538             |idspace:
539             |default-relationship-id-prefix:
540             |id-mapping:
541             )/x
542             )
543             {
544 21 50       53 if ( $line =~ /^([\w\-]+)\:\s*(.*)/ ) {
545 21         45 ( $tag, $value ) = ( $1, $2 );
546             }
547              
548 21 100       46 if ( $tag =~ /format-version/) {
    100          
549 3         4 $format_version_header_flag = 1;
550             }elsif( $tag =~ /default-namespace/ ) {
551 3         4 $default_namespace_header_flag = 1;
552             }
553              
554 21         56 my $header = Bio::Annotation::SimpleValue->new( -value => $value );
555 21         42 $annotation_collection->add_Annotation( $tag, $header );
556              
557             # Assign the Ontology name as the value of the default-namespace header
558 21 100       63 if ( $tag =~ /default-namespace/i ) {
559 3         7 $self->ontology_name($value);
560             }
561             }
562             }
563             }
564              
565             # Parses each stanza of the file
566             sub _next_term {
567 1024     1024   803 my $self = shift;
568 1024         621 my $term;
569 1024         783 my $skip_stanza_flag = 1;
570 1024         864 my $line_counter = $self->{'_current_line_no'};
571              
572 1024         1750 while ( my $line = $self->_readline() ) {
573 6590         4205 ++$line_counter;
574 6590         7615 my $line = $self->_filter_line($line);
575              
576 6590 100 100     13468 if ( !$line && $term ) {
577 1021         934 $self->{'_current_line_no'} = $line_counter;
578 1021         2092 return $term;
579             }
580              
581 5569 100       10549 if ( ( $line =~ /^\[(\w+)\]\s*(.*)/ ) ) { # New stanza
582 1039 100       2607 if ( uc($1) eq "TERM" ) {
    50          
583 1021         1386 $term = $self->_create_term_object;
584 1021         730 $skip_stanza_flag = 0;
585              
586             # Reset the relationships after each stanza
587 1021         1052 $self->{'_relationships'} = {};
588 1021         1313 $self->{'_isa_parents'} = undef;
589             }
590             elsif ( uc($1) eq "TYPEDEF" ) {
591 18         18 $skip_stanza_flag = 1;
592             # Check if this typedef is already defined by the relationship
593             }
594             else {
595 0         0 $skip_stanza_flag = 1;
596 0         0 $self->warn("OBO File Format Warning on line $line_counter $line\n"
597             . "Unrecognized stanza type found. Skipping this stanza." );
598             }
599 1039         2347 next;
600             }
601              
602             # If the line is not null, check it contains at least one colon
603 4530         5613 $self->_check_colon( $line, $line_counter );
604              
605             # If there is any tag value other than the list below move to the next tag
606 4530 100 100     15965 next if (( $line !~ /^(\[|id:
607             |is_anonymous:
608             |name:
609             |namespace:
610             |alt_id:
611             |def:
612             |comment:
613             |subset:
614             |synonym:
615             |xref:
616             |is_a:
617             |intersection_of:
618             |union_of:
619             |disjoint_from:
620             |relationship:
621             |is_obsolete:
622             |replaced_by:
623             |consider:
624             |created_by:
625             |creation_date:
626             )/x
627             ) || $skip_stanza_flag );
628              
629             # Tag/value pair
630 4444 50       10785 if ( $line =~ /^([\w\-]+)\:\s*(.*)/ ) {
631 4444         8535 my ( $tag, $val ) = ( $1, $2 );
632              
633             # If no value for the tag throw a warning
634 4444 50       5512 if ( !$val ) {
635 0         0 $self->warn("OBO File Format Warning on line $line_counter $line\n" .
636             "Tag has no value."
637             );
638             }
639              
640 4444         2800 my $qh;
641 4444         5193 ( $val, $qh ) = $self->_extract_quals($val);
642 4444         4068 my $val2 = $val;
643 4444         3431 $val2 =~ s/\\,/,/g;
644 4444         3982 $tag = uc($tag);
645 4444 100       14951 if ( $tag eq "ID" ) {
    100          
    50          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
646 1021         1746 $term->identifier($val);
647 1021 100       1423 if ( $self->_has_term($term) ) {
648 163         197 $term = $self->_ont_engine()->get_terms($val);
649             }
650             }
651             elsif ( $tag eq "NAME" ) {
652 1021         1933 $term->name($val);
653             }
654             elsif ( $tag eq "XREF_ANALOG" ) {
655 0 0       0 if ( !$term->has_dbxref($val) ) {
656 0         0 $term->add_dbxref(-dbxrefs => $self->_to_annotation( [$val] ) );
657             }
658             }
659             elsif ( $tag eq "XREF_UNKNOWN" ) {
660 0         0 $term->add_dbxref(-dbxrefs => $self->_to_annotation( [$val] ) );
661             }
662             elsif ( $tag eq "NAMESPACE" ) {
663 10         21 $term->namespace($val);
664             }
665             elsif ( $tag eq "DEF" ) {
666 575         767 my ( $defstr, $parts ) = $self->_extract_qstr($val);
667 575         1294 $term->definition($defstr);
668 575         731 my $ann = $self->_to_annotation($parts);
669 575         1314 $term->add_dbxref( -dbxrefs => $ann );
670             }
671             elsif ( $tag eq "SYNONYM" ) {
672 293         478 $term->add_synonym($val);
673             }
674             elsif ( $tag eq "ALT_ID" ) {
675 1         11 $term->add_secondary_id($val);
676             }
677             elsif ( $tag =~ /XREF/i ) {
678 6         14 $term->add_secondary_id($val);
679             }
680             elsif ( $tag eq "IS_OBSOLETE" ) {
681 19 50       34 if ( $val eq 'true' ) {
    0          
682 19         19 $val = 1;
683             }
684             elsif ( $val eq 'false' ) {
685 0         0 $val = 0;
686             }
687 19         48 $term->is_obsolete($val);
688             }
689             elsif ( $tag eq "COMMENT" ) {
690 44         86 $term->comment($val);
691             }
692             elsif ( $tag eq "RELATIONSHIP" ) {
693 241         326 $self->_handle_relationship_tag($val);
694             }
695             elsif ( $tag eq "IS_A" ) {
696 990         850 $val =~ s/ //g;
697 990         1117 my $parent_term = $self->_create_term_object();
698 990         1538 $parent_term->identifier($val);
699 990         762 push @{ $self->{'_isa_parents'} }, $parent_term;
  990         3768  
700             }
701             }
702             }
703              
704 3         8 $term;
705             }
706              
707              
708             # Creates a Bio::Ontology::OBOterm object
709             sub _create_term_object {
710 2252     2252   2016 my ($self) = @_;
711 2252         3644 my $term = $self->term_factory->create_object();
712 2252         2534 $term;
713             }
714              
715             sub _extract_quals {
716 4444     4444   3415 my ( $self, $str ) = @_;
717              
718 4444         4047 my %q = ();
719 4444 50       5793 if ( $str =~ /(.*)\s+(\{.*\})\s*$/ ) {
720 0         0 my $return_str = $1;
721 0         0 my $extr = $2;
722 0 0       0 if ($extr) {
723 0         0 my @qparts = $self->_split_on_comma($extr);
724 0         0 foreach (@qparts) {
725 0 0       0 if (/(\w+)=\"(.*)\"/) {
    0          
726 0         0 $q{$1} = $2;
727             }
728             elsif (/(\w+)=\'(.*)\'/) {
729 0         0 $q{$1} = $2;
730             }
731             else {
732 0         0 warn("$_ in $str");
733             }
734             }
735             }
736 0         0 return ( $return_str, \%q );
737             }
738             else {
739 4444         8494 return ( $str, {} );
740             }
741             }
742              
743             sub _extract_qstr {
744 575     575   470 my ( $self, $str ) = @_;
745              
746 575         1074 my ( $extr, $rem, $prefix ) = extract_quotelike($str);
747 575         32010 my $txt = $extr;
748 575         1410 $txt =~ s/^\"//;
749 575         1192 $txt =~ s/\"$//;
750 575 50       871 if ($prefix) {
751 0         0 warn("illegal prefix: $prefix in: $str");
752             }
753              
754 575         486 my @extra = ();
755              
756             # e.g. synonym: "foo" EXACT [...]
757 575 50       935 if ( $rem =~ /(\w+)\s+(\[.*)/ ) {
758 0         0 $rem = $2;
759 0         0 push( @extra, split( ' ', $1 ) );
760             }
761              
762 575         473 my @parts = ();
763 575         1159 while ( ( $extr, $rem, $prefix ) = extract_bracketed( $rem, '[]' ) ) {
764 1054 100       70300 last unless $extr;
765 479         1063 $extr =~ s/^\[//;
766 479         849 $extr =~ s/\]$//;
767 479 50       1390 push( @parts, $extr ) if $extr;
768             }
769             @parts =
770 575         591 map { $self->_split_on_comma($_) } @parts;
  479         729  
771              
772 575         585 $txt =~ s/\\//g;
773              
774 575         1200 ( $txt, \@parts, \@extra );
775             }
776              
777             sub _split_on_comma {
778 479     479   468 my ( $self, $str ) = @_;
779 479         427 my @parts = ();
780 479         891 while ( $str =~ /(.*[^\\],\s*)(.*)/ ) {
781 5         10 $str = $1;
782 5         8 my $part = $2;
783 5         7 unshift( @parts, $part );
784 5         18 $str =~ s/,\s*$//;
785             }
786 479         660 unshift( @parts, $str );
787              
788 479         387 return map { s/\\//g; $_ } @parts;
  484         405  
  484         1527  
789             }
790              
791             # This method checks for an existing colon in a line
792             sub _check_colon {
793 4551     4551   4129 my ( $self, $line, $line_no ) = @_;
794 4551 50 66     14790 if ( $line && !( $line =~ /:/ ) ) {
795 0         0 $self->throw("OBO File Format Error on line $line_no $line\n" .
796             "Cannot find key-terminating colon"
797             );
798             }
799             }
800              
801             # This method handles relationship tags
802             sub _handle_relationship_tag {
803 241     241   202 my ( $self, $val ) = @_;
804 241         451 my @parts = split( / /, $val );
805 241         218 my $relationship = uc($parts[0]);
806 241 50       321 my $id = $parts[1] =~ /\^(w+)\s+\!/ ? $1 : $parts[1];
807 241         152 push @{$self->{_relationships}->{$relationship}}, $id;
  241         987  
808             }
809              
810             # Convert simple strings to Bio::Annotation::DBLinks
811             sub _to_annotation {
812 575     575   490 my ($self , $links) = @_;
813 575 50       757 return unless $links;
814 575         366 my @dbxrefs;
815 575         352 for my $string (@{$links}) {
  575         818  
816 484         1306 my ($db, $id) = split(':',$string,2);
817 484         1613 push @dbxrefs, Bio::Annotation::DBLink->new(-database => $db, -primary_id => $id);
818             }
819              
820 575         753 \@dbxrefs;
821             }
822              
823             1;