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 - parser for OBO flat-file format
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   5 use strict;
  1         2  
  1         24  
115              
116 1     1   5 use Bio::Root::IO;
  1         1  
  1         19  
117 1     1   354 use Bio::Ontology::OBOEngine;
  1         2  
  1         21  
118 1     1   6 use Bio::Ontology::Ontology;
  1         2  
  1         21  
119 1     1   4 use Bio::Ontology::OntologyStore;
  1         2  
  1         17  
120 1     1   321 use Bio::Ontology::TermFactory;
  1         3  
  1         21  
121 1     1   358 use Bio::Annotation::Collection;
  1         2  
  1         25  
122 1     1   415 use Text::Balanced qw(extract_quotelike extract_bracketed);
  1         6953  
  1         73  
123              
124 1     1   6 use constant TRUE => 1;
  1         2  
  1         48  
125 1     1   5 use constant FALSE => 0;
  1         1  
  1         36  
126              
127 1     1   4 use base qw(Bio::OntologyIO);
  1         2  
  1         2649  
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   10 my ( $self, %arg ) = @_;
156              
157 3         17 my ( $file, $name, $eng ) = $self->_rearrange(
158             [ qw( FILE ONTOLOGY_NAME ENGINE) ], %arg
159             );
160              
161 3         16 $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       24 $eng = Bio::Ontology::OBOEngine->new() unless $eng;
166 3 50       21 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         11 $self->_ont_engine($eng);
171              
172 3 50       10 $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 8 my $self = shift;
188              
189 6 100       18 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 5 my $self = shift;
211              
212             # Setup the default term factory if not done by anyone yet
213 3 50       26 $self->term_factory(Bio::Ontology::TermFactory->new( -type => "Bio::Ontology::OBOterm" ) )
214             unless $self->term_factory();
215              
216             # Parse the file header
217 3         11 my $annotations_collection = $self->_header();
218              
219             # Create the default ontology object itself
220 3         8 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         10 $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         25 $_->ontology($ont);
239             }
240              
241 3         9 $self->_add_ontology($ont);
242              
243             # Adding new terms
244 3         6 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     1573 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         1339 my $new_ontology_flag = 1;
256 1021         1226 my $ontologies_array_ref = $self->{'_ontologies'};
257              
258 1021         1353 for my $ontology ( @$ontologies_array_ref ) {
259 1037         2216 my ($oname, $t_ns) = ( $ontology->name, $term->namespace );
260 1037 100 66     3094 next unless ( defined($oname) && defined($t_ns) );
261 187 100       326 if ( $oname eq $t_ns ) {
262             # No need to create new ontology
263 169         152 $new_ontology_flag = 0;
264 169         268 $ont = $ontology;
265             }
266             }
267              
268 1021 100 100     2037 if ( $new_ontology_flag && $term->namespace ) {
269 2         5 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         2313 $self->_add_term( $term, $ont );
279              
280             # Adding the IS_A relationship
281 1021         1099 for my $parent_term ( @{$self->{'_isa_parents'}} ) {
  1021         1730  
282             # Check if parent exists, if not then add the term to the graph.
283 990 100       1588 if ( ! $self->_has_term($parent_term) ) {
284 97         175 $self->_add_term( $parent_term, $ont ); # !
285             }
286              
287 990         2032 $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         1295 for my $relationship ( keys %{$self->{'_relationships'}} ) {
  1021         3515  
293 139         174 my $reltype;
294             # Check if relationship exists, if not add it
295 139 100       224 if ( $self->_ont_engine->get_relationship_type($relationship) ) {
296 130         196 $reltype = $self->_ont_engine->get_relationship_type($relationship);
297             }
298             else {
299 9         59 $self->_ont_engine->add_relationship_type( $relationship, $ont );
300 9         22 $reltype = $self->_ont_engine->get_relationship_type($relationship);
301             }
302              
303             # Check if the id already exists in the graph
304 139         199 for my $id ( @{$self->{'_relationships'}->{$relationship}} ) {
  139         251  
305 241         411 my $parent_term = $self->_create_term_object();
306 241         473 $parent_term->identifier($id);
307 241         462 $parent_term->ontology($ont);
308              
309 241 100       387 if ( ! $self->_has_term($parent_term) ) {
310 66         141 $self->_add_term( $parent_term, $ont );
311             }
312              
313 241         499 $self->_add_relationship( $parent_term, $term, $reltype, $ont );
314             }
315             }
316             }
317              
318 3         8 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 687 my $self = shift;
336              
337             # Parse if not done already
338 6 100       23 $self->parse() unless exists( $self->{'_ontologies'} );
339              
340             # Return next available ontology
341 6 50       13 if ( exists( $self->{'_ontologies'} ) ) {
342 6         12 my $ont = shift( @{ $self->{'_ontologies'} } );
  6         12  
343 6 100       13 if ($ont) {
344 5         37 my $store = Bio::Ontology::OntologyStore->new();
345 5         17 $store->register_ontology($ont);
346              
347 5         11 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 4 my $self = shift;
373              
374             # first call the inherited implementation
375 3         18 $self->SUPER::close();
376             }
377              
378             # INTERNAL METHODS
379              
380             sub _add_ontology {
381 5     5   7 my $self = shift;
382 5 100       20 $self->{'_ontologies'} = [] unless exists( $self->{'_ontologies'} );
383 5         8 for my $ont (@_) {
384 5 50 33     26 $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       7 $ont->name( $self->ontology_name ) unless $ont->name();
389 5         5 push( @{ $self->{'_ontologies'} }, $ont );
  5         13  
390             }
391             }
392              
393             # This simply delegates. See Ontology::OBOEngine::add_term.
394             sub _add_term {
395 1184     1184   1388 my ( $self, $term, $ont ) = @_;
396 1184 100 66     2451 $term->ontology($ont) if $ont && ( !$term->ontology );
397 1184         1879 $self->_ont_engine()->add_term($term);
398             }
399              
400             # This simply delegates. See OBOEngine
401             sub _part_of_relationship {
402 3     3   4 my $self = shift;
403              
404 3         10 return $self->_ont_engine()->part_of_relationship(@_);
405             }
406              
407             # This simply delegates. See OBOEngine
408             sub _is_a_relationship {
409 993     993   1014 my $self = shift;
410              
411 993         1252 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         6 return $self->_ont_engine()->related_to_relationship(@_);
419             }
420              
421             # This simply delegates. See OBOEngine
422             sub _regulates_relationship {
423 3     3   3 my $self = shift;
424              
425 3         4 return $self->_ont_engine()->regulates_relationship(@_);
426             }
427              
428             # This simply delegates. See OBOEngine
429             sub _positively_regulates_relationship {
430 3     3   6 my $self = shift;
431              
432 3         6 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   1761 my ( $self, $parent, $child, $type, $ont ) = @_;
445             # Note the triple terminology (subject,predicate,object) corresponds to
446             # (child,type,parent)
447 1231         1312 $self->_ont_engine()->add_relationship( $child, $type, $parent, $ont );
448             }
449              
450             # This simply delegates. See OBOEngine
451             sub _has_term {
452 2252     2252   2209 my $self = shift;
453              
454 2252         2759 return $self->_ont_engine()->has_term(@_);
455             }
456              
457             # Holds the OBO engine to be parsed into
458             sub _ont_engine {
459 6136     6136   6931 my ( $self, $value ) = @_;
460              
461 6136 100       7818 if ( defined $value ) {
462 3         6 $self->{"_ont_engine"} = $value;
463             }
464              
465 6136         11950 $self->{"_ont_engine"};
466             }
467              
468             # Removes the escape characters from the file
469             sub _filter_line {
470 6614     6614   8054 my ( $self, $line ) = @_;
471              
472 6614         8650 chomp($line);
473 6614         6418 $line =~ tr [\200-\377] [\000-\177];
474             # see 'man perlop', section on tr/
475             # weird ascii characters should be excluded
476 6614         7034 $line =~ tr/\0-\10//d; # remove weird characters; ascii 0-8
477             # preserve \11 (9 - tab) and \12 (10-linefeed)
478 6614         6856 $line =~ tr/\13\14//d; # remove weird characters; 11,12
479             # preserve \15 (13 - carriage return)
480 6614         7006 $line =~ tr/\16-\37//d; # remove 14-31 (all rest before space)
481 6614         6100 $line =~ tr/\177//d; # remove DEL character
482              
483 6614         6930 $line =~ s/^\!.*//;
484 6614         9572 $line =~ s/[^\\]\!.*//;
485 6614         7324 $line =~ s/[^\\]\#.*//;
486 6614         9630 $line =~ s/^\s+//;
487 6614         10308 $line =~ s/\s+$//;
488              
489 6614         9537 return $line;
490             }
491              
492             # Parses the header
493             sub _header {
494 3     3   3 my $self = shift;
495 3         20 my $annotation_collection = Bio::Annotation::Collection->new();
496 3         4 my ( $tag, $value );
497 3         5 my $line_counter = 0;
498 3         8 $self->{'_current_line_no'} = 0;
499 3         4 my $format_version_header_flag = 0;
500 3         4 my $default_namespace_header_flag = 0;
501              
502 3         12 while ( my $line = $self->_readline() ) {
503 24         25 ++$line_counter;
504 24         37 my $line = $self->_filter_line($line);
505              
506 24 100       37 if ( !$line ) {
507 3 50       6 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         8 return $annotation_collection;
514             }
515              
516             # Check if there is a header
517 21 50       35 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         38 $self->_check_colon( $line, $line_counter );
524              
525             # These are the allowed headers. Any other headers will be ignored
526 21 50       62 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       65 if ( $line =~ /^([\w\-]+)\:\s*(.*)/ ) {
545 21         55 ( $tag, $value ) = ( $1, $2 );
546             }
547              
548 21 100       47 if ( $tag =~ /format-version/) {
    100          
549 3         6 $format_version_header_flag = 1;
550             }elsif( $tag =~ /default-namespace/ ) {
551 3         10 $default_namespace_header_flag = 1;
552             }
553              
554 21         60 my $header = Bio::Annotation::SimpleValue->new( -value => $value );
555 21         48 $annotation_collection->add_Annotation( $tag, $header );
556              
557             # Assign the Ontology name as the value of the default-namespace header
558 21 100       61 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   1258 my $self = shift;
568 1024         946 my $term;
569 1024         969 my $skip_stanza_flag = 1;
570 1024         1312 my $line_counter = $self->{'_current_line_no'};
571              
572 1024         2077 while ( my $line = $self->_readline() ) {
573 6590         6390 ++$line_counter;
574 6590         8804 my $line = $self->_filter_line($line);
575              
576 6590 100 100     11897 if ( !$line && $term ) {
577 1021         1249 $self->{'_current_line_no'} = $line_counter;
578 1021         2388 return $term;
579             }
580              
581 5569 100       10647 if ( ( $line =~ /^\[(\w+)\]\s*(.*)/ ) ) { # New stanza
582 1039 100       2892 if ( uc($1) eq "TERM" ) {
    50          
583 1021         1390 $term = $self->_create_term_object;
584 1021         1090 $skip_stanza_flag = 0;
585              
586             # Reset the relationships after each stanza
587 1021         1537 $self->{'_relationships'} = {};
588 1021         2637 $self->{'_isa_parents'} = undef;
589             }
590             elsif ( uc($1) eq "TYPEDEF" ) {
591 18         19 $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         2812 next;
600             }
601              
602             # If the line is not null, check it contains at least one colon
603 4530         7797 $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     15291 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       12274 if ( $line =~ /^([\w\-]+)\:\s*(.*)/ ) {
631 4444         10772 my ( $tag, $val ) = ( $1, $2 );
632              
633             # If no value for the tag throw a warning
634 4444 50       5554 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         3462 my $qh;
641 4444         5891 ( $val, $qh ) = $self->_extract_quals($val);
642 4444         5180 my $val2 = $val;
643 4444         4990 $val2 =~ s/\\,/,/g;
644 4444         5059 $tag = uc($tag);
645 4444 100       14069 if ( $tag eq "ID" ) {
    100          
    50          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
646 1021         1998 $term->identifier($val);
647 1021 100       1668 if ( $self->_has_term($term) ) {
648 163         261 $term = $self->_ont_engine()->get_terms($val);
649             }
650             }
651             elsif ( $tag eq "NAME" ) {
652 1021         1949 $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         743 my ( $defstr, $parts ) = $self->_extract_qstr($val);
667 575         1523 $term->definition($defstr);
668 575         888 my $ann = $self->_to_annotation($parts);
669 575         1330 $term->add_dbxref( -dbxrefs => $ann );
670             }
671             elsif ( $tag eq "SYNONYM" ) {
672 293         531 $term->add_synonym($val);
673             }
674             elsif ( $tag eq "ALT_ID" ) {
675 1         10 $term->add_secondary_id($val);
676             }
677             elsif ( $tag =~ /XREF/i ) {
678 6         13 $term->add_secondary_id($val);
679             }
680             elsif ( $tag eq "IS_OBSOLETE" ) {
681 19 50       37 if ( $val eq 'true' ) {
    0          
682 19         22 $val = 1;
683             }
684             elsif ( $val eq 'false' ) {
685 0         0 $val = 0;
686             }
687 19         39 $term->is_obsolete($val);
688             }
689             elsif ( $tag eq "COMMENT" ) {
690 44         107 $term->comment($val);
691             }
692             elsif ( $tag eq "RELATIONSHIP" ) {
693 241         383 $self->_handle_relationship_tag($val);
694             }
695             elsif ( $tag eq "IS_A" ) {
696 990         1091 $val =~ s/ //g;
697 990         1361 my $parent_term = $self->_create_term_object();
698 990         1773 $parent_term->identifier($val);
699 990         929 push @{ $self->{'_isa_parents'} }, $parent_term;
  990         3771  
700             }
701             }
702             }
703              
704 3         9 $term;
705             }
706              
707              
708             # Creates a Bio::Ontology::OBOterm object
709             sub _create_term_object {
710 2252     2252   2581 my ($self) = @_;
711 2252         4303 my $term = $self->term_factory->create_object();
712 2252         2869 $term;
713             }
714              
715             sub _extract_quals {
716 4444     4444   4948 my ( $self, $str ) = @_;
717              
718 4444         4235 my %q = ();
719 4444 50       6683 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         8677 return ( $str, {} );
740             }
741             }
742              
743             sub _extract_qstr {
744 575     575   673 my ( $self, $str ) = @_;
745              
746 575         1393 my ( $extr, $rem, $prefix ) = extract_quotelike($str);
747 575         45637 my $txt = $extr;
748 575         1683 $txt =~ s/^\"//;
749 575         1768 $txt =~ s/\"$//;
750 575 50       976 if ($prefix) {
751 0         0 warn("illegal prefix: $prefix in: $str");
752             }
753              
754 575         685 my @extra = ();
755              
756             # e.g. synonym: "foo" EXACT [...]
757 575 50       975 if ( $rem =~ /(\w+)\s+(\[.*)/ ) {
758 0         0 $rem = $2;
759 0         0 push( @extra, split( ' ', $1 ) );
760             }
761              
762 575         563 my @parts = ();
763 575         1160 while ( ( $extr, $rem, $prefix ) = extract_bracketed( $rem, '[]' ) ) {
764 1054 100       102752 last unless $extr;
765 479         1290 $extr =~ s/^\[//;
766 479         1506 $extr =~ s/\]$//;
767 479 50       1366 push( @parts, $extr ) if $extr;
768             }
769             @parts =
770 575         772 map { $self->_split_on_comma($_) } @parts;
  479         810  
771              
772 575         811 $txt =~ s/\\//g;
773              
774 575         1647 ( $txt, \@parts, \@extra );
775             }
776              
777             sub _split_on_comma {
778 479     479   633 my ( $self, $str ) = @_;
779 479         435 my @parts = ();
780 479         938 while ( $str =~ /(.*[^\\],\s*)(.*)/ ) {
781 5         12 $str = $1;
782 5         10 my $part = $2;
783 5         8 unshift( @parts, $part );
784 5         21 $str =~ s/,\s*$//;
785             }
786 479         802 unshift( @parts, $str );
787              
788 479         576 return map { s/\\//g; $_ } @parts;
  484         551  
  484         1264  
789             }
790              
791             # This method checks for an existing colon in a line
792             sub _check_colon {
793 4551     4551   5944 my ( $self, $line, $line_no ) = @_;
794 4551 50 66     12648 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   319 my ( $self, $val ) = @_;
804 241         526 my @parts = split( / /, $val );
805 241         318 my $relationship = uc($parts[0]);
806 241 50       388 my $id = $parts[1] =~ /\^(w+)\s+\!/ ? $1 : $parts[1];
807 241         215 push @{$self->{_relationships}->{$relationship}}, $id;
  241         990  
808             }
809              
810             # Convert simple strings to Bio::Annotation::DBLinks
811             sub _to_annotation {
812 575     575   708 my ($self , $links) = @_;
813 575 50       885 return unless $links;
814 575         611 my @dbxrefs;
815 575         525 for my $string (@{$links}) {
  575         864  
816 484         1157 my ($db, $id) = split(':',$string,2);
817 484         1561 push @dbxrefs, Bio::Annotation::DBLink->new(-database => $db, -primary_id => $id);
818             }
819              
820 575         934 \@dbxrefs;
821             }
822              
823             1;